mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
支持腾讯云资源导入
This commit is contained in:
@@ -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.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/nelsonken/cos-go-sdk-v5"
|
||||
version = "=v1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/tredoe/osutil"
|
||||
|
||||
@@ -37,6 +37,7 @@ func init() {
|
||||
PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud"`
|
||||
AccessURL string `helo:"hello" metavar:"Azure choices: <AzureGermanCloud、AzureChinaCloud、AzureUSGovernmentCloud、AzurePublicCloud>"`
|
||||
Desc string `help:"Description"`
|
||||
Enabled bool `help:"Enabled the account automatically"`
|
||||
|
||||
Import bool `help:"Import all sub account automatically"`
|
||||
AutoSync bool `help:"Enabled the account automatically"`
|
||||
@@ -49,6 +50,10 @@ func init() {
|
||||
params.Add(jsonutils.NewString(args.SECRET), "secret")
|
||||
params.Add(jsonutils.NewString(args.PROVIDER), "provider")
|
||||
|
||||
if args.Enabled {
|
||||
params.Add(jsonutils.JSONTrue, "enabled")
|
||||
}
|
||||
|
||||
if args.Import {
|
||||
params.Add(jsonutils.JSONTrue, "import")
|
||||
if args.AutoSync {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
"yunion.io/x/structarg"
|
||||
|
||||
_ "yunion.io/x/onecloud/pkg/util/qcloud/shell"
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help"`
|
||||
AppID string `help:"AppID" default:"$QCLOUD_APPID"`
|
||||
SecretID string `help:"Secret" default:"$QCLOUD_SECRET_ID"`
|
||||
SecretKey string `help:"Access key" default:"$QCLOUD_SECRET_KEY"`
|
||||
RegionId string `help:"RegionId" default:"$QCLOUD_REGION_ID"`
|
||||
SUBCOMMAND string `help:"azurecli subcommand" subcommand:"true"`
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
"qcloudcli",
|
||||
"Command-line interface to tencentcloud API.",
|
||||
`See "tencentcli help COMMAND" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
subcmd := parse.GetSubcommand()
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
func showErrorAndExit(e error) {
|
||||
log.Errorf("%s", e)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func newClient(options *BaseOptions) (*qcloud.SRegion, error) {
|
||||
if len(options.SecretKey) == 0 {
|
||||
return nil, fmt.Errorf("Missing SecretKey")
|
||||
}
|
||||
|
||||
if len(options.SecretID) == 0 {
|
||||
return nil, fmt.Errorf("Missing SecretID")
|
||||
}
|
||||
|
||||
account := options.SecretID
|
||||
if len(options.AppID) > 0 {
|
||||
account = fmt.Sprintf("%s/%s", account, options.AppID)
|
||||
}
|
||||
|
||||
if cli, err := qcloud.NewQcloudClient("", "", account, options.SecretKey); err != nil {
|
||||
return nil, err
|
||||
} else if region := cli.GetRegion(options.RegionId); region == nil {
|
||||
return nil, fmt.Errorf("No such region %s", options.RegionId)
|
||||
} else {
|
||||
return region, nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
parser, e := getSubcommandParser()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *qcloud.SRegion
|
||||
if len(options.RegionId) == 0 {
|
||||
options.RegionId = qcloud.QCLOUD_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
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{"local_basic", "local_ssd", "cloud_basic", "cloud_ssd", "cloud_premium"} {
|
||||
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 ready ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_READY, 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 = diskInfo[i].AutoDelete
|
||||
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
|
||||
}
|
||||
@@ -31,6 +31,7 @@ const (
|
||||
|
||||
CLOUD_PROVIDER_VMWARE = "VMware"
|
||||
CLOUD_PROVIDER_ALIYUN = "Aliyun"
|
||||
CLOUD_PROVIDER_QCLOUD = "Qcloud"
|
||||
CLOUD_PROVIDER_AZURE = "Azure"
|
||||
)
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ const (
|
||||
HYPERVISOR_ESXI = "esxi"
|
||||
HYPERVISOR_HYPERV = "hyperv"
|
||||
HYPERVISOR_ALIYUN = "aliyun"
|
||||
HYPERVISOR_QCLOUD = "qcloud"
|
||||
HYPERVISOR_AZURE = "azure"
|
||||
|
||||
// HYPERVISOR_DEFAULT = HYPERVISOR_KVM
|
||||
@@ -132,7 +133,7 @@ const (
|
||||
var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_SNAPSHOT_STREAM}
|
||||
var VM_CREATING_STATUS = []string{VM_CREATE_NETWORK, VM_CREATE_DISK, VM_START_DEPLOY, VM_DEPLOYING}
|
||||
|
||||
var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_BAREMETAL, HYPERVISOR_ESXI, HYPERVISOR_CONTAINER, HYPERVISOR_ALIYUN, HYPERVISOR_AZURE}
|
||||
var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_BAREMETAL, HYPERVISOR_ESXI, HYPERVISOR_CONTAINER, HYPERVISOR_ALIYUN, HYPERVISOR_AZURE, HYPERVISOR_QCLOUD}
|
||||
|
||||
// var HYPERVISORS = []string{HYPERVISOR_ALIYUN}
|
||||
|
||||
@@ -143,6 +144,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{
|
||||
@@ -152,6 +154,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{
|
||||
HOST_TYPE_KUBELET: HYPERVISOR_CONTAINER,
|
||||
HOST_TYPE_ALIYUN: HYPERVISOR_ALIYUN,
|
||||
HOST_TYPE_AZURE: HYPERVISOR_AZURE,
|
||||
HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD,
|
||||
}
|
||||
|
||||
type SGuestManager struct {
|
||||
|
||||
@@ -43,6 +43,7 @@ const (
|
||||
HOST_TYPE_HYPERV = "hyperv" // # Microsoft Hyper-V
|
||||
HOST_TYPE_XEN = "xen" // # XenServer
|
||||
HOST_TYPE_ALIYUN = "aliyun"
|
||||
HOST_TYPE_QCLOUD = "qcloud"
|
||||
HOST_TYPE_AZURE = "azure"
|
||||
|
||||
HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR
|
||||
@@ -81,7 +82,7 @@ const (
|
||||
HOST_STATUS_UNKNOWN = BAREMETAL_UNKNOWN
|
||||
)
|
||||
|
||||
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE}
|
||||
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE, HOST_TYPE_QCLOUD}
|
||||
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
|
||||
|
||||
type SHostManager struct {
|
||||
|
||||
@@ -25,11 +25,12 @@ const (
|
||||
MANUAL = "manual"
|
||||
AUTO = "auto"
|
||||
|
||||
SNAPSHOT_CREATING = "creating"
|
||||
SNAPSHOT_FAILED = "create_failed"
|
||||
SNAPSHOT_READY = "ready"
|
||||
SNAPSHOT_DELETING = "deleting"
|
||||
SNAPSHOT_UNKNOWN = "unknown"
|
||||
SNAPSHOT_CREATING = "creating"
|
||||
SNAPSHOT_ROLLBACKING = "rollbacking"
|
||||
SNAPSHOT_FAILED = "create_failed"
|
||||
SNAPSHOT_READY = "ready"
|
||||
SNAPSHOT_DELETING = "deleting"
|
||||
SNAPSHOT_UNKNOWN = "unknown"
|
||||
)
|
||||
|
||||
type SSnapshotManager struct {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
_ "yunion.io/x/onecloud/pkg/util/aliyun/provider"
|
||||
_ "yunion.io/x/onecloud/pkg/util/azure/provider"
|
||||
_ "yunion.io/x/onecloud/pkg/util/esxi/provider"
|
||||
_ "yunion.io/x/onecloud/pkg/util/qcloud/provider"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
|
||||
@@ -2,6 +2,7 @@ package aliyun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"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)
|
||||
|
||||
_, err := self.cbsRequest("ResizeDisk", params)
|
||||
if err != nil {
|
||||
log.Errorf("ResizeDisk %s to %s GiB fail %s", diskId, sizeGb, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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, err := body.GetArray("DiskIdSet")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(diskIdSet) < 1 {
|
||||
return "", fmt.Errorf("Create Disk error")
|
||||
}
|
||||
return diskIdSet[0].String(), nil
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type TInternetChargeType string
|
||||
|
||||
const (
|
||||
InternetChargeByTraffic = TInternetChargeType("PayByTraffic")
|
||||
InternetChargeByBandwidth = TInternetChargeType("PayByBandwidth")
|
||||
)
|
||||
|
||||
const (
|
||||
EIP_STATUS_ASSOCIATING = "Associating"
|
||||
EIP_STATUS_UNASSOCIATING = "Unassociating"
|
||||
EIP_STATUS_INUSE = "InUse"
|
||||
EIP_STATUS_AVAILABLE = "Available"
|
||||
|
||||
EIP_OPERATION_LOCK_FINANCIAL = "financial"
|
||||
EIP_OPERATION_LOCK_SECURITY = "security"
|
||||
|
||||
EIP_INSTANCE_TYPE_ECS = "EcsInstance" // (默认值):VPC类型的ECS实例
|
||||
EIP_INTANNCE_TYPE_SLB = "SlbInstance" // :VPC类型的SLB实例
|
||||
EIP_INSTANCE_TYPE_NAT = "Nat" // :NAT网关
|
||||
EIP_INSTANCE_TYPE_HAVIP = "HaVip" // :HAVIP
|
||||
)
|
||||
|
||||
type SEipAddress struct {
|
||||
region *SRegion
|
||||
|
||||
AddressId string // EIP的ID,是EIP的唯一标识。
|
||||
AddressName string // EIP名称。
|
||||
AddressStatus string // EIP状态。
|
||||
AddressIp string // 外网IP地址
|
||||
InstanceId string // 绑定的资源实例ID。可能是一个CVM,NAT。
|
||||
CreatedTime time.Time // 创建时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
|
||||
NetworkInterfaceId string // 绑定的弹性网卡ID
|
||||
PrivateAddressIp string // 绑定的资源内网ip
|
||||
IsArrears bool // 资源隔离状态。true表示eip处于隔离状态,false表示资源处于未隔离装填
|
||||
IsBlocked bool // 资源封堵状态。true表示eip处于封堵状态,false表示eip处于未封堵状态
|
||||
IsEipDirectConnection bool // eip是否支持直通模式。true表示eip支持直通模式,false表示资源不支持直通模式
|
||||
AddressType string // eip资源类型,包括"CalcIP","WanIP","EIP","AnycastEIP"。其中"CalcIP"表示设备ip,“WanIP”表示普通公网ip,“EIP”表示弹性公网ip,“AnycastEip”表示加速EIP
|
||||
CascadeRelease bool // eip是否在解绑后自动释放。true表示eip将会在解绑后自动释放,false表示eip在解绑后不会自动释放
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetId() string {
|
||||
return self.AddressId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetName() string {
|
||||
return self.AddressName
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetGlobalId() string {
|
||||
return self.AddressId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetStatus() string {
|
||||
switch self.AddressStatus {
|
||||
case EIP_STATUS_AVAILABLE, EIP_STATUS_INUSE:
|
||||
return models.EIP_STATUS_READY
|
||||
case EIP_STATUS_ASSOCIATING:
|
||||
return models.EIP_STATUS_ASSOCIATE
|
||||
case EIP_STATUS_UNASSOCIATING:
|
||||
return models.EIP_STATUS_DISSOCIATE
|
||||
default:
|
||||
return models.EIP_STATUS_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Refresh() error {
|
||||
if self.IsEmulated() {
|
||||
return nil
|
||||
}
|
||||
new, err := self.region.GetEip(self.AddressId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) IsEmulated() bool {
|
||||
if self.AddressId == self.InstanceId {
|
||||
// fixed Public IP
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetIpAddr() string {
|
||||
return self.AddressIp
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetMode() string {
|
||||
if self.InstanceId == self.AddressId {
|
||||
return models.EIP_MODE_INSTANCE_PUBLICIP
|
||||
}
|
||||
return models.EIP_MODE_STANDALONE_EIP
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetAssociationType() string {
|
||||
switch self.AddressType {
|
||||
case "EIP", "AnycastEIP", "WanIP":
|
||||
return "server"
|
||||
case "CalcIP":
|
||||
return "server"
|
||||
default:
|
||||
log.Fatalf("unsupported type: %s", self.AddressType)
|
||||
return "unsupported"
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetAssociationExternalId() string {
|
||||
return self.InstanceId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Delete() error {
|
||||
return self.region.DeallocateEIP(self.AddressId)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetBandwidth() int {
|
||||
return 100
|
||||
//return self.Bandwidth
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetInternetChargeType() string {
|
||||
// switch self.InternetChargeType {
|
||||
// case string(InternetChargeByTraffic):
|
||||
// return models.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
// case string(InternetChargeByBandwidth):
|
||||
// return models.EIP_CHARGE_TYPE_BY_BANDWIDTH
|
||||
// default:
|
||||
// return models.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
// }
|
||||
return "unkonw"
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Associate(instanceId string) error {
|
||||
err := self.region.AssociateEip(self.AddressId, instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(self, models.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Dissociate() error {
|
||||
err := self.region.DissociateEip(self.AddressId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(self, models.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) ChangeBandwidth(bw int) error {
|
||||
return self.region.UpdateEipBandwidth(self.AddressId, bw)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetEips(eipId string, offset int, limit int) ([]SEipAddress, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
if len(eipId) > 0 {
|
||||
params["AddressIds.0"] = eipId
|
||||
}
|
||||
|
||||
body, err := region.vpcRequest("DescribeAddresses", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeEipAddresses fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
eips := make([]SEipAddress, 0)
|
||||
err = body.Unmarshal(&eips, "AddressSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal EipAddress details fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Int("TotalCount")
|
||||
for i := 0; i < len(eips); i++ {
|
||||
eips[i].region = region
|
||||
}
|
||||
return eips, int(total), nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
|
||||
eips, total, err := region.GetEips(eipId, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &eips[0], nil
|
||||
}
|
||||
|
||||
func (region *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType) (*SEipAddress, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = region.Region
|
||||
addRessSet := []string{}
|
||||
body, err := region.vpcRequest("AllocateAddresses", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := body.Unmarshal(&addRessSet, "AddressSet"); err == nil && len(addRessSet) > 0 {
|
||||
params["AddressId"] = addRessSet[0]
|
||||
params["AddressName"] = name
|
||||
if _, err := region.vpcRequest("ModifyAddressAttribute", params); err != nil {
|
||||
return nil, err
|
||||
} else if err := region.UpdateEipBandwidth(addRessSet[0], bwMbps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return region.GetEip(addRessSet[0])
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string) (cloudprovider.ICloudEIP, error) {
|
||||
var ctype TInternetChargeType
|
||||
switch chargeType {
|
||||
case models.EIP_CHARGE_TYPE_BY_TRAFFIC:
|
||||
ctype = InternetChargeByTraffic
|
||||
case models.EIP_CHARGE_TYPE_BY_BANDWIDTH:
|
||||
ctype = InternetChargeByBandwidth
|
||||
}
|
||||
return region.AllocateEIP(name, bwMbps, ctype)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeallocateEIP(eipId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = region.Region
|
||||
params["AddressIds.0"] = eipId
|
||||
|
||||
_, err := region.vpcRequest("ReleaseAddresses", params)
|
||||
if err != nil {
|
||||
log.Errorf("ReleaseAddresses fail %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
|
||||
params := make(map[string]string)
|
||||
params["AllocationId"] = eipId
|
||||
params["InstanceId"] = instanceId
|
||||
|
||||
_, err := region.vpcRequest("AssociateEipAddress", params)
|
||||
if err != nil {
|
||||
log.Errorf("AssociateEipAddress fail %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) DissociateEip(eipId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = region.Region
|
||||
params["AddressId"] = eipId
|
||||
|
||||
_, err := region.vpcRequest("DisassociateAddress", params)
|
||||
if err != nil {
|
||||
log.Errorf("UnassociateEipAddress fail %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) UpdateEipBandwidth(eipId string, bw int) error {
|
||||
// params := make(map[string]string)
|
||||
// params["Region"] = region.Region
|
||||
// params["AddressIds.0"] = eipId
|
||||
// params["InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bw)
|
||||
|
||||
// _, err := region.vpcRequest("ModifyAddressesBandwidth", params)
|
||||
// return err
|
||||
// 腾讯云这个接口目前有问题
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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.getStorageByCategory(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
|
||||
}
|
||||
disks[0].DiskType = storageType
|
||||
|
||||
for i, sz := range diskSizes {
|
||||
disks[i+1].DiskSize = sz
|
||||
disks[i+1].DiskType = 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)
|
||||
if err != nil {
|
||||
log.Errorf("Failed for %s: %s", instanceTypeId, err)
|
||||
} else {
|
||||
return vmId, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Failed to create, specification not supported")
|
||||
}
|
||||
|
||||
func (self *SHost) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessIp() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessMac() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetSN() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuCount() int8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetNodeCount() int8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuDesc() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuMhz() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetMemSizeMB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorageSizeMB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHost) GetStatus() string {
|
||||
return models.HOST_STATUS_RUNNING
|
||||
}
|
||||
|
||||
func (self *SHost) GetHostStatus() string {
|
||||
return models.HOST_ONLINE
|
||||
}
|
||||
|
||||
func (self *SHost) GetHostType() string {
|
||||
return models.HOST_TYPE_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorageType() string {
|
||||
return models.DISK_TYPE_HYBRID
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
return self.zone.GetIStorageById(id)
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
return self.zone.GetIStorages()
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMById(gid string) (cloudprovider.ICloudVM, error) {
|
||||
parts, _, err := self.zone.region.GetInstances(self.zone.Zone, []string{gid}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
parts[0].host = self
|
||||
return &parts[0], nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms := make([]SInstance, 0)
|
||||
for {
|
||||
parts, total, err := self.zone.region.GetInstances(self.zone.Zone, nil, len(vms), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vms = append(vms, parts...)
|
||||
if len(vms) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
ivms := make([]cloudprovider.ICloudVM, len(vms))
|
||||
for i := 0; i < len(vms); i++ {
|
||||
vms[i].host = self
|
||||
ivms[i] = &vms[i]
|
||||
}
|
||||
return ivms, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
return self.zone.GetIWires()
|
||||
}
|
||||
|
||||
func (self *SHost) GetManagerId() string {
|
||||
return self.zone.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SHost) GetSysInfo() jsonutils.JSONObject {
|
||||
info := jsonutils.NewDict()
|
||||
info.Add(jsonutils.NewString(CLOUD_PROVIDER_QCLOUD), "manufacture")
|
||||
return info
|
||||
}
|
||||
|
||||
func (self *SHost) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
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)
|
||||
if body, err := self.cvmRequest("DescribeImages", params); err != nil {
|
||||
return nil, 0, err
|
||||
} else if err := body.Unmarshal(&images, "ImageSet"); err != nil {
|
||||
return nil, 0, err
|
||||
} else {
|
||||
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|-"
|
||||
params["OsType"] = osType // "CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"
|
||||
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
|
||||
} else {
|
||||
for i := 0; i < 3; i++ {
|
||||
image, err := self.GetImageByName(name)
|
||||
if err == nil {
|
||||
return image, nil
|
||||
}
|
||||
time.Sleep(time.Minute * time.Duration(i))
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// PENDING:表示创建中
|
||||
// LAUNCH_FAILED:表示创建失败
|
||||
// RUNNING:表示运行中
|
||||
// STOPPED:表示关机
|
||||
// STARTING:表示开机中
|
||||
// STOPPING:表示关机中
|
||||
// REBOOTING:表示重启中
|
||||
// SHUTDOWN:表示停止待销毁
|
||||
// TERMINATING:表示销毁中。
|
||||
|
||||
InstanceStatusStopped = "STOPPED"
|
||||
InstanceStatusRunning = "RUNNING"
|
||||
InstanceStatusStopping = "STOPPING"
|
||||
InstanceStatusStarting = "STARTING"
|
||||
)
|
||||
|
||||
type SystemDisk struct {
|
||||
DiskType string //系统盘类型。系统盘类型限制详见CVM实例配置。取值范围:LOCAL_BASIC:本地硬盘 LOCAL_SSD:本地SSD硬盘 CLOUD_BASIC:普通云硬盘 CLOUD_SSD:SSD云硬盘 CLOUD_PREMIUM:高性能云硬盘 默认取值:CLOUD_BASIC。
|
||||
DiskId string // 系统盘ID。LOCAL_BASIC 和 LOCAL_SSD 类型没有ID。暂时不支持该参数。
|
||||
DiskSize float32 //系统盘大小,单位:GB。默认值为 50
|
||||
}
|
||||
|
||||
type DataDisk struct {
|
||||
DiskSize float32 // 数据盘大小,单位:GB。最小调整步长为10G,不同数据盘类型取值范围不同,具体限制详见:CVM实例配置。默认值为0,表示不购买数据盘。更多限制详见产品文档。
|
||||
DiskType string // 数据盘类型。数据盘类型限制详见CVM实例配置。取值范围:LOCAL_BASIC:本地硬盘 LOCAL_SSD:本地SSD硬盘 CLOUD_BASIC:普通云硬盘 CLOUD_PREMIUM:高性能云硬盘 CLOUD_SSD:SSD云硬盘 默认取值:LOCAL_BASIC。 该参数对ResizeInstanceDisk接口无效。
|
||||
DiskId string // 数据盘ID。LOCAL_BASIC 和 LOCAL_SSD 类型没有ID。暂时不支持该参数。
|
||||
DeleteWithInstance bool // 数据盘是否随子机销毁。取值范围:TRUE:子机销毁时,销毁数据盘 FALSE:子机销毁时,保留数据盘 默认取值:TRUE 该参数目前仅用于 RunInstances 接口。
|
||||
}
|
||||
|
||||
type InternetAccessible struct {
|
||||
InternetChargeType string //网络计费类型。取值范围:BANDWIDTH_PREPAID:预付费按带宽结算 TRAFFIC_POSTPAID_BY_HOUR:流量按小时后付费 BANDWIDTH_POSTPAID_BY_HOUR:带宽按小时后付费 BANDWIDTH_PACKAGE:带宽包用户 默认取值:非带宽包用户默认与子机付费类型保持一致。
|
||||
InternetMaxBandwidthOut int // 公网出带宽上限,单位:Mbps。默认值:0Mbps。不同机型带宽上限范围不一致,具体限制详见购买网络带宽。
|
||||
PublicIpAssigned bool // 是否分配公网IP。取值范围: TRUE:表示分配公网IP FALSE:表示不分配公网IP 当公网带宽大于0Mbps时,可自由选择开通与否,默认开通公网IP;当公网带宽为0,则不允许分配公网IP。
|
||||
}
|
||||
|
||||
type VirtualPrivateCloud struct {
|
||||
VpcId string // 私有网络ID,形如vpc-xxx。有效的VpcId可通过登录控制台查询;也可以调用接口 DescribeVpcEx ,从接口返回中的unVpcId字段获取。
|
||||
SubnetId string // 私有网络子网ID,形如subnet-xxx。有效的私有网络子网ID可通过登录控制台查询;也可以调用接口 DescribeSubnets ,从接口返回中的unSubnetId字段获取。
|
||||
AsVpcGateway bool // 是否用作公网网关。公网网关只有在实例拥有公网IP以及处于私有网络下时才能正常使用。取值范围:TRUE:表示用作公网网关 FALSE:表示不用作公网网关 默认取值:FALSE。
|
||||
PrivateIpAddresses []string // 私有网络子网 IP 数组,在创建实例、修改实例vpc属性操作中可使用此参数。当前仅批量创建多台实例时支持传入相同子网的多个 IP。
|
||||
}
|
||||
|
||||
type LoginSettings struct {
|
||||
Password string //实例登录密码。不同操作系统类型密码复杂度限制不一样,具体如下:Linux实例密码必须8到16位,至少包括两项[a-z,A-Z]、[0-9] 和 [( ) ~ ! @ # $ % ^ & * - + = | { } [ ] : ; ' , . ? / ]中的特殊符号。<br><li>Windows实例密码必须12到16位,至少包括三项[a-z],[A-Z],[0-9] 和 [( ) ~ ! @ # $ % ^ & * - + = { } [ ] : ; ' , . ? /]中的特殊符号。 若不指定该参数,则由系统随机生成密码,并通过站内信方式通知到用户。
|
||||
KeyIds []string // 密钥ID列表。关联密钥后,就可以通过对应的私钥来访问实例;KeyId可通过接口DescribeKeyPairs获取,密钥与密码不能同时指定,同时Windows操作系统不支持指定密钥。当前仅支持购买的时候指定一个密钥。
|
||||
KeepImageLogin string // 保持镜像的原始设置。该参数与Password或KeyIds.N不能同时指定。只有使用自定义镜像、共享镜像或外部导入镜像创建实例时才能指定该参数为TRUE。取值范围: TRUE:表示保持镜像的登录设置 FALSE:表示不保持镜像的登录设置 默认取值:FALSE。
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
host *SHost
|
||||
|
||||
image *SImage
|
||||
idisks []cloudprovider.ICloudDisk
|
||||
|
||||
Placement Placement
|
||||
InstanceId string
|
||||
InstanceType string
|
||||
CPU int8
|
||||
Memory int
|
||||
RestrictState string //NORMAL EXPIRED PROTECTIVELY_ISOLATED
|
||||
InstanceName string
|
||||
InstanceChargeType InstanceChargeType //PREPAID:表示预付费,即包年包月 POSTPAID_BY_HOUR:表示后付费,即按量计费 CDHPAID:CDH付费,即只对CDH计费,不对CDH上的实例计费。
|
||||
SystemDisk SystemDisk //实例系统盘信息。
|
||||
DataDisks []DataDisk //实例数据盘信息。只包含随实例购买的数据盘。
|
||||
PrivateIpAddresses []string //实例主网卡的内网IP列表。
|
||||
PublicIpAddresses []string //实例主网卡的公网IP列表。
|
||||
InternetAccessible InternetAccessible //实例带宽信息。
|
||||
VirtualPrivateCloud VirtualPrivateCloud //实例所属虚拟私有网络信息。
|
||||
ImageId string // 生产实例所使用的镜像ID。
|
||||
RenewFlag string // 自动续费标识。取值范围:NOTIFY_AND_MANUAL_RENEW:表示通知即将过期,但不自动续费 NOTIFY_AND_AUTO_RENEW:表示通知即将过期,而且自动续费 DISABLE_NOTIFY_AND_MANUAL_RENEW:表示不通知即将过期,也不自动续费。
|
||||
CreatedTime time.Time // 创建时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
|
||||
ExpiredTime time.Time // 到期时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
|
||||
OsName string // 操作系统名称。
|
||||
SecurityGroupIds []string // 实例所属安全组。该参数可以通过调用 DescribeSecurityGroups 的返回值中的sgId字段来获取。
|
||||
LoginSettings LoginSettings //实例登录设置。目前只返回实例所关联的密钥。
|
||||
InstanceState string // 实例状态。取值范围:PENDING:表示创建中 LAUNCH_FAILED:表示创建失败 RUNNING:表示运行中 STOPPED:表示关机 STARTING:表示开机中 STOPPING:表示关机中 REBOOTING:表示重启中 SHUTDOWN:表示停止待销毁 TERMINATING:表示销毁中。
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstances(zoneId string, ids []string, offset int, limit int) ([]SInstance, int, error) {
|
||||
params := make(map[string]string)
|
||||
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
|
||||
// }
|
||||
// }
|
||||
return self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId, passwd, keypair, sysSizeGB)
|
||||
}
|
||||
|
||||
func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error {
|
||||
return nil
|
||||
//return self.host.zone.region.ChangeVMConfig(self.ZoneId, 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) (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["HostName"] = name
|
||||
if len(passwd) > 0 {
|
||||
params["LoginSettings.Password"] = passwd
|
||||
} else {
|
||||
params["PasswordInherit"] = "True"
|
||||
}
|
||||
//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 != InstanceStatusRunning {
|
||||
log.Errorf("StopVM: vm status is %s expect %s", status, InstanceStatusRunning)
|
||||
return cloudprovider.ErrInvalidStatus
|
||||
}
|
||||
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 {
|
||||
// err = self.DetachKeyPair(instanceId, instance.KeyPairName)
|
||||
// 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 resetPassword {
|
||||
// params["Password"] = seclib2.RandomPassword2(12)
|
||||
// }
|
||||
// 指定密码的情况下,使用指定的密码
|
||||
if len(password) > 0 {
|
||||
params["Password"] = password
|
||||
}
|
||||
|
||||
if len(name) > 0 && instance.InstanceName != name {
|
||||
params["InstanceName"] = name
|
||||
params["HostName"] = name
|
||||
}
|
||||
|
||||
// if len(description) > 0 && instance.Description != description {
|
||||
// params["Description"] = description
|
||||
// }
|
||||
|
||||
if len(params) > 0 {
|
||||
return self.modifyInstanceAttribute(instanceId, params)
|
||||
} else {
|
||||
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, "ModifyInstanceAttribute", params)
|
||||
}
|
||||
|
||||
func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, keypairName string, sysDiskSizeGB int) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["RegionId"] = self.Region
|
||||
params["InstanceId"] = instanceId
|
||||
params["ImageId"] = imageId
|
||||
if len(passwd) > 0 {
|
||||
params["Password"] = passwd
|
||||
} else {
|
||||
params["PasswordInherit"] = "True"
|
||||
}
|
||||
if len(keypairName) > 0 {
|
||||
params["KeyPairName"] = keypairName
|
||||
}
|
||||
if sysDiskSizeGB > 0 {
|
||||
params["SystemDisk.Size"] = fmt.Sprintf("%d", sysDiskSizeGB)
|
||||
}
|
||||
body, err := self.cvmRequest("ReplaceSystemDisk", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// log.Debugf("%s", body.String())
|
||||
return body.GetString("DiskId")
|
||||
}
|
||||
|
||||
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.InstanceTypeId
|
||||
// params["ClientToken"] = utils.GenRequestId(20)
|
||||
// if err := self.instanceOperation(instanceId, "ModifyInstanceSpec", params); err != nil {
|
||||
// log.Errorf("Failed for %s: %s", instancetype.InstanceTypeId, err)
|
||||
// } else {
|
||||
// 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["DiskId"] = diskId
|
||||
_, err := self.cvmRequest("AttachDisk", params)
|
||||
if err != nil {
|
||||
log.Errorf("AttachDisk %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.ErrNotImplemented
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SInstanceNic struct {
|
||||
instance *SInstance
|
||||
ipAddr string
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetIP() string {
|
||||
return self.ipAddr
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetMAC() string {
|
||||
ip, _ := netutils.NewIPV4Addr(self.ipAddr)
|
||||
return ip.ToMac("00:16:")
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetDriver() string {
|
||||
return "virtio"
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork {
|
||||
networkId := self.instance.VirtualPrivateCloud.VpcId
|
||||
wires, err := self.instance.host.GetIWires()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(wires); i++ {
|
||||
wire := wires[i].(*SWire)
|
||||
net := wire.getNetworkById(networkId)
|
||||
if net != nil {
|
||||
return net
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package qcloud
|
||||
|
||||
import "yunion.io/x/log"
|
||||
|
||||
// "time"
|
||||
|
||||
// {"CpuCoreCount":1,"EniQuantity":1,"GPUAmount":0,"GPUSpec":"","InstanceTypeFamily":"ecs.t1","InstanceTypeId":"ecs.t1.xsmall","LocalStorageCategory":"","MemorySize":0.500000}
|
||||
// InstanceBandwidthRx":26214400,"InstanceBandwidthTx":26214400,"InstancePpsRx":4500000,"InstancePpsTx":4500000
|
||||
|
||||
type SInstanceType struct {
|
||||
Zone string // 可用区。
|
||||
InstanceType string // 实例机型。
|
||||
InstanceFamily string // 实例机型系列。
|
||||
GPU int // GPU核数,单位:核。
|
||||
CPU int // CPU核数,单位:核。
|
||||
Memory int // 内存容量,单位:GB。
|
||||
CbsSupport string // 是否支持云硬盘。取值范围:TRUE:表示支持云硬盘;FALSE:表示不支持云硬盘。
|
||||
InstanceTypeState string // 机型状态。取值范围:AVAILABLE:表示机型可用;UNAVAILABLE:表示机型不可用。
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceTypes() ([]SInstanceType, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
|
||||
body, err := self.cvmRequest("DescribeInstanceTypeConfigs", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeInstanceTypeConfigs fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceTypes := make([]SInstanceType, 0)
|
||||
err = body.Unmarshal(&instanceTypes, "InstanceTypeConfigSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal instance type details fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return instanceTypes, nil
|
||||
}
|
||||
|
||||
func (self *SInstanceType) memoryMB() int {
|
||||
return int(self.Memory * 1024)
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aokoli/goutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SKeypair struct {
|
||||
AssociatedInstanceIds []string
|
||||
CreateTime time.Time
|
||||
Description string
|
||||
KeyId string
|
||||
KeyName string
|
||||
PublicKey string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetKeypairs(name string, keyIds []string, offset int, limit int) ([]SKeypair, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := map[string]string{}
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
if len(keyIds) > 0 {
|
||||
for i := 0; i < len(keyIds); i++ {
|
||||
params[fmt.Sprintf("KeyIds.%d", i)] = keyIds[i]
|
||||
}
|
||||
} else {
|
||||
if len(name) > 0 {
|
||||
params["Filters.0.Name"] = "key-name"
|
||||
params["Filters.0.Values.0"] = name
|
||||
}
|
||||
}
|
||||
|
||||
body, err := self.cvmRequest("DescribeKeyPairs", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetKeypairs fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
keypairs := []SKeypair{}
|
||||
err = body.Unmarshal(&keypairs, "KeyPairSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal keypair fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Int("TotalCount")
|
||||
return keypairs, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) ImportKeypair(name string, pubKey string) (*SKeypair, error) {
|
||||
params := map[string]string{}
|
||||
params["PublicKey"] = pubKey
|
||||
params["ProjectId"] = "0"
|
||||
params["KeyName"] = name
|
||||
|
||||
body, err := self.cvmRequest("ImportKeyPair", params)
|
||||
if err != nil {
|
||||
log.Errorf("ImportKeypair fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keypairID, err := body.GetString("KeyId")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keypairs, total, err := self.GetKeypairs("", []string{keypairID}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &keypairs[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) AttachKeypair(instanceId string, keypairId string) error {
|
||||
params := map[string]string{}
|
||||
params["InstanceIds.0"] = instanceId
|
||||
params["KeyIds.0"] = keypairId
|
||||
_, err := self.cvmRequest("AssociateInstancesKeyPairs", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachKeyPair(instanceId string, keypairId string) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceIds.0"] = instanceId
|
||||
params["KeyIds.0"] = keypairId
|
||||
_, err := self.cvmRequest("DisassociateInstancesKeyPairs", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateKeyPair(name string) (*SKeypair, error) {
|
||||
params := make(map[string]string)
|
||||
params["KeyName"] = name
|
||||
params["ProjectId"] = "0"
|
||||
body, err := self.cvmRequest("CreateKeyPair", params)
|
||||
keypair := SKeypair{}
|
||||
err = body.Unmarshal(&keypair, "KeyPair")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &keypair, err
|
||||
}
|
||||
|
||||
func (self *SRegion) lookUpAliyunKeypair(publicKey string) (string, error) {
|
||||
keypairs, _, err := self.GetKeypairs("", []string{}, 0, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i := 0; i < len(keypairs); i++ {
|
||||
if keypairs[i].PublicKey == publicKey {
|
||||
return keypairs[i].KeyId, nil
|
||||
}
|
||||
}
|
||||
return "", cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) syncKeypair(publicKey string) (string, error) {
|
||||
keypairId, e := self.lookUpAliyunKeypair(publicKey)
|
||||
if e == nil {
|
||||
return keypairId, nil
|
||||
}
|
||||
|
||||
prefix, e := goutils.RandomAlphabetic(6)
|
||||
if e != nil {
|
||||
return "", fmt.Errorf("publicKey error %s", e)
|
||||
}
|
||||
|
||||
name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
|
||||
keypair, err := self.ImportKeypair(name, publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return keypair.KeyId, nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package qcloud
|
||||
|
||||
var LatitudeAndLongitude = map[string]map[string]float32{
|
||||
"ap-bangkok": {"latitude": 13.756330, "longitude": 100.501762}, // 腾讯云 亚太地区(曼谷)
|
||||
"ap-beijing": {"latitude": 39.904202, "longitude": 116.407394}, // 腾讯云 华北地区(北京)
|
||||
"ap-chengdu": {"latitude": 30.572815, "longitude": 104.066803}, // 腾讯云 西南地区(成都)
|
||||
"ap-chongqing": {"latitude": 29.431585, "longitude": 106.912254}, // 腾讯云 西南地区(重庆)
|
||||
"ap-guangzhou": {"latitude": 23.129110, "longitude": 113.264381}, // 腾讯云 华南地区(广州)
|
||||
"ap-guangzhou-open": {"latitude": 23.126593, "longitude": 113.273415}, // 腾讯云 华南地区(广州Open)
|
||||
"ap-hongkong": {"latitude": 22.396427, "longitude": 114.109497}, // 腾讯云 东南亚地区(香港)
|
||||
"ap-mumbai": {"latitude": 19.075983, "longitude": 72.877655}, // 腾讯云 亚太地区(孟买)
|
||||
"ap-seoul": {"latitude": 37.566536, "longitude": 126.977966}, // 腾讯云 东南亚地区(首尔)
|
||||
"ap-shanghai": {"latitude": 31.230391, "longitude": 121.473701}, // 腾讯云 华东地区(上海)
|
||||
"ap-shanghai-fsi": {"latitude": 31.311033, "longitude": 121.536217}, // 腾讯云 华东地区(上海金融)
|
||||
"ap-shenzhen-fsi": {"latitude": 22.531544, "longitude": 114.025467}, // 腾讯云 华南地区(深圳金融)
|
||||
"ap-singapore": {"latitude": 1.352083, "longitude": 103.819839}, // 腾讯云 东南亚地区(新加坡)
|
||||
"ap-tokyo": {"latitude": 35.709026, "longitude": 139.731995}, // 腾讯云 亚太地区(东京)
|
||||
"eu-frankfurt": {"latitude": 51.165691, "longitude": 10.451526}, // 腾讯云 欧洲地区(德国)
|
||||
"eu-moscow": {"latitude": 55.755825, "longitude": 37.617298}, // 腾讯云 欧洲地区(莫斯科)
|
||||
"na-ashburn": {"latitude": 37.431572, "longitude": -78.656891}, // 腾讯云 美国东部(弗吉尼亚)
|
||||
"na-siliconvalley": {"latitude": 37.387474, "longitude": -122.057541}, // 腾讯云 美国西部(硅谷)
|
||||
"na-toronto": {"latitude": 43.653225, "longitude": -79.383186}, // 腾讯云 北美地区(多伦多)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SLocalDisk struct {
|
||||
storage *SLocalStorage
|
||||
DiskId string
|
||||
DiskSize float32
|
||||
DisktType string
|
||||
DiskUsage string
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Delete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetBillingType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetFsFormat() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetIsNonPersistent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDriver() string {
|
||||
return "scsi"
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetCacheMode() string {
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetMountpoint() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDiskFormat() string {
|
||||
return "vhd"
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDiskSizeMB() int {
|
||||
return int(self.DiskSize) * 1024
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetIsAutoDelete() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetExpiredAt() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDiskType() string {
|
||||
switch self.DiskUsage {
|
||||
case "SYSTEM_DISK":
|
||||
return models.DISK_TYPE_SYS
|
||||
case "DATA_DISK":
|
||||
return models.DISK_TYPE_DATA
|
||||
default:
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Reset(snapshotId string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetTemplateId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetStatus() string {
|
||||
return models.DISK_READY
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetName() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetGlobalId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetIStorge() cloudprovider.ICloudStorage {
|
||||
return self.storage
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Resize(size int64) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SLocalStorage struct {
|
||||
zone *SZone
|
||||
storageType string
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetId() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetName() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerName, self.zone.GetId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetGlobalId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIZone() cloudprovider.ICloudZone {
|
||||
return self.zone
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks := []SLocalDisk{}
|
||||
idisks := make([]cloudprovider.ICloudDisk, len(disks))
|
||||
for i := 0; i < len(disks); i++ {
|
||||
disks[i].storage = self
|
||||
idisks[i] = &disks[i]
|
||||
}
|
||||
return idisks, nil
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetStorageType() string {
|
||||
return strings.ToLower(self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetMediumType() string {
|
||||
if strings.HasSuffix(self.storageType, "_BASIC") {
|
||||
return models.DISK_TYPE_ROTATE
|
||||
}
|
||||
return models.DISK_TYPE_SSD
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetCapacityMB() int {
|
||||
return 0 // unlimited
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetStorageConf() jsonutils.JSONObject {
|
||||
conf := jsonutils.NewDict()
|
||||
return conf
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetManagerId() string {
|
||||
return self.zone.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetStatus() string {
|
||||
return models.STORAGE_ONLINE
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) Refresh() error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return self.zone.region.getStoragecache()
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) {
|
||||
return &SLocalDisk{storage: self, DiskId: idStr}, nil
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SNetwork struct {
|
||||
wire *SWire
|
||||
|
||||
CidrBlock string
|
||||
Zone string
|
||||
SubnetId string
|
||||
VpcId string
|
||||
SubnetName string
|
||||
AvailableIpAddressCount int
|
||||
CreatedTime time.Time
|
||||
EnableBroadcast bool
|
||||
IsDefault bool
|
||||
RouteTableId string
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetId() string {
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetName() string {
|
||||
if len(self.SubnetName) > 0 {
|
||||
return self.SubnetName
|
||||
}
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGlobalId() string {
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SNetwork) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetStatus() string {
|
||||
return models.NETWORK_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SNetwork) Delete() error {
|
||||
return self.wire.zone.region.DeleteNetwork(self.SubnetId)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNetwork(networkId string) error {
|
||||
params := make(map[string]string)
|
||||
params["SubnetId"] = networkId
|
||||
|
||||
_, err := self.vpcRequest("DeleteSubnet", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
|
||||
return self.wire
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetAllocTimeoutSeconds() int {
|
||||
return 120 // 2 minutes
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGateway() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
|
||||
endIp = endIp.StepDown() // 254
|
||||
return endIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpStart() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
|
||||
startIp = startIp.StepUp() // 1
|
||||
return startIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpEnd() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
|
||||
endIp = endIp.StepDown() // 254
|
||||
endIp = endIp.StepDown() // 253
|
||||
endIp = endIp.StepDown() // 252
|
||||
return endIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpMask() int8 {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
return pref.MaskLen
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIsPublic() bool {
|
||||
// return self.IsDefault
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetServerType() string {
|
||||
return models.SERVER_TYPE_GUEST
|
||||
}
|
||||
|
||||
func (self *SNetwork) Refresh() error {
|
||||
log.Debugf("network refresh %s", self.SubnetId)
|
||||
new, err := self.wire.zone.region.GetNetwork(self.SubnetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateNetwork(zoneId string, vpcId string, name string, cidr string, desc string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["Zone"] = zoneId
|
||||
params["VpcId"] = vpcId
|
||||
params["CidrBlock"] = cidr
|
||||
params["SubnetName"] = name
|
||||
body, err := self.vpcRequest("CreateSubnet", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return body.GetString("Subnet", "SubnetId")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package provider
|
||||
@@ -0,0 +1,84 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
)
|
||||
|
||||
type STencentProviderFactory struct {
|
||||
// providerTable map[string]*SAliyunProvider
|
||||
}
|
||||
|
||||
func (self *STencentProviderFactory) GetId() string {
|
||||
return qcloud.CLOUD_PROVIDER_QCLOUD
|
||||
}
|
||||
|
||||
func (self *STencentProviderFactory) 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 &STencentProvider{client: client}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
factory := STencentProviderFactory{}
|
||||
cloudprovider.RegisterFactory(&factory)
|
||||
}
|
||||
|
||||
type STencentProvider struct {
|
||||
client *qcloud.SQcloudClient
|
||||
}
|
||||
|
||||
func (self *STencentProvider) IsPublicCloud() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetId() string {
|
||||
return qcloud.CLOUD_PROVIDER_QCLOUD
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetName() string {
|
||||
return qcloud.CLOUD_PROVIDER_QCLOUD_CN
|
||||
}
|
||||
|
||||
func (self *STencentProvider) 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 *STencentProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
return self.client.GetSubAccounts()
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
return self.client.GetIRegions()
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
|
||||
return self.client.GetIRegionById(id)
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
return self.client.GetIHostById(id)
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
return self.client.GetIVpcById(id)
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
return self.client.GetIStorageById(id)
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
return self.client.GetIStoragecacheById(id)
|
||||
}
|
||||
|
||||
func (self *STencentProvider) GetBalance() (float64, error) {
|
||||
return 0.0, nil
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"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 error %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//log.Debugf(jsonutils.Marshal(resp.Response).PrettyString())
|
||||
|
||||
return jsonutils.Marshal(resp.Response), nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetRegions() []SRegion {
|
||||
regions := make([]SRegion, len(client.iregions))
|
||||
for i := 0; i < len(regions); i++ {
|
||||
region := client.iregions[i].(*SRegion)
|
||||
regions[i] = *region
|
||||
}
|
||||
return regions
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) getDefaultClient() (*common.Client, error) {
|
||||
return common.NewClientWithSecretId(client.SecretID, client.SecretKey, QCLOUD_DEFAULT_REGION)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vpcRequest(cli, apiName, params)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cbsRequest(cli, apiName, params)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) jsonRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jsonRequest(cli, apiName, params)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) fetchRegions() error {
|
||||
body, err := client.jsonRequest("DescribeRegions", nil)
|
||||
if err != nil {
|
||||
log.Errorf("fetchRegions fail %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
regions := make([]SRegion, 0)
|
||||
err = body.Unmarshal(®ions, "RegionSet")
|
||||
if err != nil {
|
||||
log.Errorf("unmarshal json error %s", err)
|
||||
return err
|
||||
}
|
||||
client.iregions = make([]cloudprovider.ICloudRegion, len(regions))
|
||||
for i := 0; i < len(regions); i++ {
|
||||
regions[i].client = client
|
||||
client.iregions[i] = ®ions[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
err := client.fetchRegions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subAccount := cloudprovider.SSubAccount{}
|
||||
subAccount.Name = client.providerName
|
||||
subAccount.Account = client.SecretKey
|
||||
return []cloudprovider.SSubAccount{subAccount}, nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
return client.iregions
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) getDefaultRegion() (cloudprovider.ICloudRegion, error) {
|
||||
if len(client.iregions) > 0 {
|
||||
return client.iregions[0], nil
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
if client.iregions[i].GetGlobalId() == id {
|
||||
return client.iregions[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetRegion(regionId string) *SRegion {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
if client.iregions[i].GetId() == regionId {
|
||||
return client.iregions[i].(*SRegion)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIHostById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIVpcById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIStorageById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIStoragecacheById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
type SAccountBalance struct {
|
||||
AvailableAmount float64
|
||||
AvailableCashAmount float64
|
||||
CreditAmount float64
|
||||
MybankCreditAmount float64
|
||||
Currency string
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) QueryAccountBalance() (*SAccountBalance, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
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) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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)
|
||||
params["Region"] = self.Region
|
||||
zones := make([]SZone, 0)
|
||||
body, err := self.client.jsonRequest("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.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["Region"] = self.Region
|
||||
params["InstanceIds.0"] = instanceId
|
||||
if extra != nil && len(extra) > 0 {
|
||||
for k, v := range extra {
|
||||
params[k] = v
|
||||
}
|
||||
}
|
||||
_, err := self.cvmRequest(opname, params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["InstanceId"] = instanceId
|
||||
body, err := self.cvmRequest("DescribeInstanceVncUrl", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return body.GetString("InstanceVncUrl")
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) {
|
||||
instance, err := self.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.InstanceState, nil
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SecurityGroupPolicy struct {
|
||||
vpc *SVpc
|
||||
PolicyIndex int // 安全组规则索引号。
|
||||
Protocol string // 协议, 取值: TCP,UDP, ICMP。
|
||||
Port string // 端口(all, 离散port, range)。
|
||||
ServiceTemplate ServiceTemplateSpecification // 协议端口ID或者协议端口组ID。ServiceTemplate和Protocol+Port互斥。
|
||||
CidrBlock string // 网段或IP(互斥)。
|
||||
SecurityGroupId string // 已绑定安全组的网段或IP。
|
||||
AddressTemplate AddressTemplateSpecification // IP地址ID或者ID地址组ID。
|
||||
Action string // ACCEPT 或 DROP。
|
||||
PolicyDescription string // 安全组规则描述。
|
||||
direction string
|
||||
}
|
||||
|
||||
type ServiceTemplateSpecification struct {
|
||||
ServiceId string // 协议端口ID,例如:ppm-f5n1f8da。
|
||||
ServiceGroupId string // 协议端口组ID,例如:ppmg-f5n1f8da。
|
||||
}
|
||||
|
||||
type AddressTemplateSpecification struct {
|
||||
AddressId string // IP地址ID,例如:ipm-2uw6ujo6。
|
||||
AddressGroupId string // IP地址组ID,例如:ipmg-2uw6ujo6。
|
||||
}
|
||||
|
||||
type SecurityGroupPolicySet struct {
|
||||
Version string
|
||||
Egress []SecurityGroupPolicy // 出站规则。
|
||||
Ingress []SecurityGroupPolicy // 入站规则。
|
||||
}
|
||||
|
||||
type SSecurityGroup struct {
|
||||
vpc *SVpc
|
||||
SecurityGroupId string // 安全组实例ID,例如:sg-ohuuioma。
|
||||
SecurityGroupName string // 安全组名称,可任意命名,但不得超过60个字符。
|
||||
SecurityGroupDesc string // 安全组备注,最多100个字符。
|
||||
ProjectId string // 项目id,默认0。可在qcloud控制台项目管理页面查询到。
|
||||
IsDefault bool // 是否是默认安全组,默认安全组不支持删除。
|
||||
CreatedTime time.Time // 安全组创建时间。
|
||||
SecurityGroupPolicySet SecurityGroupPolicySet
|
||||
}
|
||||
|
||||
type SecurityGroupRuleSet []SecurityGroupPolicy
|
||||
|
||||
func (v SecurityGroupRuleSet) Len() int {
|
||||
return len(v)
|
||||
}
|
||||
|
||||
func (v SecurityGroupRuleSet) Swap(i, j int) {
|
||||
v[i], v[j] = v[j], v[i]
|
||||
}
|
||||
|
||||
func (v SecurityGroupRuleSet) Less(i, j int) bool {
|
||||
if v[i].PolicyIndex < v[j].PolicyIndex {
|
||||
return true
|
||||
} else if v[i].PolicyIndex == v[j].PolicyIndex {
|
||||
return strings.Compare(v[i].String(), v[j].String()) <= 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSecurityGroups(vpcId string, offset int, limit int) ([]SSecurityGroup, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
body, err := self.vpcRequest("DescribeSecurityGroups", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetSecurityGroups fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
secgrps := make([]SSecurityGroup, 0)
|
||||
err = body.Unmarshal(&secgrps, "SecurityGroupSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal security groups fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return secgrps, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetId() string {
|
||||
return self.SecurityGroupId
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetGlobalId() string {
|
||||
return self.SecurityGroupId
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetDescription() string {
|
||||
return self.SecurityGroupDesc
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetName() string {
|
||||
if len(self.SecurityGroupName) > 0 {
|
||||
return self.SecurityGroupName
|
||||
}
|
||||
return self.SecurityGroupId
|
||||
}
|
||||
|
||||
func (self *SecurityGroupPolicy) String() string {
|
||||
rules := self.toRules()
|
||||
result := []string{}
|
||||
for _, rule := range rules {
|
||||
result = append(result, rule.String())
|
||||
}
|
||||
return strings.Join(result, ";")
|
||||
}
|
||||
|
||||
func parseCIDR(cidr string) (*net.IPNet, error) {
|
||||
if strings.Index(cidr, "/") > 0 {
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
return ipnet, err
|
||||
}
|
||||
ip := net.ParseIP(cidr)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("Parse ip %s error", cidr)
|
||||
}
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}, nil
|
||||
}
|
||||
|
||||
func (self *SecurityGroupPolicy) toRules() []secrules.SecurityRule {
|
||||
result := []secrules.SecurityRule{}
|
||||
rule := secrules.SecurityRule{
|
||||
Action: secrules.SecurityRuleAllow,
|
||||
Protocol: secrules.PROTO_ANY,
|
||||
Direction: secrules.TSecurityRuleDirection(self.direction),
|
||||
Ports: []int{},
|
||||
PortStart: -1,
|
||||
PortEnd: -1,
|
||||
}
|
||||
if len(self.SecurityGroupId) != 0 {
|
||||
//安全组关联安全组的规则忽略
|
||||
return nil
|
||||
}
|
||||
if strings.ToLower(self.Action) == "drop" {
|
||||
rule.Action = secrules.SecurityRuleDeny
|
||||
}
|
||||
if utils.IsInStringArray(strings.ToLower(self.Protocol), []string{"tcp", "udp", "icmp"}) {
|
||||
rule.Protocol = strings.ToLower(self.Protocol)
|
||||
}
|
||||
if strings.Index(self.Port, ",") > 0 {
|
||||
for _, _port := range strings.Split(self.Port, ",") {
|
||||
port, err := strconv.Atoi(_port)
|
||||
if err != nil {
|
||||
log.Errorf("parse secgroup port %s %s error %v", self.Port, _port, err)
|
||||
continue
|
||||
}
|
||||
rule.Ports = append(rule.Ports, port)
|
||||
}
|
||||
} else if strings.Index(self.Port, "-") > 0 {
|
||||
ports := strings.Split(self.Port, "-")
|
||||
if len(ports) == 2 {
|
||||
portStart, err := strconv.Atoi(ports[0])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
portEnd, err := strconv.Atoi(ports[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rule.PortStart, rule.PortEnd = portStart, portEnd
|
||||
}
|
||||
} else if strings.ToLower(self.Port) != "all" {
|
||||
port, err := strconv.Atoi(self.Port)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rule.PortStart, rule.PortEnd = port, port
|
||||
}
|
||||
|
||||
if len(self.AddressTemplate.AddressGroupId) > 0 {
|
||||
addressGroup, total, err := self.vpc.region.AddressGroupList(self.AddressTemplate.AddressGroupId, "", 0, 1)
|
||||
if err != nil {
|
||||
log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err)
|
||||
return nil
|
||||
}
|
||||
if total != 1 {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(addressGroup[0].AddressTemplateIdSet); i++ {
|
||||
rules, err := self.getAddressRules(rule, addressGroup[0].AddressTemplateIdSet[i])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result = append(result, rules...)
|
||||
}
|
||||
} else if len(self.AddressTemplate.AddressId) > 0 {
|
||||
rules, err := self.getAddressRules(rule, self.AddressTemplate.AddressId)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result = append(result, rules...)
|
||||
} else if len(self.CidrBlock) > 0 {
|
||||
ipnet, err := parseCIDR(self.CidrBlock)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rule.IPNet = ipnet
|
||||
result = append(result, rule)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *SecurityGroupPolicy) getAddressRules(rule secrules.SecurityRule, addressId string) ([]secrules.SecurityRule, error) {
|
||||
result := []secrules.SecurityRule{}
|
||||
address, total, err := self.vpc.region.AddressList(addressId, "", 0, 1)
|
||||
if err != nil {
|
||||
log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err)
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, fmt.Errorf("failed to find address %s", addressId)
|
||||
}
|
||||
for _, ip := range address[0].AddressSet {
|
||||
ipnet, err := parseCIDR(ip)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
rule.IPNet = ipnet
|
||||
result = append(result, rule)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
|
||||
secgroup, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(secgroup.SecurityGroupPolicySet.Egress); i++ {
|
||||
secgroup.SecurityGroupPolicySet.Egress[i].direction = "out"
|
||||
}
|
||||
for i := 0; i < len(secgroup.SecurityGroupPolicySet.Ingress); i++ {
|
||||
secgroup.SecurityGroupPolicySet.Ingress[i].direction = "in"
|
||||
}
|
||||
originRules := []SecurityGroupPolicy{}
|
||||
originRules = append(secgroup.SecurityGroupPolicySet.Egress)
|
||||
originRules = append(secgroup.SecurityGroupPolicySet.Ingress)
|
||||
sort.Sort(SecurityGroupRuleSet(originRules))
|
||||
rules := []secrules.SecurityRule{}
|
||||
priority := 100
|
||||
for _, rule := range originRules {
|
||||
rule.vpc = self.vpc
|
||||
subRules := rule.toRules()
|
||||
for i := 0; i < len(subRules); i++ {
|
||||
subRules[i].Priority = priority
|
||||
}
|
||||
if len(subRules) > 0 {
|
||||
priority--
|
||||
}
|
||||
rules = append(rules, subRules...)
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetStatus() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) Refresh() error {
|
||||
if new, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSecurityGroupDetails(secGroupId string) (*SSecurityGroup, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["SecurityGroupId"] = secGroupId
|
||||
|
||||
body, err := self.vpcRequest("DescribeSecurityGroupPolicies", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeSecurityGroupAttribute fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secgrp := SSecurityGroup{SecurityGroupId: secGroupId}
|
||||
err = body.Unmarshal(&secgrp.SecurityGroupPolicySet, "SecurityGroupPolicySet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal security group details fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return &secgrp, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteSecurityGroup(secGroupId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["SecurityGroupId"] = secGroupId
|
||||
_, err := self.vpcRequest("DeleteSecurityGroup", params)
|
||||
return err
|
||||
}
|
||||
|
||||
type AddressTemplate struct {
|
||||
AddressSet []string
|
||||
AddressTemplateId string
|
||||
AddressTemplateName string
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
func (self *SRegion) AddressList(addressId, addressName string, offset, limit int) ([]AddressTemplate, int, error) {
|
||||
params := map[string]string{}
|
||||
filter := 0
|
||||
if len(addressId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressId
|
||||
filter++
|
||||
}
|
||||
if len(addressName) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressName
|
||||
filter++
|
||||
}
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
if limit == 0 {
|
||||
limit = 20
|
||||
}
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
body, err := self.vpcRequest("DescribeAddressTemplates", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
addressTemplates := []AddressTemplate{}
|
||||
err = body.Unmarshal(&addressTemplates, "AddressTemplateSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return addressTemplates, int(total), nil
|
||||
}
|
||||
|
||||
type AddressTemplateGroup struct {
|
||||
AddressTemplateIdSet []string
|
||||
AddressTemplateGroupName string
|
||||
AddressTemplateGroupId string
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
func (self *SRegion) AddressGroupList(groupId, groupName string, offset, limit int) ([]AddressTemplateGroup, int, error) {
|
||||
params := map[string]string{}
|
||||
filter := 0
|
||||
if len(groupId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupId
|
||||
filter++
|
||||
}
|
||||
if len(groupName) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupName
|
||||
filter++
|
||||
}
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
if limit == 0 {
|
||||
limit = 20
|
||||
}
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
body, err := self.vpcRequest("DescribeAddressTemplateGroups", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
addressTemplateGroups := []AddressTemplateGroup{}
|
||||
err = body.Unmarshal(&addressTemplateGroups, "AddressTemplateGroupSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return addressTemplateGroups, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateSecurityGroup(name, description string) (*SSecurityGroup, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["GroupName"] = name
|
||||
params["GroupDescription"] = description
|
||||
if len(description) == 0 {
|
||||
params["GroupDescription"] = "Customize Create"
|
||||
}
|
||||
secgroup := SSecurityGroup{}
|
||||
if body, err := self.vpcRequest("CreateSecurityGroup", params); err != nil {
|
||||
return nil, err
|
||||
} else if err := body.Unmarshal(&secgroup, "SecurityGroup"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &secgroup, nil
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
coslib "github.com/nelsonken/cos-go-sdk-v5/cos"
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type CosListOptions struct {
|
||||
}
|
||||
shellutils.R(&CosListOptions{}, "cos-list", "List COS buckets", func(cli *qcloud.SRegion, args *CosListOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := cos.GetBucketList(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result.Buckets.Bucket, len(result.Buckets.Bucket), 0, len(result.Buckets.Bucket), nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CosListBucketOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
}
|
||||
|
||||
shellutils.R(&CosListBucketOptions{}, "cos-bucket-list", "List content of a OSS bucket", func(cli *qcloud.SRegion, args *CosListBucketOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := cos.ListBucketContents(context.Background(), args.BUCKET, &coslib.QueryCondition{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result.Contents, len(result.Contents), 0, len(result.Contents), nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&CosListBucketOptions{}, "cos-bucket-create", "Create a OSS bucket", func(cli *qcloud.SRegion, args *CosListBucketOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.CreateBucket(context.Background(), args.BUCKET, &coslib.AccessControl{})
|
||||
})
|
||||
|
||||
type CosUploadOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
KEY string `help:"Object key"`
|
||||
FILE string `help:"Local file path"`
|
||||
Acl string `help:"Object ACL" choices:"private|public-read|public-read-write"`
|
||||
}
|
||||
shellutils.R(&CosUploadOptions{}, "cos-upload", "Upload a file to a Cos bucket", func(cli *qcloud.SRegion, args *CosUploadOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.Bucket(args.BUCKET).UploadObjectBySlice(context.Background(), args.KEY, args.FILE, 3, nil)
|
||||
})
|
||||
|
||||
type CosDownloadOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
NAME string `help:"File name"`
|
||||
}
|
||||
shellutils.R(&CosDownloadOptions{}, "cos-download", "Download a file", func(cli *qcloud.SRegion, args *CosDownloadOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//file
|
||||
return cos.Bucket(args.BUCKET).DownloadObject(context.Background(), args.NAME, os.Stdout)
|
||||
//return cos.Bucket(args.BUCKET).UploadObjectBySlice(context.Background(), args.KEY, args.FILE, 3, nil)
|
||||
})
|
||||
|
||||
type CosObjectAclOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
KEY string `help:"object key"`
|
||||
ACL string `help:"ACL" choices:"private|public-read|public-read-write"`
|
||||
}
|
||||
shellutils.R(&CosObjectAclOptions{}, "cos-set-acl", "Set acl for a object", func(cli *qcloud.SRegion, args *CosObjectAclOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.SetBucketACL(context.Background(), args.KEY, &coslib.AccessControl{ACL: args.ACL})
|
||||
})
|
||||
|
||||
type CosDeleteOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
KEY string `help:"Object key"`
|
||||
}
|
||||
|
||||
shellutils.R(&CosDeleteOptions{}, "cos-delete", "Delete a file from a Cos bucket", func(cli *qcloud.SRegion, args *CosDeleteOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.Bucket(args.BUCKET).DeleteObject(context.Background(), args.KEY)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DiskListOptions struct {
|
||||
Instance string `help:"Instance ID"`
|
||||
Zone string `help:"Zone ID"`
|
||||
Category string `help:"Disk category"`
|
||||
Offset int `help:"List offset"`
|
||||
Limit int `help:"List limit"`
|
||||
}
|
||||
shellutils.R(&DiskListOptions{}, "disk-list", "List disks", func(cli *qcloud.SRegion, args *DiskListOptions) error {
|
||||
disks, total, e := cli.GetDisks(args.Instance, args.Zone, args.Category, nil, args.Offset, args.Limit)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(disks, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
}
|
||||
shellutils.R(&DiskOptions{}, "disk-delete", "Delete disks", func(cli *qcloud.SRegion, args *DiskOptions) error {
|
||||
e := cli.DeleteDisk(args.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DiskOptions{}, "disk-show", "Show disk", func(cli *qcloud.SRegion, args *DiskOptions) error {
|
||||
disk, err := cli.GetDisk(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskCreateOptions struct {
|
||||
ZONE string `help:"Zone ID"`
|
||||
CATEGORY string `help:"Disk category" choices:"CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"`
|
||||
NAME string `help:"Disk Name"`
|
||||
SIZE int `help:"Disk Size GB"`
|
||||
}
|
||||
shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disk", func(cli *qcloud.SRegion, args *DiskCreateOptions) error {
|
||||
diskId, err := cli.CreateDisk(args.ZONE, args.CATEGORY, args.NAME, args.SIZE, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(diskId)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskResizeOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
SIZE int64 `help:"Disk Size GB"`
|
||||
}
|
||||
shellutils.R(&DiskResizeOptions{}, "disk-resize", "Resize disk", func(cli *qcloud.SRegion, args *DiskResizeOptions) error {
|
||||
return cli.ResizeDisk(args.ID, args.SIZE)
|
||||
})
|
||||
|
||||
type DiskResetOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
SNAPSHOT string `help:"Snapshot ID"`
|
||||
}
|
||||
shellutils.R(&DiskResetOptions{}, "disk-reset", "Reset disk", func(cli *qcloud.SRegion, args *DiskResetOptions) error {
|
||||
return cli.ResetDisk(args.ID, args.SNAPSHOT)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type EipListOptions struct {
|
||||
Eip string `help:"EIP ID"`
|
||||
Offset int `help:"List offset"`
|
||||
Limit int `help:"List limit"`
|
||||
}
|
||||
shellutils.R(&EipListOptions{}, "eip-list", "List eips", func(cli *qcloud.SRegion, args *EipListOptions) error {
|
||||
eips, total, err := cli.GetEips(args.Eip, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(eips, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipAllocateOptions struct {
|
||||
BANDWIDTH int `help:"EIP bandwoidth"`
|
||||
NAME string `help:"EIP Name"`
|
||||
ChargeType qcloud.TInternetChargeType `help:"EIP ChargeType"`
|
||||
}
|
||||
shellutils.R(&EipAllocateOptions{}, "eip-create", "Allocate an EIP", func(cli *qcloud.SRegion, args *EipAllocateOptions) error {
|
||||
eip, err := cli.AllocateEIP(args.NAME, args.BANDWIDTH, args.ChargeType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(eip)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipReleaseOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
}
|
||||
shellutils.R(&EipReleaseOptions{}, "eip-delete", "Release an EIP", func(cli *qcloud.SRegion, args *EipReleaseOptions) error {
|
||||
return cli.DeallocateEIP(args.ID)
|
||||
})
|
||||
|
||||
type EipShowOptions struct {
|
||||
ID string `help:"EIP ID"`
|
||||
}
|
||||
shellutils.R(&EipShowOptions{}, "eip-show", "Show an EIP", func(cli *qcloud.SRegion, args *EipShowOptions) error {
|
||||
eip, err := cli.GetEip(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(eip)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipAssociateOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
INSTANCE string `help:"Instance ID"`
|
||||
}
|
||||
shellutils.R(&EipAssociateOptions{}, "eip-associate", "Associate an EIP", func(cli *qcloud.SRegion, args *EipAssociateOptions) error {
|
||||
return cli.AssociateEip(args.ID, args.INSTANCE)
|
||||
})
|
||||
|
||||
type EipDissociateOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&EipDissociateOptions{}, "eip-dissociate", "Dissociate an EIP", func(cli *qcloud.SRegion, args *EipDissociateOptions) error {
|
||||
return cli.DissociateEip(args.ID)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ImageListOptions struct {
|
||||
Status string `help:"Image status"`
|
||||
Owner string `help:"Image owner" choices:"PRIVATE_IMAGE|PUBLIC_IMAGE|MARKET_IMAGE|SHARED_IMAGE"`
|
||||
Image string `help:"Image Id"`
|
||||
Name string `help:"Image Name"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&ImageListOptions{}, "image-list", "List images", func(cli *qcloud.SRegion, args *ImageListOptions) error {
|
||||
imageIds := []string{}
|
||||
if len(args.Image) > 0 {
|
||||
imageIds = append(imageIds, args.Image)
|
||||
}
|
||||
images, total, err := cli.GetImages(args.Status, args.Owner, imageIds, args.Name, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(images, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageCreateOptions struct {
|
||||
NAME string `helo:"Image name"`
|
||||
OSTYPE string `helo:"Operation system" choices:"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"`
|
||||
OSARCH string `help:"OS Architecture" choices:"x86_64|i386"`
|
||||
osVersion string `help:"OS Version"`
|
||||
URL string `helo:"Cos URL"`
|
||||
}
|
||||
|
||||
shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *qcloud.SRegion, args *ImageCreateOptions) error {
|
||||
image, err := cli.ImportImage(args.NAME, args.OSARCH, args.OSTYPE, args.osVersion, args.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageDeleteOptions struct {
|
||||
ID string `helo:"Image ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *qcloud.SRegion, args *ImageDeleteOptions) error {
|
||||
return cli.DeleteImage(args.ID)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type InstanceListOptions struct {
|
||||
Id []string `help:"IDs of instances to show"`
|
||||
Zone string `help:"Zone ID"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *qcloud.SRegion, args *InstanceListOptions) error {
|
||||
instances, total, e := cli.GetInstances(args.Zone, args.Id, args.Offset, args.Limit)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(instances, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceCrateOptions struct {
|
||||
NAME string `help:"name of instance"`
|
||||
IMAGE string `help:"image ID"`
|
||||
CPU int `help:"CPU count"`
|
||||
MEMORYGB int `help:"MemoryGB"`
|
||||
Disk []int `help:"Data disk sizes int GB"`
|
||||
STORAGE string `help:"Storage type" choices:"LOCAL_BASIC|LOCAL_SSD|CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"`
|
||||
NETWORK string `help:"Network ID"`
|
||||
PASSWD string `help:"password"`
|
||||
PublicKey string `help:"PublicKey"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceCrateOptions{}, "instance-create", "Create a instance", func(cli *qcloud.SRegion, args *InstanceCrateOptions) error {
|
||||
instance, e := cli.CreateInstanceSimple(args.NAME, args.IMAGE, args.CPU, args.MEMORYGB, args.STORAGE, args.Disk, args.NETWORK, args.PASSWD, args.PublicKey)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(instance)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceDiskOperationOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
DISK string `help:"disk ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDiskOperationOptions{}, "instance-attach-disk", "Attach a disk to instance", func(cli *qcloud.SRegion, args *InstanceDiskOperationOptions) error {
|
||||
err := cli.AttachDisk(args.ID, args.DISK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceDiskOperationOptions{}, "instance-detach-disk", "Detach a disk to instance", func(cli *qcloud.SRegion, args *InstanceDiskOperationOptions) error {
|
||||
err := cli.DetachDisk(args.ID, args.DISK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceOperationOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
}
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-start", "Start a instance", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
|
||||
err := cli.StartVM(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-vnc", "Get a instance VNC url", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
|
||||
url, err := cli.GetInstanceVNCUrl(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(url)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceStopOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Force bool `help:"Force stop instance"`
|
||||
}
|
||||
shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *qcloud.SRegion, args *InstanceStopOptions) error {
|
||||
err := cli.StopVM(args.ID, args.Force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-delete", "Delete a instance", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
|
||||
err := cli.DeleteVM(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
/*
|
||||
server-change-config 更改系统配置
|
||||
server-reset
|
||||
*/
|
||||
type InstanceDeployOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Name string `help:"new instance name"`
|
||||
Hostname string `help:"new hostname"`
|
||||
Keypair string `help:"Keypair Name"`
|
||||
DeleteKeypair bool `help:"Remove SSH keypair"`
|
||||
Password string `help:"new password"`
|
||||
// ResetPassword bool `help:"Force reset password"`
|
||||
Description string `help:"new instances description"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDeployOptions{}, "instance-deploy", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceDeployOptions) error {
|
||||
err := cli.DeployVM(args.ID, args.Name, args.Password, args.Keypair, args.DeleteKeypair, args.Description)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceRebuildRootOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Image string `help:"Image ID"`
|
||||
Password string `help:"pasword"`
|
||||
Keypair string `help:"keypair name"`
|
||||
Size int `help:"system disk size in GB"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *qcloud.SRegion, args *InstanceRebuildRootOptions) error {
|
||||
diskID, err := cli.ReplaceSystemDisk(args.ID, args.Image, args.Password, args.Keypair, args.Size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("New diskID is %s", diskID)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceChangeConfigOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Ncpu int `help:"number of CPU"`
|
||||
Vmem int `help:"MiB of memory"`
|
||||
Disk []int `help:"Data disk sizes int GB"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceChangeConfigOptions) error {
|
||||
instance, e := cli.GetInstance(args.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// todo : add create disks
|
||||
err := cli.ChangeVMConfig(instance.Placement.Zone, args.ID, args.Ncpu, args.Vmem, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceUpdatePasswordOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
PASSWD string `help:"new password"`
|
||||
}
|
||||
shellutils.R(&InstanceUpdatePasswordOptions{}, "instance-update-password", "Update instance password", func(cli *qcloud.SRegion, args *InstanceUpdatePasswordOptions) error {
|
||||
err := cli.UpdateInstancePassword(args.ID, args.PASSWD)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type KeyPairListOptions struct {
|
||||
Name string `help:"Keypair Name"`
|
||||
IDs []string `help:"Keypari ids"`
|
||||
Offset int `help:"List offset"`
|
||||
Limit int `help:"List limit"`
|
||||
}
|
||||
shellutils.R(&KeyPairListOptions{}, "keypair-list", "List keypair", func(cli *qcloud.SRegion, args *KeyPairListOptions) error {
|
||||
keypairs, total, err := cli.GetKeypairs(args.Name, args.IDs, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(keypairs, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type KeyPairCreateOptions struct {
|
||||
NAME string `help:"Keypair Name"`
|
||||
}
|
||||
shellutils.R(&KeyPairCreateOptions{}, "keypair-create", "Create keypair", func(cli *qcloud.SRegion, args *KeyPairCreateOptions) error {
|
||||
keypair, err := cli.CreateKeyPair(args.NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(keypair)
|
||||
return nil
|
||||
})
|
||||
|
||||
type KeyPairAssociateOptions struct {
|
||||
KEYPAIRID string `help:"Keypair ID"`
|
||||
INSTANCEID string `help:"Instance ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&KeyPairAssociateOptions{}, "keypair-associate-instance", "Attach Keypair to a instance", func(cli *qcloud.SRegion, args *KeyPairAssociateOptions) error {
|
||||
return cli.AttachKeypair(args.INSTANCEID, args.KEYPAIRID)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type NetworkListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&NetworkListOptions{}, "network-list", "List networks", func(cli *qcloud.SRegion, args *NetworkListOptions) error {
|
||||
networks, total, e := cli.GetNetworks(nil, "", args.Offset, args.Limit)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(networks, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type NetworkOptions struct {
|
||||
ID string `help:"Network ID"`
|
||||
}
|
||||
shellutils.R(&NetworkOptions{}, "network-delete", "Delete network", func(cli *qcloud.SRegion, args *NetworkOptions) error {
|
||||
return cli.DeleteNetwork(args.ID)
|
||||
})
|
||||
|
||||
shellutils.R(&NetworkOptions{}, "network-show", "Show network", func(cli *qcloud.SRegion, args *NetworkOptions) error {
|
||||
network, err := cli.GetNetwork(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(network)
|
||||
return nil
|
||||
})
|
||||
|
||||
type NetworkCreateOptions struct {
|
||||
ZONE string `help:"Zone ID"`
|
||||
VPC string `help:"VPC ID"`
|
||||
CIDR string `help:"Network CIDR"`
|
||||
NAME string `help:"Network Name"`
|
||||
}
|
||||
shellutils.R(&NetworkCreateOptions{}, "network-create", "Create network", func(cli *qcloud.SRegion, args *NetworkCreateOptions) error {
|
||||
networkId, err := cli.CreateNetwork(args.ZONE, args.VPC, args.NAME, args.CIDR, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(networkId)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package shell
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/util/printutils"
|
||||
|
||||
func printList(data interface{}, total, offset, limit int, columns []string) {
|
||||
printutils.PrintInterfaceList(data, total, offset, limit, columns)
|
||||
}
|
||||
|
||||
func printObject(obj interface{}) {
|
||||
printutils.PrintInterfaceObject(obj)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type RegionListOptions struct {
|
||||
}
|
||||
shellutils.R(&RegionListOptions{}, "region-list", "List regions", func(cli *qcloud.SRegion, args *RegionListOptions) error {
|
||||
regions := cli.GetClient().GetRegions()
|
||||
printList(regions, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type SecurityGroupListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&SecurityGroupListOptions{}, "security-group-list", "List SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupListOptions) error {
|
||||
secgrps, total, err := cli.GetSecurityGroups("", args.Limit, args.Offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(secgrps, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type SecurityGroupOptions struct {
|
||||
ID string `help:"SecurityGroup ID"`
|
||||
}
|
||||
shellutils.R(&SecurityGroupOptions{}, "security-group-show", "Show SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error {
|
||||
secgroup, err := cli.GetSecurityGroupDetails(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(secgroup)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&SecurityGroupOptions{}, "security-group-delete", "Delete SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error {
|
||||
return cli.DeleteSecurityGroup(args.ID)
|
||||
})
|
||||
|
||||
type SecurityGroupCreateOptions struct {
|
||||
NAME string `help:"SecurityGroup Name"`
|
||||
Desc string `help:"SecurityGroup Description"`
|
||||
}
|
||||
|
||||
shellutils.R(&SecurityGroupCreateOptions{}, "security-group-create", "Create SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupCreateOptions) error {
|
||||
secgrp, err := cli.CreateSecurityGroup(args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(secgrp)
|
||||
return nil
|
||||
})
|
||||
|
||||
type AddressShowOptions struct {
|
||||
Id string `help:"IP address ID"`
|
||||
Name string `help:"IP address name"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&AddressShowOptions{}, "address-list", "Show address", func(cli *qcloud.SRegion, args *AddressShowOptions) error {
|
||||
address, total, err := cli.AddressList(args.Id, args.Name, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(address, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type SnapshotListOptions struct {
|
||||
DiskId string `help:"Disk ID"`
|
||||
InstanceId string `help:"Instance ID"`
|
||||
SnapshotIds []string `helo:"Snapshot ids"`
|
||||
Name string `help:"Snapshot Name"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&SnapshotListOptions{}, "snapshot-list", "List snapshot", func(cli *qcloud.SRegion, args *SnapshotListOptions) error {
|
||||
snapshots, total, err := cli.GetSnapshots(args.InstanceId, args.DiskId, args.Name, args.SnapshotIds, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(snapshots, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type SnapshotDeleteOptions struct {
|
||||
ID string `help:"Snapshot ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotDeleteOptions{}, "snapshot-delete", "Delete snapshot", func(cli *qcloud.SRegion, args *SnapshotDeleteOptions) error {
|
||||
return cli.DeleteSnapshot(args.ID)
|
||||
})
|
||||
|
||||
type SnapshotCreateOptions struct {
|
||||
DISK string `help:"Disk ID"`
|
||||
Name string `help:"Snapeshot Name"`
|
||||
Desc string `help:"Snapshot Desc"`
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "Create snapshot", func(cli *qcloud.SRegion, args *SnapshotCreateOptions) error {
|
||||
_, err := cli.CreateSnapshot(args.DISK, args.Name, args.Desc)
|
||||
return err
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type VpcListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *qcloud.SRegion, args *VpcListOptions) error {
|
||||
vpcs, total, err := cli.GetVpcs(nil, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(vpcs, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type VpcCreateOptions struct {
|
||||
NAME string `help:"Name for vpc"`
|
||||
CIDR string `help:"Cidr for vpc" choices:"10.0.0.0/16|172.16.0.0/12|192.168.0.0/16"`
|
||||
}
|
||||
shellutils.R(&VpcCreateOptions{}, "vpc-create", "Create vpc", func(cli *qcloud.SRegion, args *VpcCreateOptions) error {
|
||||
vpc, err := cli.CreateIVpc(args.NAME, "", args.CIDR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(vpc)
|
||||
return nil
|
||||
})
|
||||
|
||||
type VpcDeleteOptions struct {
|
||||
ID string `help:"VPC ID or Name"`
|
||||
}
|
||||
shellutils.R(&VpcDeleteOptions{}, "vpc-delete", "Delete vpc", func(cli *qcloud.SRegion, args *VpcDeleteOptions) error {
|
||||
return cli.DeleteVpc(args.ID)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ZoneListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&ZoneListOptions{}, "zone-list", "List zones", func(cli *qcloud.SRegion, args *ZoneListOptions) error {
|
||||
zones, err := cli.GetIZones()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(zones, len(zones), args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SnapshotStatusType string
|
||||
|
||||
const (
|
||||
SnapshotStatusAccomplished SnapshotStatusType = "NORMAL"
|
||||
SnapshotStatusProgress SnapshotStatusType = "CREATING"
|
||||
SnapshotStatusFailed SnapshotStatusType = "failed"
|
||||
)
|
||||
|
||||
type SSnapshot struct {
|
||||
region *SRegion
|
||||
|
||||
SnapshotId string // 快照ID。
|
||||
Placement Placement // 快照所在的位置。
|
||||
DiskUsage string // 创建此快照的云硬盘类型。取值范围:SYSTEM_DISK:系统盘 DATA_DISK:数据盘。
|
||||
DiskId string // 创建此快照的云硬盘ID。
|
||||
DiskSize int32 // 创建此快照的云硬盘大小,单位GB。
|
||||
SnapshotState SnapshotStatusType // 快照的状态。取值范围: NORMAL:正常 CREATING:创建中 ROLLBACKING:回滚中 COPYING_FROM_REMOTE:跨地域复制快照拷贝中。
|
||||
SnapshotName string // 快照名称,用户自定义的快照别名。调用ModifySnapshotAttribute可修改此字段。
|
||||
Percent int // 快照创建进度百分比,快照创建成功后此字段恒为100。
|
||||
CreateTime time.Time // 快照的创建时间。
|
||||
DeadlineTime time.Time // 快照到期时间。如果快照为永久保留,此字段为空。
|
||||
Encrypt bool // 是否为加密盘创建的快照。取值范围:true:该快照为加密盘创建的 false:非加密盘创建的快照。
|
||||
IsPermanent bool // 是否为永久快照。取值范围: true:永久快照 false:非永久快照。
|
||||
CopyingToRegions []string // 快照正在跨地域复制的目的地域,默认取值为[]。
|
||||
CopyFromRemote bool // 是否为跨地域复制的快照。取值范围:true:表示为跨地域复制的快照。 false:本地域的快照。
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots, total, err := self.GetSnapshots("", "", "", []string{snapshotId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
if total == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &snapshots[0], nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetStatus() string {
|
||||
// NORMAL:正常
|
||||
// CREATING:创建中
|
||||
// ROLLBACKING:回滚中
|
||||
// COPYING_FROM_REMOTE:跨地域复制快照拷贝中。
|
||||
switch self.SnapshotState {
|
||||
case "NORMAL", "COPYING_FROM_REMOTE":
|
||||
return models.SNAPSHOT_READY
|
||||
case "CREATING":
|
||||
return models.SNAPSHOT_CREATING
|
||||
case "ROLLBACKING":
|
||||
return models.SNAPSHOT_ROLLBACKING
|
||||
}
|
||||
return models.SNAPSHOT_UNKNOWN
|
||||
}
|
||||
|
||||
func (self *SSnapshot) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SSnapshot) Refresh() error {
|
||||
snapshots, total, err := self.region.GetSnapshots("", "", "", []string{self.SnapshotId}, 0, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if total > 1 {
|
||||
return cloudprovider.ErrDuplicateId
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
return cloudprovider.ErrNotFound
|
||||
}
|
||||
return jsonutils.Update(self, snapshots[0])
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSnapshots(instanceId string, diskId string, snapshotName string, snapshotIds []string, offset int, limit int) ([]SSnapshot, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
filter := 0
|
||||
if len(instanceId) > 0 {
|
||||
}
|
||||
if len(diskId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "disk-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values", filter)] = diskId
|
||||
filter++
|
||||
}
|
||||
if len(snapshotName) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "snapshot-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values", filter)] = snapshotName
|
||||
filter++
|
||||
}
|
||||
if snapshotIds != nil && len(snapshotIds) > 0 {
|
||||
for index, snapshotId := range snapshotIds {
|
||||
params[fmt.Sprintf("SnapshotIds.%d", index)] = snapshotId
|
||||
}
|
||||
}
|
||||
snapshots := []SSnapshot{}
|
||||
body, err := self.cbsRequest("DescribeSnapshots", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetSnapshots fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
body.Unmarshal(&snapshots, "SnapshotSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
snapshots[i].region = self
|
||||
}
|
||||
return snapshots, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots, total, err := self.GetSnapshots("", "", "", []string{}, 0, 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for len(snapshots) < total {
|
||||
var parts []SSnapshot
|
||||
parts, total, err = self.GetSnapshots("", "", "", []string{}, len(snapshots), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshots = append(snapshots, parts...)
|
||||
}
|
||||
ret := make([]cloudprovider.ICloudSnapshot, len(snapshots))
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
ret[i] = &snapshots[i]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetRegionId() string {
|
||||
return self.region.GetId()
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetSize() int32 {
|
||||
return self.DiskSize
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetDiskId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetId() string {
|
||||
return self.SnapshotId
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s", self.SnapshotId)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetName() string {
|
||||
return self.SnapshotName
|
||||
}
|
||||
|
||||
func (self *SSnapshot) Delete() error {
|
||||
if self.region == nil {
|
||||
return fmt.Errorf("not init region for snapshot %s", self.SnapshotId)
|
||||
}
|
||||
return self.region.DeleteSnapshot(self.SnapshotId)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetDiskType() string {
|
||||
switch self.DiskUsage {
|
||||
case "SYSTEM_DISK":
|
||||
return models.DISK_TYPE_SYS
|
||||
case "DATA_DISK":
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteSnapshot(snapshotId string) error {
|
||||
params := map[string]string{"SnapshotIds.0": snapshotId}
|
||||
_, err := self.cbsRequest("DeleteSnapshots", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateSnapshot(diskId, name, desc string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["DiskId"] = diskId
|
||||
params["SnapshotName"] = name
|
||||
|
||||
body, err := self.cbsRequest("CreateSnapshot", params)
|
||||
if err != nil {
|
||||
log.Errorf("CreateSnapshot fail %s", err)
|
||||
return "", err
|
||||
}
|
||||
return body.GetString("SnapshotId")
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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 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
|
||||
}
|
||||
disk, err := self.zone.region.GetDisk(diskId)
|
||||
if err != nil {
|
||||
log.Errorf("getDisk fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
disk.storage = self
|
||||
return disk, nil
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) {
|
||||
disk, err := self.zone.region.GetDisk(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disk.storage = self
|
||||
return disk, nil
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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 {
|
||||
return fmt.Sprintf("http://%s-%s.cosd.myqcloud.com/%s", bucket, self.client.AppID, 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
|
||||
}
|
||||
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-%s", self.region.GetId(), self.region.client.providerId))
|
||||
if err := cos.BucketExists(context.Background(), bucketName); err != nil {
|
||||
log.Debugf("Bucket %s not exists, to create ...", bucketName)
|
||||
if err := cos.CreateBucket(context.Background(), bucketName, &coslib.AccessControl{ACL: "public-read"}); 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)
|
||||
if err := cos.Bucket(bucketName).UploadObject(context.Background(), imageId, reader, &coslib.AccessControl{}); err != nil {
|
||||
log.Errorf("UploadObject error %s %s", imageId, err)
|
||||
return "", err
|
||||
}
|
||||
imageBaseName := imageId
|
||||
if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' {
|
||||
imageBaseName = fmt.Sprintf("img%s", imageId)
|
||||
}
|
||||
imageName := imageBaseName
|
||||
nameIdx := 1
|
||||
|
||||
// check image name, avoid name conflict
|
||||
for {
|
||||
_, err = self.region.GetImageByName(imageName)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
break
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx)
|
||||
nameIdx++
|
||||
}
|
||||
|
||||
log.Debugf("Import image %s", imageName)
|
||||
if image, err := self.region.ImportImage(imageName, osArch, osType, osDist, self.region.getCosUrl(bucketName, imageId)); err != nil {
|
||||
return "", err
|
||||
} else if cloudprovider.WaitStatus(image, string(ImageStatusAvailable), 15*time.Second, 3600*time.Second); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return image.ImageId, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
)
|
||||
|
||||
type SVpc struct {
|
||||
region *SRegion
|
||||
|
||||
iwires []cloudprovider.ICloudWire
|
||||
|
||||
secgroups []cloudprovider.ICloudSecurityGroup
|
||||
|
||||
CidrBlock string
|
||||
CreatedTime time.Time
|
||||
DhcpOptionsId string
|
||||
DnsServerSet []string
|
||||
DomainName string
|
||||
EnableMulticast bool
|
||||
IsDefault bool
|
||||
VpcId string
|
||||
VpcName string
|
||||
}
|
||||
|
||||
func (self *SVpc) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SVpc) GetName() string {
|
||||
if len(self.VpcName) > 0 {
|
||||
return self.VpcName
|
||||
}
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SVpc) GetGlobalId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SVpc) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIsDefault() bool {
|
||||
return self.IsDefault
|
||||
}
|
||||
|
||||
func (self *SVpc) GetCidrBlock() string {
|
||||
return self.CidrBlock
|
||||
}
|
||||
|
||||
func (self *SVpc) GetStatus() string {
|
||||
return models.VPC_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SVpc) Delete() error {
|
||||
return self.region.DeleteVpc(self.VpcId)
|
||||
}
|
||||
|
||||
func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
|
||||
secgroups := make([]SSecurityGroup, 0)
|
||||
for {
|
||||
parts, total, err := self.region.GetSecurityGroups(self.VpcId, len(secgroups), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secgroups = append(secgroups, parts...)
|
||||
if len(secgroups) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
isecgroups := make([]cloudprovider.ICloudSecurityGroup, len(secgroups))
|
||||
for i := 0; i < len(secgroups); i++ {
|
||||
secgroups[i].vpc = self
|
||||
isecgroups[i] = &secgroups[i]
|
||||
}
|
||||
return isecgroups, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) getWireByZoneId(zoneId string) *SWire {
|
||||
for i := 0; i <= len(self.iwires); i++ {
|
||||
wire := self.iwires[i].(*SWire)
|
||||
if wire.zone.Zone == zoneId {
|
||||
return wire
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVpc) fetchNetworks() error {
|
||||
networks, total, err := self.region.GetNetworks(nil, self.VpcId, 0, 50)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if total > len(networks) {
|
||||
networks, _, err = self.region.GetNetworks(nil, self.VpcId, 0, total)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
wire := self.getWireByZoneId(networks[i].Zone)
|
||||
networks[i].wire = wire
|
||||
wire.addNetwork(&networks[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) {
|
||||
if self.iwires == nil {
|
||||
err := self.fetchNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(self.iwires); i += 1 {
|
||||
if self.iwires[i].GetGlobalId() == wireId {
|
||||
return self.iwires[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
if self.iwires == nil {
|
||||
err := self.fetchNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.iwires, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SVpc) GetRegion() cloudprovider.ICloudRegion {
|
||||
return self.region
|
||||
}
|
||||
|
||||
func (self *SVpc) Refresh() error {
|
||||
new, err := self.region.getVpc(self.VpcId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SVpc) SyncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) (string, error) {
|
||||
secgrpId := ""
|
||||
// if secgroup, err := self.region.getSecurityGroupByTag(self.VpcId, secgroupId); err != nil {
|
||||
// if secgrpId, err = self.region.createSecurityGroup(self.VpcId, name, ""); err != nil {
|
||||
// return "", err
|
||||
// } else if err := self.region.addTagToSecurityGroup(secgrpId, "id", secgroupId, 1); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// //addRules
|
||||
// log.Debugf("Add Rules for %s", secgrpId)
|
||||
// for _, rule := range rules {
|
||||
// if err := self.region.addSecurityGroupRule(secgrpId, &rule); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// //syncRules
|
||||
// secgrpId = secgroup.SecurityGroupId
|
||||
// log.Debugf("Sync Rules for %s", secgroup.GetName())
|
||||
// if secgroup.GetName() != name {
|
||||
// if err := self.region.modifySecurityGroup(secgrpId, name, ""); err != nil {
|
||||
// log.Errorf("Change SecurityGroup name to %s failed: %v", name, err)
|
||||
// }
|
||||
// }
|
||||
// self.region.syncSecgroupRules(secgrpId, rules)
|
||||
// }
|
||||
return secgrpId, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) addWire(wire *SWire) {
|
||||
if self.iwires == nil {
|
||||
self.iwires = make([]cloudprovider.ICloudWire, 0)
|
||||
}
|
||||
self.iwires = append(self.iwires, wire)
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SWire struct {
|
||||
zone *SZone
|
||||
vpc *SVpc
|
||||
|
||||
inetworks []cloudprovider.ICloudNetwork
|
||||
}
|
||||
|
||||
func (self *SWire) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetId() string {
|
||||
return fmt.Sprintf("%s-%s", self.vpc.GetId(), self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SWire) GetName() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SWire) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SWire) GetStatus() string {
|
||||
return "available"
|
||||
}
|
||||
|
||||
func (self *SWire) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s", self.vpc.GetGlobalId(), self.zone.GetGlobalId())
|
||||
}
|
||||
|
||||
func (self *SWire) GetIVpc() cloudprovider.ICloudVpc {
|
||||
return self.vpc
|
||||
}
|
||||
|
||||
func (self *SWire) GetIZone() cloudprovider.ICloudZone {
|
||||
return self.zone
|
||||
}
|
||||
|
||||
func (self *SWire) GetBandwidth() int {
|
||||
return 10000
|
||||
}
|
||||
|
||||
func (self *SWire) GetNetworkById(networkId string) *SNetwork {
|
||||
networks, err := self.GetINetworks()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.Debugf("search for networks %d", len(networks))
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
log.Debugf("search %s", networks[i].GetName())
|
||||
network := networks[i].(*SNetwork)
|
||||
if network.SubnetId == networkId {
|
||||
return network
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
|
||||
networkId, err := self.zone.region.CreateNetwork(self.zone.Zone, self.vpc.VpcId, name, cidr, desc)
|
||||
if err != nil {
|
||||
log.Errorf("CreateNetwork error %s", err)
|
||||
return nil, err
|
||||
}
|
||||
self.inetworks = nil
|
||||
network := self.GetNetworkById(networkId)
|
||||
if network == nil {
|
||||
log.Errorf("cannot find vswitch after create????")
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return network, nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) {
|
||||
networks, err := self.GetINetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
if networks[i].GetGlobalId() == netid {
|
||||
return networks[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SWire) getNetworkById(networkId string) *SNetwork {
|
||||
networks, err := self.GetINetworks()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.Debugf("search for networks %d", len(networks))
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
log.Debugf("search %s", networks[i].GetName())
|
||||
network := networks[i].(*SNetwork)
|
||||
if network.SubnetId == networkId {
|
||||
return network
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) {
|
||||
if self.inetworks == nil {
|
||||
err := self.vpc.fetchNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.inetworks, nil
|
||||
}
|
||||
|
||||
func (self *SWire) addNetwork(network *SNetwork) {
|
||||
if self.inetworks == nil {
|
||||
self.inetworks = make([]cloudprovider.ICloudNetwork, 0)
|
||||
}
|
||||
find := false
|
||||
for i := 0; i < len(self.inetworks); i += 1 {
|
||||
if self.inetworks[i].GetId() == network.SubnetId {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
self.inetworks = append(self.inetworks, network)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
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/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) {
|
||||
return &SLocalStorage{zone: self, storageType: category}, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
+1
-1
@@ -57,7 +57,7 @@ func (resolver *LocationResolver) TryResolve(param *ResolveParam) (endpoint stri
|
||||
getEndpointRequest.Product = "Location"
|
||||
getEndpointRequest.Version = "2015-06-12"
|
||||
getEndpointRequest.ApiName = "DescribeEndpoints"
|
||||
getEndpointRequest.Domain = "location.aliyuncs.com"
|
||||
getEndpointRequest.Domain = "location-readonly.aliyuncs.com"
|
||||
getEndpointRequest.Method = "GET"
|
||||
getEndpointRequest.Scheme = requests.HTTPS
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/konsorten/go-windows-terminal-sequences
|
||||
+2
-5
@@ -1,21 +1,18 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- tip
|
||||
|
||||
env:
|
||||
- TESTS="-race -v -bench=. -coverprofile=coverage.txt -covermode=atomic"
|
||||
- TESTS="-race -v ./..."
|
||||
|
||||
before_install:
|
||||
# don't use the miekg/dns when testing forks
|
||||
- mkdir -p $GOPATH/src/github.com/miekg
|
||||
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
||||
|
||||
script:
|
||||
- go test $TESTS
|
||||
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
+41
-5
@@ -3,19 +3,55 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ed25519","ed25519/internal/edwards25519"]
|
||||
revision = "b47b1587369238182299fe4dad77d05b8b461e06"
|
||||
packages = [
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:08e41d63f8dac84d83797368b56cf0b339e42d0224e5e56668963c28aec95685"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"]
|
||||
revision = "1e491301e022f8f977054da4c2d852decd59571f"
|
||||
packages = [
|
||||
"bpf",
|
||||
"context",
|
||||
"internal/iana",
|
||||
"internal/socket",
|
||||
"ipv4",
|
||||
"ipv6",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "4dfa2610cdf3b287375bbba5b8f2a14d3b01d8de"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
pruneopts = ""
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:149a432fabebb8221a80f77731b1cd63597197ded4f14af606ebe3a0959004ec"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
pruneopts = ""
|
||||
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "c4abc38abaeeeeb9be92455c9c02cae32841122b8982aaa067ef25bb8e86ff9d"
|
||||
input-imports = [
|
||||
"golang.org/x/crypto/ed25519",
|
||||
"golang.org/x/net/ipv4",
|
||||
"golang.org/x/net/ipv6",
|
||||
"golang.org/x/sync/errgroup",
|
||||
"golang.org/x/sys/unix",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
+12
@@ -24,3 +24,15 @@
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sync"
|
||||
|
||||
+1
-1
@@ -567,7 +567,7 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg,
|
||||
if deadline, ok := ctx.Deadline(); !ok {
|
||||
timeout = 0
|
||||
} else {
|
||||
timeout = deadline.Sub(time.Now())
|
||||
timeout = time.Until(deadline)
|
||||
}
|
||||
// not passing the context to the underlying calls, as the API does not support
|
||||
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
|
||||
|
||||
+3
-1
@@ -107,6 +107,8 @@ BuildRR:
|
||||
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
} else if start + offset < 0 || end + offset > 1<<31-1 {
|
||||
return "bad offset in $GENERATE"
|
||||
}
|
||||
j += 2 + sep // Jump to it
|
||||
}
|
||||
@@ -152,7 +154,7 @@ func modToPrintf(s string) (string, int, error) {
|
||||
return "", 0, errors.New("bad base in $GENERATE")
|
||||
}
|
||||
offset, err := strconv.Atoi(xs[0])
|
||||
if err != nil || offset > 255 {
|
||||
if err != nil {
|
||||
return "", 0, errors.New("bad offset in $GENERATE")
|
||||
}
|
||||
width, err := strconv.Atoi(xs[1])
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
// +build go1.11,!windows
|
||||
// +build go1.11
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
package dns
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !go1.11 windows
|
||||
// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
|
||||
|
||||
package dns
|
||||
|
||||
|
||||
+13
-38
@@ -302,6 +302,12 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c
|
||||
}
|
||||
// If we did compression and we find something add the pointer here
|
||||
if pointer != -1 {
|
||||
// Clear the msg buffer after the pointer location, otherwise
|
||||
// packDataNsec writes the wrong data to msg.
|
||||
tainted := msg[nameoffset:off]
|
||||
for i := range tainted {
|
||||
tainted[i] = 0
|
||||
}
|
||||
// We have two bytes (14 bits) to put the pointer in
|
||||
// if msg == nil, we will never do compression
|
||||
binary.BigEndian.PutUint16(msg[nameoffset:], uint16(pointer^0xC000))
|
||||
@@ -367,12 +373,10 @@ Loop:
|
||||
var buf [3]byte
|
||||
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||||
s = append(s, '\\')
|
||||
for i := 0; i < 3-len(bufs); i++ {
|
||||
for i := len(bufs); i < 3; i++ {
|
||||
s = append(s, '0')
|
||||
}
|
||||
for _, r := range bufs {
|
||||
s = append(s, r)
|
||||
}
|
||||
s = append(s, bufs...)
|
||||
// presentation-format \DDD escapes add 3 extra bytes
|
||||
maxLen += 3
|
||||
} else {
|
||||
@@ -512,7 +516,7 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {
|
||||
off = off0
|
||||
var s string
|
||||
for off < len(msg) && err == nil {
|
||||
s, off, err = unpackTxtString(msg, off)
|
||||
s, off, err = unpackString(msg, off)
|
||||
if err == nil {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
@@ -520,39 +524,6 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func unpackTxtString(msg []byte, offset int) (string, int, error) {
|
||||
if offset+1 > len(msg) {
|
||||
return "", offset, &Error{err: "overflow unpacking txt"}
|
||||
}
|
||||
l := int(msg[offset])
|
||||
if offset+l+1 > len(msg) {
|
||||
return "", offset, &Error{err: "overflow unpacking txt"}
|
||||
}
|
||||
s := make([]byte, 0, l)
|
||||
for _, b := range msg[offset+1 : offset+1+l] {
|
||||
switch b {
|
||||
case '"', '\\':
|
||||
s = append(s, '\\', b)
|
||||
default:
|
||||
if b < 32 || b > 127 { // unprintable
|
||||
var buf [3]byte
|
||||
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||||
s = append(s, '\\')
|
||||
for i := 0; i < 3-len(bufs); i++ {
|
||||
s = append(s, '0')
|
||||
}
|
||||
for _, r := range bufs {
|
||||
s = append(s, r)
|
||||
}
|
||||
} else {
|
||||
s = append(s, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += 1 + l
|
||||
return string(s), offset, nil
|
||||
}
|
||||
|
||||
// Helpers for dealing with escaped bytes
|
||||
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
|
||||
|
||||
@@ -560,6 +531,10 @@ func dddToByte(s []byte) byte {
|
||||
return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
|
||||
}
|
||||
|
||||
func dddStringToByte(s string) byte {
|
||||
return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
|
||||
}
|
||||
|
||||
// Helper function for packing and unpacking
|
||||
func intToBytes(i *big.Int, length int) []byte {
|
||||
buf := i.Bytes()
|
||||
|
||||
+11
-19
@@ -6,7 +6,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// helper functions called from the generated zmsg.go
|
||||
@@ -267,29 +267,21 @@ func unpackString(msg []byte, off int) (string, int, error) {
|
||||
if off+l+1 > len(msg) {
|
||||
return "", off, &Error{err: "overflow unpacking txt"}
|
||||
}
|
||||
s := make([]byte, 0, l)
|
||||
var s strings.Builder
|
||||
s.Grow(l)
|
||||
for _, b := range msg[off+1 : off+1+l] {
|
||||
switch b {
|
||||
case '"', '\\':
|
||||
s = append(s, '\\', b)
|
||||
switch {
|
||||
case b == '"' || b == '\\':
|
||||
s.WriteByte('\\')
|
||||
s.WriteByte(b)
|
||||
case b < ' ' || b > '~': // unprintable
|
||||
writeEscapedByte(&s, b)
|
||||
default:
|
||||
if b < 32 || b > 127 { // unprintable
|
||||
var buf [3]byte
|
||||
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||||
s = append(s, '\\')
|
||||
for i := 0; i < 3-len(bufs); i++ {
|
||||
s = append(s, '0')
|
||||
}
|
||||
for _, r := range bufs {
|
||||
s = append(s, r)
|
||||
}
|
||||
} else {
|
||||
s = append(s, b)
|
||||
}
|
||||
s.WriteByte(b)
|
||||
}
|
||||
}
|
||||
off += 1 + l
|
||||
return string(s), off, nil
|
||||
return s.String(), off, nil
|
||||
}
|
||||
|
||||
func packString(s string, msg []byte, off int) (int, error) {
|
||||
|
||||
+1
-2
@@ -134,7 +134,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata)
|
||||
typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
|
||||
}
|
||||
|
||||
// PrivateHandleRemove removes defenitions required to support private RR type.
|
||||
// PrivateHandleRemove removes definitions required to support private RR type.
|
||||
func PrivateHandleRemove(rtype uint16) {
|
||||
rtypestr, ok := TypeToString[rtype]
|
||||
if ok {
|
||||
@@ -144,5 +144,4 @@ func PrivateHandleRemove(rtype uint16) {
|
||||
delete(StringToType, rtypestr)
|
||||
delete(typeToUnpack, rtype)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+2
-5
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
const maxTok = 2048 // Largest token we can return.
|
||||
const maxUint16 = 1<<16 - 1
|
||||
|
||||
// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
|
||||
// * Add ownernames if they are left blank;
|
||||
@@ -80,9 +79,9 @@ type lex struct {
|
||||
length int // length of the token
|
||||
err bool // when true, token text has lexer error
|
||||
value uint8 // value: zString, _BLANK, etc.
|
||||
torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
|
||||
line int // line in the file
|
||||
column int // column in the file
|
||||
torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
|
||||
comment string // any comment text seen
|
||||
}
|
||||
|
||||
@@ -209,10 +208,9 @@ func parseZone(r io.Reader, origin, f string, defttl *ttlState, t chan *Token, i
|
||||
var prevName string
|
||||
for l := range c {
|
||||
// Lexer spotted an error already
|
||||
if l.err == true {
|
||||
if l.err {
|
||||
t <- &Token{Error: &ParseError{f, l.token, l}}
|
||||
return
|
||||
|
||||
}
|
||||
switch st {
|
||||
case zExpectOwnerDir:
|
||||
@@ -639,7 +637,6 @@ func zlexer(s *scan, c chan lex) {
|
||||
if quote {
|
||||
str[stri] = x
|
||||
stri++
|
||||
break
|
||||
}
|
||||
// discard if outside of quotes
|
||||
case '\n':
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ func (s *scan) tokenText() (byte, error) {
|
||||
|
||||
// delay the newline handling until the next token is delivered,
|
||||
// fixes off-by-one errors when reporting a parse error.
|
||||
if s.eof == true {
|
||||
if s.eof {
|
||||
s.position.Line++
|
||||
s.position.Column = 0
|
||||
s.eof = false
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ServeMux is an DNS request multiplexer. It matches the zone name of
|
||||
// each incoming request against a list of registered patterns add calls
|
||||
// the handler for the pattern that most closely matches the zone name.
|
||||
//
|
||||
// ServeMux is DNSSEC aware, meaning that queries for the DS record are
|
||||
// redirected to the parent zone (if that is also registered), otherwise
|
||||
// the child gets the query.
|
||||
//
|
||||
// ServeMux is also safe for concurrent access from multiple goroutines.
|
||||
//
|
||||
// The zero ServeMux is empty and ready for use.
|
||||
type ServeMux struct {
|
||||
z map[string]Handler
|
||||
m sync.RWMutex
|
||||
}
|
||||
|
||||
// NewServeMux allocates and returns a new ServeMux.
|
||||
func NewServeMux() *ServeMux {
|
||||
return new(ServeMux)
|
||||
}
|
||||
|
||||
// DefaultServeMux is the default ServeMux used by Serve.
|
||||
var DefaultServeMux = NewServeMux()
|
||||
|
||||
func (mux *ServeMux) match(q string, t uint16) Handler {
|
||||
mux.m.RLock()
|
||||
defer mux.m.RUnlock()
|
||||
if mux.z == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var handler Handler
|
||||
|
||||
// TODO(tmthrgd): Once https://go-review.googlesource.com/c/go/+/137575
|
||||
// lands in a go release, replace the following with strings.ToLower.
|
||||
var sb strings.Builder
|
||||
for i := 0; i < len(q); i++ {
|
||||
c := q[i]
|
||||
if !(c >= 'A' && c <= 'Z') {
|
||||
continue
|
||||
}
|
||||
|
||||
sb.Grow(len(q))
|
||||
sb.WriteString(q[:i])
|
||||
|
||||
for ; i < len(q); i++ {
|
||||
c := q[i]
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
}
|
||||
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
|
||||
q = sb.String()
|
||||
break
|
||||
}
|
||||
|
||||
for off, end := 0, false; !end; off, end = NextLabel(q, off) {
|
||||
if h, ok := mux.z[q[off:]]; ok {
|
||||
if t != TypeDS {
|
||||
return h
|
||||
}
|
||||
// Continue for DS to see if we have a parent too, if so delegate to the parent
|
||||
handler = h
|
||||
}
|
||||
}
|
||||
|
||||
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
||||
if h, ok := mux.z["."]; ok {
|
||||
return h
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// Handle adds a handler to the ServeMux for pattern.
|
||||
func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
||||
if pattern == "" {
|
||||
panic("dns: invalid pattern " + pattern)
|
||||
}
|
||||
mux.m.Lock()
|
||||
if mux.z == nil {
|
||||
mux.z = make(map[string]Handler)
|
||||
}
|
||||
mux.z[Fqdn(pattern)] = handler
|
||||
mux.m.Unlock()
|
||||
}
|
||||
|
||||
// HandleFunc adds a handler function to the ServeMux for pattern.
|
||||
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||
mux.Handle(pattern, HandlerFunc(handler))
|
||||
}
|
||||
|
||||
// HandleRemove deregisters the handler specific for pattern from the ServeMux.
|
||||
func (mux *ServeMux) HandleRemove(pattern string) {
|
||||
if pattern == "" {
|
||||
panic("dns: invalid pattern " + pattern)
|
||||
}
|
||||
mux.m.Lock()
|
||||
delete(mux.z, Fqdn(pattern))
|
||||
mux.m.Unlock()
|
||||
}
|
||||
|
||||
// ServeDNS dispatches the request to the handler whose pattern most
|
||||
// closely matches the request message.
|
||||
//
|
||||
// ServeDNS is DNSSEC aware, meaning that queries for the DS record
|
||||
// are redirected to the parent zone (if that is also registered),
|
||||
// otherwise the child gets the query.
|
||||
//
|
||||
// If no handler is found, or there is no question, a standard SERVFAIL
|
||||
// message is returned
|
||||
func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {
|
||||
var h Handler
|
||||
if len(req.Question) >= 1 { // allow more than one question
|
||||
h = mux.match(req.Question[0].Name, req.Question[0].Qtype)
|
||||
}
|
||||
|
||||
if h != nil {
|
||||
h.ServeDNS(w, req)
|
||||
} else {
|
||||
HandleFailed(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle registers the handler with the given pattern
|
||||
// in the DefaultServeMux. The documentation for
|
||||
// ServeMux explains how patterns are matched.
|
||||
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
|
||||
|
||||
// HandleRemove deregisters the handle with the given pattern
|
||||
// in the DefaultServeMux.
|
||||
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
|
||||
|
||||
// HandleFunc registers the handler function with the given pattern
|
||||
// in the DefaultServeMux.
|
||||
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||
DefaultServeMux.HandleFunc(pattern, handler)
|
||||
}
|
||||
+58
-143
@@ -41,6 +41,17 @@ type Handler interface {
|
||||
ServeDNS(w ResponseWriter, r *Msg)
|
||||
}
|
||||
|
||||
// The HandlerFunc type is an adapter to allow the use of
|
||||
// ordinary functions as DNS handlers. If f is a function
|
||||
// with the appropriate signature, HandlerFunc(f) is a
|
||||
// Handler object that calls f.
|
||||
type HandlerFunc func(ResponseWriter, *Msg)
|
||||
|
||||
// ServeDNS calls f(w, r).
|
||||
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
||||
f(w, r)
|
||||
}
|
||||
|
||||
// A ResponseWriter interface is used by an DNS handler to
|
||||
// construct an DNS response.
|
||||
type ResponseWriter interface {
|
||||
@@ -72,8 +83,8 @@ type ConnectionStater interface {
|
||||
type response struct {
|
||||
msg []byte
|
||||
hijacked bool // connection has been hijacked by handler
|
||||
tsigStatus error
|
||||
tsigTimersOnly bool
|
||||
tsigStatus error
|
||||
tsigRequestMAC string
|
||||
tsigSecret map[string]string // the tsig secrets
|
||||
udp *net.UDPConn // i/o connection if UDP was used
|
||||
@@ -83,35 +94,6 @@ type response struct {
|
||||
wg *sync.WaitGroup // for gracefull shutdown
|
||||
}
|
||||
|
||||
// ServeMux is an DNS request multiplexer. It matches the
|
||||
// zone name of each incoming request against a list of
|
||||
// registered patterns add calls the handler for the pattern
|
||||
// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
|
||||
// that queries for the DS record are redirected to the parent zone (if that
|
||||
// is also registered), otherwise the child gets the query.
|
||||
// ServeMux is also safe for concurrent access from multiple goroutines.
|
||||
type ServeMux struct {
|
||||
z map[string]Handler
|
||||
m *sync.RWMutex
|
||||
}
|
||||
|
||||
// NewServeMux allocates and returns a new ServeMux.
|
||||
func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} }
|
||||
|
||||
// DefaultServeMux is the default ServeMux used by Serve.
|
||||
var DefaultServeMux = NewServeMux()
|
||||
|
||||
// The HandlerFunc type is an adapter to allow the use of
|
||||
// ordinary functions as DNS handlers. If f is a function
|
||||
// with the appropriate signature, HandlerFunc(f) is a
|
||||
// Handler object that calls f.
|
||||
type HandlerFunc func(ResponseWriter, *Msg)
|
||||
|
||||
// ServeDNS calls f(w, r).
|
||||
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
||||
f(w, r)
|
||||
}
|
||||
|
||||
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
|
||||
func HandleFailed(w ResponseWriter, r *Msg) {
|
||||
m := new(Msg)
|
||||
@@ -120,8 +102,6 @@ func HandleFailed(w ResponseWriter, r *Msg) {
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
|
||||
|
||||
// ListenAndServe Starts a server on address and network specified Invoke handler
|
||||
// for incoming queries.
|
||||
func ListenAndServe(addr string, network string, handler Handler) error {
|
||||
@@ -160,99 +140,6 @@ func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
|
||||
return server.ActivateAndServe()
|
||||
}
|
||||
|
||||
func (mux *ServeMux) match(q string, t uint16) Handler {
|
||||
mux.m.RLock()
|
||||
defer mux.m.RUnlock()
|
||||
var handler Handler
|
||||
b := make([]byte, len(q)) // worst case, one label of length q
|
||||
off := 0
|
||||
end := false
|
||||
for {
|
||||
l := len(q[off:])
|
||||
for i := 0; i < l; i++ {
|
||||
b[i] = q[off+i]
|
||||
if b[i] >= 'A' && b[i] <= 'Z' {
|
||||
b[i] |= 'a' - 'A'
|
||||
}
|
||||
}
|
||||
if h, ok := mux.z[string(b[:l])]; ok { // causes garbage, might want to change the map key
|
||||
if t != TypeDS {
|
||||
return h
|
||||
}
|
||||
// Continue for DS to see if we have a parent too, if so delegeate to the parent
|
||||
handler = h
|
||||
}
|
||||
off, end = NextLabel(q, off)
|
||||
if end {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
||||
if h, ok := mux.z["."]; ok {
|
||||
return h
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
// Handle adds a handler to the ServeMux for pattern.
|
||||
func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
||||
if pattern == "" {
|
||||
panic("dns: invalid pattern " + pattern)
|
||||
}
|
||||
mux.m.Lock()
|
||||
mux.z[Fqdn(pattern)] = handler
|
||||
mux.m.Unlock()
|
||||
}
|
||||
|
||||
// HandleFunc adds a handler function to the ServeMux for pattern.
|
||||
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||
mux.Handle(pattern, HandlerFunc(handler))
|
||||
}
|
||||
|
||||
// HandleRemove deregistrars the handler specific for pattern from the ServeMux.
|
||||
func (mux *ServeMux) HandleRemove(pattern string) {
|
||||
if pattern == "" {
|
||||
panic("dns: invalid pattern " + pattern)
|
||||
}
|
||||
mux.m.Lock()
|
||||
delete(mux.z, Fqdn(pattern))
|
||||
mux.m.Unlock()
|
||||
}
|
||||
|
||||
// ServeDNS dispatches the request to the handler whose
|
||||
// pattern most closely matches the request message. If DefaultServeMux
|
||||
// is used the correct thing for DS queries is done: a possible parent
|
||||
// is sought.
|
||||
// If no handler is found a standard SERVFAIL message is returned
|
||||
// If the request message does not have exactly one question in the
|
||||
// question section a SERVFAIL is returned, unlesss Unsafe is true.
|
||||
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
|
||||
var h Handler
|
||||
if len(request.Question) < 1 { // allow more than one question
|
||||
h = failedHandler()
|
||||
} else {
|
||||
if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
|
||||
h = failedHandler()
|
||||
}
|
||||
}
|
||||
h.ServeDNS(w, request)
|
||||
}
|
||||
|
||||
// Handle registers the handler with the given pattern
|
||||
// in the DefaultServeMux. The documentation for
|
||||
// ServeMux explains how patterns are matched.
|
||||
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
|
||||
|
||||
// HandleRemove deregisters the handle with the given pattern
|
||||
// in the DefaultServeMux.
|
||||
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
|
||||
|
||||
// HandleFunc registers the handler function with the given pattern
|
||||
// in the DefaultServeMux.
|
||||
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||
DefaultServeMux.HandleFunc(pattern, handler)
|
||||
}
|
||||
|
||||
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
||||
type Writer interface {
|
||||
io.Writer
|
||||
@@ -529,14 +416,13 @@ func (srv *Server) Shutdown() error {
|
||||
// to terminate.
|
||||
func (srv *Server) ShutdownContext(ctx context.Context) error {
|
||||
srv.lock.Lock()
|
||||
started := srv.started
|
||||
srv.started = false
|
||||
srv.lock.Unlock()
|
||||
|
||||
if !started {
|
||||
if !srv.started {
|
||||
srv.lock.Unlock()
|
||||
return &Error{err: "server not started"}
|
||||
}
|
||||
|
||||
srv.started = false
|
||||
|
||||
if srv.PacketConn != nil {
|
||||
srv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads
|
||||
}
|
||||
@@ -545,10 +431,10 @@ func (srv *Server) ShutdownContext(ctx context.Context) error {
|
||||
srv.Listener.Close()
|
||||
}
|
||||
|
||||
srv.lock.Lock()
|
||||
for rw := range srv.conns {
|
||||
rw.SetReadDeadline(aLongTimeAgo) // Unblock reads
|
||||
}
|
||||
|
||||
srv.lock.Unlock()
|
||||
|
||||
if testShutdownNotify != nil {
|
||||
@@ -735,20 +621,23 @@ func (srv *Server) serve(w *response) {
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) serveDNS(w *response) {
|
||||
req := new(Msg)
|
||||
err := req.Unpack(w.msg)
|
||||
func (srv *Server) disposeBuffer(w *response) {
|
||||
if w.udp != nil && cap(w.msg) == srv.UDPSize {
|
||||
srv.udpPool.Put(w.msg[:srv.UDPSize])
|
||||
}
|
||||
w.msg = nil
|
||||
}
|
||||
|
||||
func (srv *Server) serveDNS(w *response) {
|
||||
req := new(Msg)
|
||||
err := req.Unpack(w.msg)
|
||||
if err != nil { // Send a FormatError back
|
||||
x := new(Msg)
|
||||
x.SetRcodeFormatError(req)
|
||||
w.WriteMsg(x)
|
||||
return
|
||||
}
|
||||
if !srv.Unsafe && req.Response {
|
||||
if err != nil || !srv.Unsafe && req.Response {
|
||||
srv.disposeBuffer(w)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -765,6 +654,8 @@ func (srv *Server) serveDNS(w *response) {
|
||||
}
|
||||
}
|
||||
|
||||
srv.disposeBuffer(w)
|
||||
|
||||
handler := srv.Handler
|
||||
if handler == nil {
|
||||
handler = DefaultServeMux
|
||||
@@ -774,7 +665,16 @@ func (srv *Server) serveDNS(w *response) {
|
||||
}
|
||||
|
||||
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
// If we race with ShutdownContext, the read deadline may
|
||||
// have been set in the distant past to unblock the read
|
||||
// below. We must not override it, otherwise we may block
|
||||
// ShutdownContext.
|
||||
srv.lock.RLock()
|
||||
if srv.started {
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
|
||||
l := make([]byte, 2)
|
||||
n, err := conn.Read(l)
|
||||
if err != nil || n != 2 {
|
||||
@@ -809,7 +709,13 @@ func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
|
||||
}
|
||||
|
||||
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
srv.lock.RLock()
|
||||
if srv.started {
|
||||
// See the comment in readTCP above.
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
|
||||
m := srv.udpPool.Get().([]byte)
|
||||
n, s, err := ReadFromSessionUDP(conn, m)
|
||||
if err != nil {
|
||||
@@ -861,24 +767,33 @@ func (w *response) Write(m []byte) (int, error) {
|
||||
|
||||
n, err := io.Copy(w.tcp, bytes.NewReader(m))
|
||||
return int(n), err
|
||||
default:
|
||||
panic("dns: Write called after Close")
|
||||
}
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
// LocalAddr implements the ResponseWriter.LocalAddr method.
|
||||
func (w *response) LocalAddr() net.Addr {
|
||||
if w.tcp != nil {
|
||||
switch {
|
||||
case w.udp != nil:
|
||||
return w.udp.LocalAddr()
|
||||
case w.tcp != nil:
|
||||
return w.tcp.LocalAddr()
|
||||
default:
|
||||
panic("dns: LocalAddr called after Close")
|
||||
}
|
||||
return w.udp.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr implements the ResponseWriter.RemoteAddr method.
|
||||
func (w *response) RemoteAddr() net.Addr {
|
||||
if w.tcp != nil {
|
||||
switch {
|
||||
case w.udpSession != nil:
|
||||
return w.udpSession.RemoteAddr()
|
||||
case w.tcp != nil:
|
||||
return w.tcp.RemoteAddr()
|
||||
default:
|
||||
panic("dns: RemoteAddr called after Close")
|
||||
}
|
||||
return w.udpSession.RemoteAddr()
|
||||
}
|
||||
|
||||
// TsigStatus implements the ResponseWriter.TsigStatus method.
|
||||
|
||||
+1
-2
@@ -127,8 +127,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||
if offset+1 >= buflen {
|
||||
continue
|
||||
}
|
||||
var rdlen uint16
|
||||
rdlen = binary.BigEndian.Uint16(buf[offset:])
|
||||
rdlen := binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
offset += int(rdlen)
|
||||
}
|
||||
|
||||
+76
-74
@@ -419,128 +419,130 @@ type TXT struct {
|
||||
func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
||||
|
||||
func sprintName(s string) string {
|
||||
src := []byte(s)
|
||||
dst := make([]byte, 0, len(src))
|
||||
for i := 0; i < len(src); {
|
||||
if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
|
||||
dst = append(dst, src[i:i+2]...)
|
||||
var dst strings.Builder
|
||||
dst.Grow(len(s))
|
||||
for i := 0; i < len(s); {
|
||||
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
|
||||
dst.WriteString(s[i : i+2])
|
||||
i += 2
|
||||
} else {
|
||||
b, n := nextByte(src, i)
|
||||
if n == 0 {
|
||||
i++ // dangling back slash
|
||||
} else if b == '.' {
|
||||
dst = append(dst, b)
|
||||
} else {
|
||||
dst = appendDomainNameByte(dst, b)
|
||||
}
|
||||
i += n
|
||||
continue
|
||||
}
|
||||
|
||||
b, n := nextByte(s, i)
|
||||
switch {
|
||||
case n == 0:
|
||||
i++ // dangling back slash
|
||||
case b == '.':
|
||||
dst.WriteByte('.')
|
||||
default:
|
||||
writeDomainNameByte(&dst, b)
|
||||
}
|
||||
i += n
|
||||
}
|
||||
return string(dst)
|
||||
return dst.String()
|
||||
}
|
||||
|
||||
func sprintTxtOctet(s string) string {
|
||||
src := []byte(s)
|
||||
dst := make([]byte, 0, len(src))
|
||||
dst = append(dst, '"')
|
||||
for i := 0; i < len(src); {
|
||||
if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
|
||||
dst = append(dst, src[i:i+2]...)
|
||||
var dst strings.Builder
|
||||
dst.Grow(2 + len(s))
|
||||
dst.WriteByte('"')
|
||||
for i := 0; i < len(s); {
|
||||
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
|
||||
dst.WriteString(s[i : i+2])
|
||||
i += 2
|
||||
} else {
|
||||
b, n := nextByte(src, i)
|
||||
if n == 0 {
|
||||
i++ // dangling back slash
|
||||
} else if b == '.' {
|
||||
dst = append(dst, b)
|
||||
} else {
|
||||
if b < ' ' || b > '~' {
|
||||
dst = appendByte(dst, b)
|
||||
} else {
|
||||
dst = append(dst, b)
|
||||
}
|
||||
}
|
||||
i += n
|
||||
continue
|
||||
}
|
||||
|
||||
b, n := nextByte(s, i)
|
||||
switch {
|
||||
case n == 0:
|
||||
i++ // dangling back slash
|
||||
case b == '.':
|
||||
dst.WriteByte('.')
|
||||
case b < ' ' || b > '~':
|
||||
writeEscapedByte(&dst, b)
|
||||
default:
|
||||
dst.WriteByte(b)
|
||||
}
|
||||
i += n
|
||||
}
|
||||
dst = append(dst, '"')
|
||||
return string(dst)
|
||||
dst.WriteByte('"')
|
||||
return dst.String()
|
||||
}
|
||||
|
||||
func sprintTxt(txt []string) string {
|
||||
var out []byte
|
||||
var out strings.Builder
|
||||
for i, s := range txt {
|
||||
out.Grow(3 + len(s))
|
||||
if i > 0 {
|
||||
out = append(out, ` "`...)
|
||||
out.WriteString(` "`)
|
||||
} else {
|
||||
out = append(out, '"')
|
||||
out.WriteByte('"')
|
||||
}
|
||||
bs := []byte(s)
|
||||
for j := 0; j < len(bs); {
|
||||
b, n := nextByte(bs, j)
|
||||
for j := 0; j < len(s); {
|
||||
b, n := nextByte(s, j)
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
out = appendTXTStringByte(out, b)
|
||||
writeTXTStringByte(&out, b)
|
||||
j += n
|
||||
}
|
||||
out = append(out, '"')
|
||||
out.WriteByte('"')
|
||||
}
|
||||
return string(out)
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func appendDomainNameByte(s []byte, b byte) []byte {
|
||||
func writeDomainNameByte(s *strings.Builder, b byte) {
|
||||
switch b {
|
||||
case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
|
||||
return append(s, '\\', b)
|
||||
s.WriteByte('\\')
|
||||
s.WriteByte(b)
|
||||
default:
|
||||
writeTXTStringByte(s, b)
|
||||
}
|
||||
return appendTXTStringByte(s, b)
|
||||
}
|
||||
|
||||
func appendTXTStringByte(s []byte, b byte) []byte {
|
||||
switch b {
|
||||
case '"', '\\':
|
||||
return append(s, '\\', b)
|
||||
func writeTXTStringByte(s *strings.Builder, b byte) {
|
||||
switch {
|
||||
case b == '"' || b == '\\':
|
||||
s.WriteByte('\\')
|
||||
s.WriteByte(b)
|
||||
case b < ' ' || b > '~':
|
||||
writeEscapedByte(s, b)
|
||||
default:
|
||||
s.WriteByte(b)
|
||||
}
|
||||
if b < ' ' || b > '~' {
|
||||
return appendByte(s, b)
|
||||
}
|
||||
return append(s, b)
|
||||
}
|
||||
|
||||
func appendByte(s []byte, b byte) []byte {
|
||||
func writeEscapedByte(s *strings.Builder, b byte) {
|
||||
var buf [3]byte
|
||||
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||||
s = append(s, '\\')
|
||||
for i := 0; i < 3-len(bufs); i++ {
|
||||
s = append(s, '0')
|
||||
s.WriteByte('\\')
|
||||
for i := len(bufs); i < 3; i++ {
|
||||
s.WriteByte('0')
|
||||
}
|
||||
for _, r := range bufs {
|
||||
s = append(s, r)
|
||||
}
|
||||
return s
|
||||
s.Write(bufs)
|
||||
}
|
||||
|
||||
func nextByte(b []byte, offset int) (byte, int) {
|
||||
if offset >= len(b) {
|
||||
func nextByte(s string, offset int) (byte, int) {
|
||||
if offset >= len(s) {
|
||||
return 0, 0
|
||||
}
|
||||
if b[offset] != '\\' {
|
||||
if s[offset] != '\\' {
|
||||
// not an escape sequence
|
||||
return b[offset], 1
|
||||
return s[offset], 1
|
||||
}
|
||||
switch len(b) - offset {
|
||||
switch len(s) - offset {
|
||||
case 1: // dangling escape
|
||||
return 0, 0
|
||||
case 2, 3: // too short to be \ddd
|
||||
default: // maybe \ddd
|
||||
if isDigit(b[offset+1]) && isDigit(b[offset+2]) && isDigit(b[offset+3]) {
|
||||
return dddToByte(b[offset+1:]), 4
|
||||
if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) {
|
||||
return dddStringToByte(s[offset+1:]), 4
|
||||
}
|
||||
}
|
||||
// not \ddd, just an RFC 1035 "quoted" character
|
||||
return b[offset+1], 2
|
||||
return s[offset+1], 2
|
||||
}
|
||||
|
||||
// SPF RR. See RFC 4408, Section 3.1.1.
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build !windows
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// +build windows
|
||||
|
||||
package dns
|
||||
|
||||
import "net"
|
||||
|
||||
// SessionUDP holds the remote address
|
||||
type SessionUDP struct {
|
||||
raddr *net.UDPAddr
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||
|
||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||
// net.UDPAddr.
|
||||
// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP.
|
||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||
n, raddr, err := conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return n, nil, err
|
||||
}
|
||||
session := &SessionUDP{raddr.(*net.UDPAddr)}
|
||||
return n, session, err
|
||||
}
|
||||
|
||||
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
|
||||
// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP.
|
||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
||||
n, err := conn.WriteTo(b, session.raddr)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods
|
||||
// use the standard method in udp.go for these.
|
||||
func setUDPSocketOptions(*net.UDPConn) error { return nil }
|
||||
func parseDstFromOOB([]byte, net.IP) net.IP { return nil }
|
||||
+1
-1
@@ -3,7 +3,7 @@ package dns
|
||||
import "fmt"
|
||||
|
||||
// Version is current version of this library.
|
||||
var Version = V{1, 0, 10}
|
||||
var Version = V{1, 0, 13}
|
||||
|
||||
// V holds the version of this library.
|
||||
type V struct {
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ func CompressBlockHC(src, dst []byte, depth int) (di int, err error) {
|
||||
for ml < sn-si && src[next+ml] == src[si+ml] {
|
||||
ml++
|
||||
}
|
||||
if ml+1 < minMatch || ml <= mLen {
|
||||
if ml < minMatch || ml <= mLen {
|
||||
// Match too small (<minMath) or smaller than the current match.
|
||||
continue
|
||||
}
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
|
||||
go_import_path: github.com/prometheus/procfs
|
||||
|
||||
script:
|
||||
- make style check_license vet test staticcheck
|
||||
+14
-1
@@ -13,7 +13,11 @@
|
||||
|
||||
package util
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseUint32s parses a slice of strings into a slice of uint32s.
|
||||
func ParseUint32s(ss []string) ([]uint32, error) {
|
||||
@@ -44,3 +48,12 @@ func ParseUint64s(ss []string) ([]uint64, error) {
|
||||
|
||||
return us, nil
|
||||
}
|
||||
|
||||
// ReadUintFromFile reads a file and attempts to parse a uint64 from it.
|
||||
func ReadUintFromFile(path string) (uint64, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2018 The Prometheus Authors
|
||||
// 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.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// SysReadFile is a simplified ioutil.ReadFile that invokes syscall.Read directly.
|
||||
// https://github.com/prometheus/node_exporter/pull/728/files
|
||||
func SysReadFile(file string) (string, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// On some machines, hwmon drivers are broken and return EAGAIN. This causes
|
||||
// Go's ioutil.ReadFile implementation to poll forever.
|
||||
//
|
||||
// Since we either want to read data or bail immediately, do the simplest
|
||||
// possible read using syscall directly.
|
||||
b := make([]byte, 128)
|
||||
n, err := syscall.Read(int(f.Fd()), b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes.TrimSpace(b[:n])), nil
|
||||
}
|
||||
+5
@@ -1,3 +1,8 @@
|
||||
# 1.1.1
|
||||
This is a bug fix release.
|
||||
* fix the build break on Solaris
|
||||
* don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
|
||||
|
||||
# 1.1.0
|
||||
This new release introduces:
|
||||
* several fixes:
|
||||
|
||||
+14
-2
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -43,6 +44,9 @@ type Entry struct {
|
||||
|
||||
// When formatter is called in entry.log(), a Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
|
||||
// err may contain a field formatting error
|
||||
err string
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
@@ -80,10 +84,18 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
var field_err string
|
||||
for k, v := range fields {
|
||||
data[k] = v
|
||||
if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func {
|
||||
field_err = fmt.Sprintf("can not add field %q", k)
|
||||
if entry.err != "" {
|
||||
field_err = entry.err + ", " + field_err
|
||||
}
|
||||
} else {
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err}
|
||||
}
|
||||
|
||||
// Overrides the time of the Entry.
|
||||
|
||||
+14
-1
@@ -2,7 +2,14 @@ package logrus
|
||||
|
||||
import "time"
|
||||
|
||||
const defaultTimestampFormat = time.RFC3339
|
||||
// Default key names for the default fields
|
||||
const (
|
||||
defaultTimestampFormat = time.RFC3339
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
FieldKeyLogrusError = "logrus_error"
|
||||
)
|
||||
|
||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||
// `Entry`. It exposes all the fields, including the default ones:
|
||||
@@ -48,4 +55,10 @@ func prefixFieldClashes(data Fields, fieldMap FieldMap) {
|
||||
data["fields."+levelKey] = l
|
||||
delete(data, levelKey)
|
||||
}
|
||||
|
||||
logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
|
||||
if l, ok := data[logrusErrKey]; ok {
|
||||
data["fields."+logrusErrKey] = l
|
||||
delete(data, logrusErrKey)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -11,13 +11,6 @@ type fieldKey string
|
||||
// FieldMap allows customization of the key names for default fields.
|
||||
type FieldMap map[fieldKey]string
|
||||
|
||||
// Default key names for the default fields
|
||||
const (
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
)
|
||||
|
||||
func (f FieldMap) resolve(key fieldKey) string {
|
||||
if k, ok := f[key]; ok {
|
||||
return k
|
||||
@@ -79,6 +72,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
timestampFormat = defaultTimestampFormat
|
||||
}
|
||||
|
||||
if entry.err != "" {
|
||||
data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
|
||||
}
|
||||
if !f.DisableTimestamp {
|
||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "io"
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine,!js
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
|
||||
type Termios unix.Termios
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine,!js
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
|
||||
type Termios unix.Termios
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// +build !windows
|
||||
|
||||
package logrus
|
||||
|
||||
import "io"
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
}
|
||||
+6
-1
@@ -114,7 +114,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
fixedKeys := make([]string, 0, 3+len(entry.Data))
|
||||
fixedKeys := make([]string, 0, 4+len(entry.Data))
|
||||
if !f.DisableTimestamp {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
|
||||
}
|
||||
@@ -122,6 +122,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
if entry.Message != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
|
||||
}
|
||||
if entry.err != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
|
||||
}
|
||||
|
||||
if !f.DisableSorting {
|
||||
if f.SortingFunc == nil {
|
||||
@@ -164,6 +167,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
value = entry.Level.String()
|
||||
case f.FieldMap.resolve(FieldKeyMsg):
|
||||
value = entry.Message
|
||||
case f.FieldMap.resolve(FieldKeyLogrusError):
|
||||
value = entry.err
|
||||
default:
|
||||
value = entry.Data[key]
|
||||
}
|
||||
|
||||
+5
-2
@@ -925,13 +925,16 @@ func stripUnknownFlagValue(args []string) []string {
|
||||
}
|
||||
|
||||
first := args[0]
|
||||
if first[0] == '-' {
|
||||
if len(first) > 0 && first[0] == '-' {
|
||||
//--unknown --next-flag ...
|
||||
return args
|
||||
}
|
||||
|
||||
//--unknown arg ... (args will be arg ...)
|
||||
return args[1:]
|
||||
if len(args) > 1 {
|
||||
return args[1:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package pflag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -- stringToInt Value
|
||||
type stringToIntValue struct {
|
||||
value *map[string]int
|
||||
changed bool
|
||||
}
|
||||
|
||||
func newStringToIntValue(val map[string]int, p *map[string]int) *stringToIntValue {
|
||||
ssv := new(stringToIntValue)
|
||||
ssv.value = p
|
||||
*ssv.value = val
|
||||
return ssv
|
||||
}
|
||||
|
||||
// Format: a=1,b=2
|
||||
func (s *stringToIntValue) Set(val string) error {
|
||||
ss := strings.Split(val, ",")
|
||||
out := make(map[string]int, len(ss))
|
||||
for _, pair := range ss {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return fmt.Errorf("%s must be formatted as key=value", pair)
|
||||
}
|
||||
var err error
|
||||
out[kv[0]], err = strconv.Atoi(kv[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !s.changed {
|
||||
*s.value = out
|
||||
} else {
|
||||
for k, v := range out {
|
||||
(*s.value)[k] = v
|
||||
}
|
||||
}
|
||||
s.changed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringToIntValue) Type() string {
|
||||
return "stringToInt"
|
||||
}
|
||||
|
||||
func (s *stringToIntValue) String() string {
|
||||
var buf bytes.Buffer
|
||||
i := 0
|
||||
for k, v := range *s.value {
|
||||
if i > 0 {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
buf.WriteString(k)
|
||||
buf.WriteRune('=')
|
||||
buf.WriteString(strconv.Itoa(v))
|
||||
i++
|
||||
}
|
||||
return "[" + buf.String() + "]"
|
||||
}
|
||||
|
||||
func stringToIntConv(val string) (interface{}, error) {
|
||||
val = strings.Trim(val, "[]")
|
||||
// An empty string would cause an empty map
|
||||
if len(val) == 0 {
|
||||
return map[string]int{}, nil
|
||||
}
|
||||
ss := strings.Split(val, ",")
|
||||
out := make(map[string]int, len(ss))
|
||||
for _, pair := range ss {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return nil, fmt.Errorf("%s must be formatted as key=value", pair)
|
||||
}
|
||||
var err error
|
||||
out[kv[0]], err = strconv.Atoi(kv[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetStringToInt return the map[string]int value of a flag with the given name
|
||||
func (f *FlagSet) GetStringToInt(name string) (map[string]int, error) {
|
||||
val, err := f.getFlagType(name, "stringToInt", stringToIntConv)
|
||||
if err != nil {
|
||||
return map[string]int{}, err
|
||||
}
|
||||
return val.(map[string]int), nil
|
||||
}
|
||||
|
||||
// StringToIntVar defines a string flag with specified name, default value, and usage string.
|
||||
// The argument p points to a map[string]int variable in which to store the values of the multiple flags.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func (f *FlagSet) StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) {
|
||||
f.VarP(newStringToIntValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) {
|
||||
f.VarP(newStringToIntValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// StringToIntVar defines a string flag with specified name, default value, and usage string.
|
||||
// The argument p points to a map[string]int variable in which to store the value of the flag.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) {
|
||||
CommandLine.VarP(newStringToIntValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) {
|
||||
CommandLine.VarP(newStringToIntValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// StringToInt defines a string flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a map[string]int variable that stores the value of the flag.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func (f *FlagSet) StringToInt(name string, value map[string]int, usage string) *map[string]int {
|
||||
p := map[string]int{}
|
||||
f.StringToIntVarP(&p, name, "", value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int {
|
||||
p := map[string]int{}
|
||||
f.StringToIntVarP(&p, name, shorthand, value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// StringToInt defines a string flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a map[string]int variable that stores the value of the flag.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func StringToInt(name string, value map[string]int, usage string) *map[string]int {
|
||||
return CommandLine.StringToIntP(name, "", value, usage)
|
||||
}
|
||||
|
||||
// StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash.
|
||||
func StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int {
|
||||
return CommandLine.StringToIntP(name, shorthand, value, usage)
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package pflag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -- stringToString Value
|
||||
type stringToStringValue struct {
|
||||
value *map[string]string
|
||||
changed bool
|
||||
}
|
||||
|
||||
func newStringToStringValue(val map[string]string, p *map[string]string) *stringToStringValue {
|
||||
ssv := new(stringToStringValue)
|
||||
ssv.value = p
|
||||
*ssv.value = val
|
||||
return ssv
|
||||
}
|
||||
|
||||
// Format: a=1,b=2
|
||||
func (s *stringToStringValue) Set(val string) error {
|
||||
var ss []string
|
||||
n := strings.Count(val, "=")
|
||||
switch n {
|
||||
case 0:
|
||||
return fmt.Errorf("%s must be formatted as key=value", val)
|
||||
case 1:
|
||||
ss = append(ss, strings.Trim(val, `"`))
|
||||
default:
|
||||
r := csv.NewReader(strings.NewReader(val))
|
||||
var err error
|
||||
ss, err = r.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
out := make(map[string]string, len(ss))
|
||||
for _, pair := range ss {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return fmt.Errorf("%s must be formatted as key=value", pair)
|
||||
}
|
||||
out[kv[0]] = kv[1]
|
||||
}
|
||||
if !s.changed {
|
||||
*s.value = out
|
||||
} else {
|
||||
for k, v := range out {
|
||||
(*s.value)[k] = v
|
||||
}
|
||||
}
|
||||
s.changed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringToStringValue) Type() string {
|
||||
return "stringToString"
|
||||
}
|
||||
|
||||
func (s *stringToStringValue) String() string {
|
||||
records := make([]string, 0, len(*s.value)>>1)
|
||||
for k, v := range *s.value {
|
||||
records = append(records, k+"="+v)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := csv.NewWriter(&buf)
|
||||
if err := w.Write(records); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
w.Flush()
|
||||
return "[" + strings.TrimSpace(buf.String()) + "]"
|
||||
}
|
||||
|
||||
func stringToStringConv(val string) (interface{}, error) {
|
||||
val = strings.Trim(val, "[]")
|
||||
// An empty string would cause an empty map
|
||||
if len(val) == 0 {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
r := csv.NewReader(strings.NewReader(val))
|
||||
ss, err := r.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]string, len(ss))
|
||||
for _, pair := range ss {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return nil, fmt.Errorf("%s must be formatted as key=value", pair)
|
||||
}
|
||||
out[kv[0]] = kv[1]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetStringToString return the map[string]string value of a flag with the given name
|
||||
func (f *FlagSet) GetStringToString(name string) (map[string]string, error) {
|
||||
val, err := f.getFlagType(name, "stringToString", stringToStringConv)
|
||||
if err != nil {
|
||||
return map[string]string{}, err
|
||||
}
|
||||
return val.(map[string]string), nil
|
||||
}
|
||||
|
||||
// StringToStringVar defines a string flag with specified name, default value, and usage string.
|
||||
// The argument p points to a map[string]string variable in which to store the values of the multiple flags.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func (f *FlagSet) StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) {
|
||||
f.VarP(newStringToStringValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) {
|
||||
f.VarP(newStringToStringValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// StringToStringVar defines a string flag with specified name, default value, and usage string.
|
||||
// The argument p points to a map[string]string variable in which to store the value of the flag.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) {
|
||||
CommandLine.VarP(newStringToStringValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) {
|
||||
CommandLine.VarP(newStringToStringValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// StringToString defines a string flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a map[string]string variable that stores the value of the flag.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func (f *FlagSet) StringToString(name string, value map[string]string, usage string) *map[string]string {
|
||||
p := map[string]string{}
|
||||
f.StringToStringVarP(&p, name, "", value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string {
|
||||
p := map[string]string{}
|
||||
f.StringToStringVarP(&p, name, shorthand, value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// StringToString defines a string flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a map[string]string variable that stores the value of the flag.
|
||||
// The value of each argument will not try to be separated by comma
|
||||
func StringToString(name string, value map[string]string, usage string) *map[string]string {
|
||||
return CommandLine.StringToStringP(name, "", value, usage)
|
||||
}
|
||||
|
||||
// StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash.
|
||||
func StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string {
|
||||
return CommandLine.StringToStringP(name, shorthand, value, usage)
|
||||
}
|
||||
+3
@@ -19,3 +19,6 @@ Anfernee Yongkun Gui <agui@vmware.com> <anfernee.gui@gmail.com>
|
||||
Anfernee Yongkun Gui <agui@vmware.com> Yongkun Anfernee Gui <agui@vmware.com>
|
||||
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
|
||||
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
|
||||
Jiatong Wang <wjiatong@vmware.com> jiatongw <wjiatong@vmware.com>
|
||||
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <u.bessle.extern@eos-ts.com>
|
||||
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <uwe.bessle@web.de>
|
||||
|
||||
+7
-1
@@ -1,6 +1,12 @@
|
||||
# changelog
|
||||
|
||||
### unreleased
|
||||
### 0.19.0 (2018-09-30)
|
||||
|
||||
* New vapi/rest and and vapi/tags packages
|
||||
|
||||
* Allowing the use of STS for exchanging tokens
|
||||
|
||||
* Add object.VirtualMachine.UUID method
|
||||
|
||||
* SetRootCAs on the soap.Client returns an error for invalid certificates
|
||||
|
||||
|
||||
+13
@@ -29,6 +29,7 @@ Cédric Blomart <cblomart@gmail.com>
|
||||
Chris Marchesi <chrism@vancluevertech.com>
|
||||
Christian Höltje <docwhat@gerf.org>
|
||||
Clint Greenwood <cgreenwood@vmware.com>
|
||||
CuiHaozhi <cuihaozhi@chinacloud.com.cn>
|
||||
Danny Lockard <danny.lockard@banno.com>
|
||||
Dave Tucker <dave@dtucker.co.uk>
|
||||
Davide Agnello <dagnello@hp.com>
|
||||
@@ -42,6 +43,7 @@ Erik Hollensbe <github@hollensbe.org>
|
||||
Fabio Rapposelli <fabio@vmware.com>
|
||||
Faiyaz Ahmed <ahmedf@vmware.com>
|
||||
forkbomber <forkbomber@users.noreply.github.com>
|
||||
freebsdly <qinhuajun@outlook.com>
|
||||
Gavin Gray <gavin@infinio.com>
|
||||
Gavrie Philipson <gavrie.philipson@elastifile.com>
|
||||
George Hicken <ghicken@vmware.com>
|
||||
@@ -54,17 +56,27 @@ Ivan Porto Carrero <icarrero@vmware.com>
|
||||
Jason Kincl <jkincl@gmail.com>
|
||||
Jeremy Canady <jcanady@jackhenry.com>
|
||||
jeremy-clerc <jeremy@clerc.io>
|
||||
Jiatong Wang <wjiatong@vmware.com>
|
||||
João Pereira <joaodrp@gmail.com>
|
||||
Jorge Sevilla <jorge.sevilla@rstor.io>
|
||||
kayrus <kay.diam@gmail.com>
|
||||
Kevin George <georgek@vmware.com>
|
||||
leslie-qiwa <leslie.qiwa@gmail.com>
|
||||
Louie Jiang <jiangl@vmware.com>
|
||||
Marc Carmier <mcarmier@gmail.com>
|
||||
Maria Ntalla <maria.ntalla@gmail.com>
|
||||
Marin Atanasov Nikolov <mnikolov@vmware.com>
|
||||
Matthew Cosgrove <matthew.cosgrove@dell.com>
|
||||
Matt Moriarity <matt@mattmoriarity.com>
|
||||
Mevan Samaratunga <mevansam@gmail.com>
|
||||
Michal Jankowski <mjankowski@vmware.com>
|
||||
mingwei <mingwei@smartx.com>
|
||||
Nicolas Lamirault <nicolas.lamirault@gmail.com>
|
||||
Omar Kohl <omarkohl@gmail.com>
|
||||
Parham Alvani <parham.alvani@gmail.com>
|
||||
Pieter Noordhuis <pnoordhuis@vmware.com>
|
||||
prydin <prydin@vmware.com>
|
||||
Rowan Jacobs <rojacobs@pivotal.io>
|
||||
runner.mei <runner.mei@gmail.com>
|
||||
S.Çağlar Onur <conur@vmware.com>
|
||||
Sergey Ignatov <sergey.ignatov@jetbrains.com>
|
||||
@@ -74,6 +86,7 @@ tanishi <tanishi503@gmail.com>
|
||||
Ted Zlatanov <tzz@lifelogs.com>
|
||||
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
|
||||
Trevor Dawe <trevor.dawe@gmail.com>
|
||||
Uwe Bessle <Uwe.Bessle@iteratec.de>
|
||||
Vadim Egorov <vegorov@vmware.com>
|
||||
Volodymyr Bobyr <pupsua@gmail.com>
|
||||
Witold Krecicki <wpk@culm.net>
|
||||
|
||||
+2
@@ -75,6 +75,8 @@ Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
||||
|
||||
* [Libretto](https://github.com/apcera/libretto/tree/master/virtualmachine/vsphere)
|
||||
|
||||
* [Telegraf](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/vsphere)
|
||||
|
||||
## Related projects
|
||||
|
||||
* [rbvmomi](https://github.com/vmware/rbvmomi)
|
||||
|
||||
+1
-2
@@ -114,10 +114,9 @@ func (k *keepAlive) RoundTrip(ctx context.Context, req, res soap.HasFault) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start ticker on login, stop ticker on logout.
|
||||
switch req.(type) {
|
||||
case *methods.LoginBody, *methods.LoginExtensionByCertificateBody:
|
||||
case *methods.LoginBody, *methods.LoginExtensionByCertificateBody, *methods.LoginByTokenBody:
|
||||
k.start()
|
||||
case *methods.LogoutBody:
|
||||
k.stop()
|
||||
|
||||
+1
-1
@@ -404,7 +404,7 @@ userAuthLoop:
|
||||
perms, authErr = config.PasswordCallback(s, password)
|
||||
case "keyboard-interactive":
|
||||
if config.KeyboardInteractiveCallback == nil {
|
||||
authErr = errors.New("ssh: keyboard-interactive auth not configubred")
|
||||
authErr = errors.New("ssh: keyboard-interactive auth not configured")
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+14
-10
@@ -38,6 +38,7 @@ const (
|
||||
type JumpTest uint16
|
||||
|
||||
// Supported operators for conditional jumps.
|
||||
// K can be RegX for JumpIfX
|
||||
const (
|
||||
// K == A
|
||||
JumpEqual JumpTest = iota
|
||||
@@ -134,12 +135,9 @@ const (
|
||||
opMaskLoadDest = 0x01
|
||||
opMaskLoadWidth = 0x18
|
||||
opMaskLoadMode = 0xe0
|
||||
// opClsALU
|
||||
opMaskOperandSrc = 0x08
|
||||
opMaskOperator = 0xf0
|
||||
// opClsJump
|
||||
opMaskJumpConst = 0x0f
|
||||
opMaskJumpCond = 0xf0
|
||||
// opClsALU & opClsJump
|
||||
opMaskOperand = 0x08
|
||||
opMaskOperator = 0xf0
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -192,15 +190,21 @@ const (
|
||||
opLoadWidth1
|
||||
)
|
||||
|
||||
// Operator defined by ALUOp*
|
||||
// Operand for ALU and Jump instructions
|
||||
type opOperand uint16
|
||||
|
||||
// Supported operand sources.
|
||||
const (
|
||||
opALUSrcConstant uint16 = iota << 3
|
||||
opALUSrcX
|
||||
opOperandConstant opOperand = iota << 3
|
||||
opOperandX
|
||||
)
|
||||
|
||||
// An jumpOp is a conditional jump condition.
|
||||
type jumpOp uint16
|
||||
|
||||
// Supported jump conditions.
|
||||
const (
|
||||
opJumpAlways = iota << 4
|
||||
opJumpAlways jumpOp = iota << 4
|
||||
opJumpEqual
|
||||
opJumpGT
|
||||
opJumpGE
|
||||
|
||||
+108
-86
@@ -89,10 +89,14 @@ func (ri RawInstruction) Disassemble() Instruction {
|
||||
case opClsALU:
|
||||
switch op := ALUOp(ri.Op & opMaskOperator); op {
|
||||
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
|
||||
if ri.Op&opMaskOperandSrc != 0 {
|
||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
||||
case opOperandX:
|
||||
return ALUOpX{Op: op}
|
||||
case opOperandConstant:
|
||||
return ALUOpConstant{Op: op, Val: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
return ALUOpConstant{Op: op, Val: ri.K}
|
||||
case aluOpNeg:
|
||||
return NegateA{}
|
||||
default:
|
||||
@@ -100,63 +104,18 @@ func (ri RawInstruction) Disassemble() Instruction {
|
||||
}
|
||||
|
||||
case opClsJump:
|
||||
if ri.Op&opMaskJumpConst != opClsJump {
|
||||
return ri
|
||||
}
|
||||
switch ri.Op & opMaskJumpCond {
|
||||
switch op := jumpOp(ri.Op & opMaskOperator); op {
|
||||
case opJumpAlways:
|
||||
return Jump{Skip: ri.K}
|
||||
case opJumpEqual:
|
||||
if ri.Jt == 0 {
|
||||
return JumpIf{
|
||||
Cond: JumpNotEqual,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jf,
|
||||
SkipFalse: 0,
|
||||
}
|
||||
}
|
||||
return JumpIf{
|
||||
Cond: JumpEqual,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jt,
|
||||
SkipFalse: ri.Jf,
|
||||
}
|
||||
case opJumpGT:
|
||||
if ri.Jt == 0 {
|
||||
return JumpIf{
|
||||
Cond: JumpLessOrEqual,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jf,
|
||||
SkipFalse: 0,
|
||||
}
|
||||
}
|
||||
return JumpIf{
|
||||
Cond: JumpGreaterThan,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jt,
|
||||
SkipFalse: ri.Jf,
|
||||
}
|
||||
case opJumpGE:
|
||||
if ri.Jt == 0 {
|
||||
return JumpIf{
|
||||
Cond: JumpLessThan,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jf,
|
||||
SkipFalse: 0,
|
||||
}
|
||||
}
|
||||
return JumpIf{
|
||||
Cond: JumpGreaterOrEqual,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jt,
|
||||
SkipFalse: ri.Jf,
|
||||
}
|
||||
case opJumpSet:
|
||||
return JumpIf{
|
||||
Cond: JumpBitsSet,
|
||||
Val: ri.K,
|
||||
SkipTrue: ri.Jt,
|
||||
SkipFalse: ri.Jf,
|
||||
case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
|
||||
cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
|
||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
||||
case opOperandX:
|
||||
return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
||||
case opOperandConstant:
|
||||
return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
default:
|
||||
return ri
|
||||
@@ -187,6 +146,41 @@ func (ri RawInstruction) Disassemble() Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
|
||||
var test JumpTest
|
||||
|
||||
// Decode "fake" jump conditions that don't appear in machine code
|
||||
// Ensures the Assemble -> Disassemble stage recreates the same instructions
|
||||
// See https://github.com/golang/go/issues/18470
|
||||
if skipTrue == 0 {
|
||||
switch op {
|
||||
case opJumpEqual:
|
||||
test = JumpNotEqual
|
||||
case opJumpGT:
|
||||
test = JumpLessOrEqual
|
||||
case opJumpGE:
|
||||
test = JumpLessThan
|
||||
case opJumpSet:
|
||||
test = JumpBitsNotSet
|
||||
}
|
||||
|
||||
return test, skipFalse, 0
|
||||
}
|
||||
|
||||
switch op {
|
||||
case opJumpEqual:
|
||||
test = JumpEqual
|
||||
case opJumpGT:
|
||||
test = JumpGreaterThan
|
||||
case opJumpGE:
|
||||
test = JumpGreaterOrEqual
|
||||
case opJumpSet:
|
||||
test = JumpBitsSet
|
||||
}
|
||||
|
||||
return test, skipTrue, skipFalse
|
||||
}
|
||||
|
||||
// LoadConstant loads Val into register Dst.
|
||||
type LoadConstant struct {
|
||||
Dst Register
|
||||
@@ -413,7 +407,7 @@ type ALUOpConstant struct {
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | opALUSrcConstant | uint16(a.Op),
|
||||
Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
@@ -454,7 +448,7 @@ type ALUOpX struct {
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | opALUSrcX | uint16(a.Op),
|
||||
Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -509,7 +503,7 @@ type Jump struct {
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a Jump) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsJump | opJumpAlways,
|
||||
Op: opClsJump | uint16(opJumpAlways),
|
||||
K: a.Skip,
|
||||
}, nil
|
||||
}
|
||||
@@ -530,11 +524,39 @@ type JumpIf struct {
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIf) Assemble() (RawInstruction, error) {
|
||||
return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIf) String() string {
|
||||
return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// JumpIfX skips the following Skip instructions in the program if A
|
||||
// <Cond> X is true.
|
||||
type JumpIfX struct {
|
||||
Cond JumpTest
|
||||
SkipTrue uint8
|
||||
SkipFalse uint8
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIfX) Assemble() (RawInstruction, error) {
|
||||
return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIfX) String() string {
|
||||
return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// jumpToRaw assembles a jump instruction into a RawInstruction
|
||||
func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
|
||||
var (
|
||||
cond uint16
|
||||
cond jumpOp
|
||||
flip bool
|
||||
)
|
||||
switch a.Cond {
|
||||
switch test {
|
||||
case JumpEqual:
|
||||
cond = opJumpEqual
|
||||
case JumpNotEqual:
|
||||
@@ -552,63 +574,63 @@ func (a JumpIf) Assemble() (RawInstruction, error) {
|
||||
case JumpBitsNotSet:
|
||||
cond, flip = opJumpSet, true
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", a.Cond)
|
||||
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
|
||||
}
|
||||
jt, jf := a.SkipTrue, a.SkipFalse
|
||||
jt, jf := skipTrue, skipFalse
|
||||
if flip {
|
||||
jt, jf = jf, jt
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: opClsJump | cond,
|
||||
Op: opClsJump | uint16(cond) | uint16(operand),
|
||||
Jt: jt,
|
||||
Jf: jf,
|
||||
K: a.Val,
|
||||
K: k,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIf) String() string {
|
||||
switch a.Cond {
|
||||
// jumpToString converts a jump instruction to assembler notation
|
||||
func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
|
||||
switch cond {
|
||||
// K == A
|
||||
case JumpEqual:
|
||||
return conditionalJump(a, "jeq", "jneq")
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
|
||||
// K != A
|
||||
case JumpNotEqual:
|
||||
return fmt.Sprintf("jneq #%d,%d", a.Val, a.SkipTrue)
|
||||
return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
|
||||
// K > A
|
||||
case JumpGreaterThan:
|
||||
return conditionalJump(a, "jgt", "jle")
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
|
||||
// K < A
|
||||
case JumpLessThan:
|
||||
return fmt.Sprintf("jlt #%d,%d", a.Val, a.SkipTrue)
|
||||
return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
|
||||
// K >= A
|
||||
case JumpGreaterOrEqual:
|
||||
return conditionalJump(a, "jge", "jlt")
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
|
||||
// K <= A
|
||||
case JumpLessOrEqual:
|
||||
return fmt.Sprintf("jle #%d,%d", a.Val, a.SkipTrue)
|
||||
return fmt.Sprintf("jle %s,%d", operand, skipTrue)
|
||||
// K & A != 0
|
||||
case JumpBitsSet:
|
||||
if a.SkipFalse > 0 {
|
||||
return fmt.Sprintf("jset #%d,%d,%d", a.Val, a.SkipTrue, a.SkipFalse)
|
||||
if skipFalse > 0 {
|
||||
return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
|
||||
}
|
||||
return fmt.Sprintf("jset #%d,%d", a.Val, a.SkipTrue)
|
||||
return fmt.Sprintf("jset %s,%d", operand, skipTrue)
|
||||
// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
|
||||
case JumpBitsNotSet:
|
||||
return JumpIf{Cond: JumpBitsSet, SkipTrue: a.SkipFalse, SkipFalse: a.SkipTrue, Val: a.Val}.String()
|
||||
return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
return fmt.Sprintf("unknown JumpTest %#v", cond)
|
||||
}
|
||||
}
|
||||
|
||||
func conditionalJump(inst JumpIf, positiveJump, negativeJump string) string {
|
||||
if inst.SkipTrue > 0 {
|
||||
if inst.SkipFalse > 0 {
|
||||
return fmt.Sprintf("%s #%d,%d,%d", positiveJump, inst.Val, inst.SkipTrue, inst.SkipFalse)
|
||||
func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
|
||||
if skipTrue > 0 {
|
||||
if skipFalse > 0 {
|
||||
return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
|
||||
}
|
||||
return fmt.Sprintf("%s #%d,%d", positiveJump, inst.Val, inst.SkipTrue)
|
||||
return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
|
||||
}
|
||||
return fmt.Sprintf("%s #%d,%d", negativeJump, inst.Val, inst.SkipFalse)
|
||||
return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
|
||||
}
|
||||
|
||||
// RetA exits the BPF program, returning the value of register A.
|
||||
|
||||
+10
@@ -35,6 +35,13 @@ func NewVM(filter []Instruction) (*VM, error) {
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
case JumpIfX:
|
||||
if check <= int(ins.SkipTrue) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
||||
}
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
// Check for division or modulus by zero
|
||||
case ALUOpConstant:
|
||||
if ins.Val != 0 {
|
||||
@@ -109,6 +116,9 @@ func (v *VM) Run(in []byte) (int, error) {
|
||||
case JumpIf:
|
||||
jump := jumpIf(ins, regA)
|
||||
i += jump
|
||||
case JumpIfX:
|
||||
jump := jumpIfX(ins, regA, regX)
|
||||
i += jump
|
||||
case LoadAbsolute:
|
||||
regA, ok = loadAbsolute(ins, in)
|
||||
case LoadConstant:
|
||||
|
||||
+21
-14
@@ -55,34 +55,41 @@ func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
func jumpIf(ins JumpIf, value uint32) int {
|
||||
var ok bool
|
||||
inV := uint32(ins.Val)
|
||||
func jumpIf(ins JumpIf, regA uint32) int {
|
||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val)
|
||||
}
|
||||
|
||||
switch ins.Cond {
|
||||
func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int {
|
||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX)
|
||||
}
|
||||
|
||||
func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int {
|
||||
var ok bool
|
||||
|
||||
switch cond {
|
||||
case JumpEqual:
|
||||
ok = value == inV
|
||||
ok = regA == value
|
||||
case JumpNotEqual:
|
||||
ok = value != inV
|
||||
ok = regA != value
|
||||
case JumpGreaterThan:
|
||||
ok = value > inV
|
||||
ok = regA > value
|
||||
case JumpLessThan:
|
||||
ok = value < inV
|
||||
ok = regA < value
|
||||
case JumpGreaterOrEqual:
|
||||
ok = value >= inV
|
||||
ok = regA >= value
|
||||
case JumpLessOrEqual:
|
||||
ok = value <= inV
|
||||
ok = regA <= value
|
||||
case JumpBitsSet:
|
||||
ok = (value & inV) != 0
|
||||
ok = (regA & value) != 0
|
||||
case JumpBitsNotSet:
|
||||
ok = (value & inV) == 0
|
||||
ok = (regA & value) == 0
|
||||
}
|
||||
|
||||
if ok {
|
||||
return int(ins.SkipTrue)
|
||||
return int(skipTrue)
|
||||
}
|
||||
|
||||
return int(ins.SkipFalse)
|
||||
return int(skipFalse)
|
||||
}
|
||||
|
||||
func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {
|
||||
|
||||
+2
@@ -193,6 +193,7 @@ struct ltchars {
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/memfd.h>
|
||||
@@ -445,6 +446,7 @@ ccflags="$@"
|
||||
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
|
||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
|
||||
$2 ~ /^KEXEC_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
|
||||
$2 !~ "NLA_TYPE_MASK" &&
|
||||
|
||||
+5
@@ -92,6 +92,11 @@ while(<>) {
|
||||
my @in = parseparamlist($in);
|
||||
my @out = parseparamlist($out);
|
||||
|
||||
# Try in vain to keep people from editing this file.
|
||||
# The theory is that they jump into the middle of the file
|
||||
# without reading the header.
|
||||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
|
||||
|
||||
# So file name.
|
||||
if($modname eq "") {
|
||||
$modname = "libc";
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ my @headers = qw (
|
||||
sys/sem.h
|
||||
sys/shm.h
|
||||
sys/vmmeter.h
|
||||
uvm/uvmexp.h
|
||||
uvm/uvm_param.h
|
||||
uvm/uvm_swap_encrypt.h
|
||||
ddb/db_var.h
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import "unsafe"
|
||||
|
||||
// Round the length of a raw sockaddr up to align it properly.
|
||||
func cmsgAlignOf(salen int) int {
|
||||
salign := sizeofPtr
|
||||
salign := SizeofPtr
|
||||
// NOTE: It seems like 64-bit Darwin, DragonFly BSD and
|
||||
// Solaris kernels still require 32-bit aligned access to
|
||||
// network subsystem.
|
||||
|
||||
+13
-13
@@ -1122,7 +1122,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
|
||||
// The ptrace syscall differs from glibc's ptrace.
|
||||
// Peeks returns the word in *data, not as the return value.
|
||||
|
||||
var buf [sizeofPtr]byte
|
||||
var buf [SizeofPtr]byte
|
||||
|
||||
// Leading edge. PEEKTEXT/PEEKDATA don't require aligned
|
||||
// access (PEEKUSER warns that it might), but if we don't
|
||||
@@ -1130,12 +1130,12 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
|
||||
// boundary and not get the bytes leading up to the page
|
||||
// boundary.
|
||||
n := 0
|
||||
if addr%sizeofPtr != 0 {
|
||||
err = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
|
||||
if addr%SizeofPtr != 0 {
|
||||
err = ptrace(req, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n += copy(out, buf[addr%sizeofPtr:])
|
||||
n += copy(out, buf[addr%SizeofPtr:])
|
||||
out = out[n:]
|
||||
}
|
||||
|
||||
@@ -1173,15 +1173,15 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
|
||||
|
||||
// Leading edge.
|
||||
n := 0
|
||||
if addr%sizeofPtr != 0 {
|
||||
var buf [sizeofPtr]byte
|
||||
err = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
|
||||
if addr%SizeofPtr != 0 {
|
||||
var buf [SizeofPtr]byte
|
||||
err = ptrace(peekReq, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n += copy(buf[addr%sizeofPtr:], data)
|
||||
n += copy(buf[addr%SizeofPtr:], data)
|
||||
word := *((*uintptr)(unsafe.Pointer(&buf[0])))
|
||||
err = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word)
|
||||
err = ptrace(pokeReq, pid, addr-addr%SizeofPtr, word)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -1189,19 +1189,19 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
|
||||
}
|
||||
|
||||
// Interior.
|
||||
for len(data) > sizeofPtr {
|
||||
for len(data) > SizeofPtr {
|
||||
word := *((*uintptr)(unsafe.Pointer(&data[0])))
|
||||
err = ptrace(pokeReq, pid, addr+uintptr(n), word)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += sizeofPtr
|
||||
data = data[sizeofPtr:]
|
||||
n += SizeofPtr
|
||||
data = data[SizeofPtr:]
|
||||
}
|
||||
|
||||
// Trailing edge.
|
||||
if len(data) > 0 {
|
||||
var buf [sizeofPtr]byte
|
||||
var buf [SizeofPtr]byte
|
||||
err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
|
||||
if err != nil {
|
||||
return n, err
|
||||
|
||||
+13
@@ -160,3 +160,16 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
|
||||
}
|
||||
return poll(&fds[0], len(fds), timeout)
|
||||
}
|
||||
|
||||
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error)
|
||||
|
||||
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error {
|
||||
cmdlineLen := len(cmdline)
|
||||
if cmdlineLen > 0 {
|
||||
// Account for the additional NULL byte added by
|
||||
// BytePtrFromString in kexecFileLoad. The kexec_file_load
|
||||
// syscall expects a NULL-terminated string.
|
||||
cmdlineLen++
|
||||
}
|
||||
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
|
||||
}
|
||||
|
||||
+13
@@ -136,3 +136,16 @@ func SyncFileRange(fd int, off int64, n int64, flags int) error {
|
||||
// order of their arguments.
|
||||
return syncFileRange2(fd, flags, off, n)
|
||||
}
|
||||
|
||||
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error)
|
||||
|
||||
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error {
|
||||
cmdlineLen := len(cmdline)
|
||||
if cmdlineLen > 0 {
|
||||
// Account for the additional NULL byte added by
|
||||
// BytePtrFromString in kexecFileLoad. The kexec_file_load
|
||||
// syscall expects a NULL-terminated string.
|
||||
cmdlineLen++
|
||||
}
|
||||
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user