支持腾讯云资源导入

This commit is contained in:
屈轩
2018-11-03 17:32:54 +08:00
parent 888b33998d
commit 9833767fb2
162 changed files with 8751 additions and 884 deletions
+8
View File
@@ -110,6 +110,14 @@
name = "github.com/360EntSecGroup-Skylar/excelize"
version = "v1.3.0"
[[constraint]]
name = "github.com/tencentcloud/tencentcloud-sdk-go"
version = "=v3.0.0"
[[constraint]]
name = "github.com/nelsonken/cos-go-sdk-v5"
version = "=v1.2.0"
[[constraint]]
branch = "master"
name = "github.com/tredoe/osutil"
+5
View File
@@ -37,6 +37,7 @@ func init() {
PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud"`
AccessURL string `helo:"hello" metavar:"Azure choices: <AzureGermanCloud、AzureChinaCloud、AzureUSGovernmentCloud、AzurePublicCloud>"`
Desc string `help:"Description"`
Enabled bool `help:"Enabled the account automatically"`
Import bool `help:"Import all sub account automatically"`
AutoSync bool `help:"Enabled the account automatically"`
@@ -49,6 +50,10 @@ func init() {
params.Add(jsonutils.NewString(args.SECRET), "secret")
params.Add(jsonutils.NewString(args.PROVIDER), "provider")
if args.Enabled {
params.Add(jsonutils.JSONTrue, "enabled")
}
if args.Import {
params.Add(jsonutils.JSONTrue, "import")
if args.AutoSync {
+127
View File
@@ -0,0 +1,127 @@
package main
import (
"fmt"
"os"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
"yunion.io/x/structarg"
_ "yunion.io/x/onecloud/pkg/util/qcloud/shell"
)
type BaseOptions struct {
Help bool `help:"Show help"`
AppID string `help:"AppID" default:"$QCLOUD_APPID"`
SecretID string `help:"Secret" default:"$QCLOUD_SECRET_ID"`
SecretKey string `help:"Access key" default:"$QCLOUD_SECRET_KEY"`
RegionId string `help:"RegionId" default:"$QCLOUD_REGION_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)
}
}
}
}
+343
View File
@@ -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
}
+1
View File
@@ -31,6 +31,7 @@ const (
CLOUD_PROVIDER_VMWARE = "VMware"
CLOUD_PROVIDER_ALIYUN = "Aliyun"
CLOUD_PROVIDER_QCLOUD = "Qcloud"
CLOUD_PROVIDER_AZURE = "Azure"
)
+4 -1
View File
@@ -123,6 +123,7 @@ const (
HYPERVISOR_ESXI = "esxi"
HYPERVISOR_HYPERV = "hyperv"
HYPERVISOR_ALIYUN = "aliyun"
HYPERVISOR_QCLOUD = "qcloud"
HYPERVISOR_AZURE = "azure"
// HYPERVISOR_DEFAULT = HYPERVISOR_KVM
@@ -132,7 +133,7 @@ const (
var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_SNAPSHOT_STREAM}
var VM_CREATING_STATUS = []string{VM_CREATE_NETWORK, VM_CREATE_DISK, VM_START_DEPLOY, VM_DEPLOYING}
var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_BAREMETAL, HYPERVISOR_ESXI, HYPERVISOR_CONTAINER, HYPERVISOR_ALIYUN, HYPERVISOR_AZURE}
var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_BAREMETAL, HYPERVISOR_ESXI, HYPERVISOR_CONTAINER, HYPERVISOR_ALIYUN, HYPERVISOR_AZURE, HYPERVISOR_QCLOUD}
// var 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 {
+2 -1
View File
@@ -43,6 +43,7 @@ const (
HOST_TYPE_HYPERV = "hyperv" // # Microsoft Hyper-V
HOST_TYPE_XEN = "xen" // # XenServer
HOST_TYPE_ALIYUN = "aliyun"
HOST_TYPE_QCLOUD = "qcloud"
HOST_TYPE_AZURE = "azure"
HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR
@@ -81,7 +82,7 @@ const (
HOST_STATUS_UNKNOWN = BAREMETAL_UNKNOWN
)
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE}
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE, HOST_TYPE_QCLOUD}
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
type SHostManager struct {
+6 -5
View File
@@ -25,11 +25,12 @@ const (
MANUAL = "manual"
AUTO = "auto"
SNAPSHOT_CREATING = "creating"
SNAPSHOT_FAILED = "create_failed"
SNAPSHOT_READY = "ready"
SNAPSHOT_DELETING = "deleting"
SNAPSHOT_UNKNOWN = "unknown"
SNAPSHOT_CREATING = "creating"
SNAPSHOT_ROLLBACKING = "rollbacking"
SNAPSHOT_FAILED = "create_failed"
SNAPSHOT_READY = "ready"
SNAPSHOT_DELETING = "deleting"
SNAPSHOT_UNKNOWN = "unknown"
)
type SSnapshotManager struct {
+1
View File
@@ -14,6 +14,7 @@ import (
_ "yunion.io/x/onecloud/pkg/util/aliyun/provider"
_ "yunion.io/x/onecloud/pkg/util/azure/provider"
_ "yunion.io/x/onecloud/pkg/util/esxi/provider"
_ "yunion.io/x/onecloud/pkg/util/qcloud/provider"
"yunion.io/x/onecloud/pkg/cloudcommon"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
+1
View File
@@ -2,6 +2,7 @@ package aliyun
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
+365
View File
@@ -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
}
+295
View File
@@ -0,0 +1,295 @@
package qcloud
import (
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type TInternetChargeType string
const (
InternetChargeByTraffic = TInternetChargeType("PayByTraffic")
InternetChargeByBandwidth = TInternetChargeType("PayByBandwidth")
)
const (
EIP_STATUS_ASSOCIATING = "Associating"
EIP_STATUS_UNASSOCIATING = "Unassociating"
EIP_STATUS_INUSE = "InUse"
EIP_STATUS_AVAILABLE = "Available"
EIP_OPERATION_LOCK_FINANCIAL = "financial"
EIP_OPERATION_LOCK_SECURITY = "security"
EIP_INSTANCE_TYPE_ECS = "EcsInstance" // (默认值):VPC类型的ECS实例
EIP_INTANNCE_TYPE_SLB = "SlbInstance" // VPC类型的SLB实例
EIP_INSTANCE_TYPE_NAT = "Nat" // NAT网关
EIP_INSTANCE_TYPE_HAVIP = "HaVip" // HAVIP
)
type SEipAddress struct {
region *SRegion
AddressId string // EIP的ID,是EIP的唯一标识。
AddressName string // EIP名称。
AddressStatus string // EIP状态。
AddressIp string // 外网IP地址
InstanceId string // 绑定的资源实例ID。可能是一个CVM,NAT。
CreatedTime time.Time // 创建时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
NetworkInterfaceId string // 绑定的弹性网卡ID
PrivateAddressIp string // 绑定的资源内网ip
IsArrears bool // 资源隔离状态。true表示eip处于隔离状态,false表示资源处于未隔离装填
IsBlocked bool // 资源封堵状态。true表示eip处于封堵状态,false表示eip处于未封堵状态
IsEipDirectConnection bool // eip是否支持直通模式。true表示eip支持直通模式,false表示资源不支持直通模式
AddressType string // eip资源类型,包括"CalcIP","WanIP","EIP","AnycastEIP"。其中"CalcIP"表示设备ip,“WanIP”表示普通公网ip,“EIP”表示弹性公网ip,“AnycastEip”表示加速EIP
CascadeRelease bool // eip是否在解绑后自动释放。true表示eip将会在解绑后自动释放,false表示eip在解绑后不会自动释放
}
func (self *SEipAddress) GetId() string {
return self.AddressId
}
func (self *SEipAddress) GetName() string {
return self.AddressName
}
func (self *SEipAddress) GetGlobalId() string {
return self.AddressId
}
func (self *SEipAddress) GetStatus() string {
switch self.AddressStatus {
case EIP_STATUS_AVAILABLE, EIP_STATUS_INUSE:
return models.EIP_STATUS_READY
case EIP_STATUS_ASSOCIATING:
return models.EIP_STATUS_ASSOCIATE
case EIP_STATUS_UNASSOCIATING:
return models.EIP_STATUS_DISSOCIATE
default:
return models.EIP_STATUS_UNKNOWN
}
}
func (self *SEipAddress) Refresh() error {
if self.IsEmulated() {
return nil
}
new, err := self.region.GetEip(self.AddressId)
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SEipAddress) IsEmulated() bool {
if self.AddressId == self.InstanceId {
// fixed Public IP
return true
} else {
return false
}
}
func (self *SEipAddress) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SEipAddress) GetIpAddr() string {
return self.AddressIp
}
func (self *SEipAddress) GetMode() string {
if self.InstanceId == self.AddressId {
return models.EIP_MODE_INSTANCE_PUBLICIP
}
return models.EIP_MODE_STANDALONE_EIP
}
func (self *SEipAddress) GetAssociationType() string {
switch self.AddressType {
case "EIP", "AnycastEIP", "WanIP":
return "server"
case "CalcIP":
return "server"
default:
log.Fatalf("unsupported type: %s", self.AddressType)
return "unsupported"
}
}
func (self *SEipAddress) GetAssociationExternalId() string {
return self.InstanceId
}
func (self *SEipAddress) GetManagerId() string {
return self.region.client.providerId
}
func (self *SEipAddress) Delete() error {
return self.region.DeallocateEIP(self.AddressId)
}
func (self *SEipAddress) GetBandwidth() int {
return 100
//return self.Bandwidth
}
func (self *SEipAddress) GetInternetChargeType() string {
// switch self.InternetChargeType {
// case string(InternetChargeByTraffic):
// return models.EIP_CHARGE_TYPE_BY_TRAFFIC
// case string(InternetChargeByBandwidth):
// return models.EIP_CHARGE_TYPE_BY_BANDWIDTH
// default:
// return models.EIP_CHARGE_TYPE_BY_TRAFFIC
// }
return "unkonw"
}
func (self *SEipAddress) Associate(instanceId string) error {
err := self.region.AssociateEip(self.AddressId, instanceId)
if err != nil {
return err
}
return cloudprovider.WaitStatus(self, models.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
}
func (self *SEipAddress) Dissociate() error {
err := self.region.DissociateEip(self.AddressId)
if err != nil {
return err
}
return cloudprovider.WaitStatus(self, models.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
}
func (self *SEipAddress) ChangeBandwidth(bw int) error {
return self.region.UpdateEipBandwidth(self.AddressId, bw)
}
func (region *SRegion) GetEips(eipId string, offset int, limit int) ([]SEipAddress, int, error) {
if limit > 50 || limit <= 0 {
limit = 50
}
params := make(map[string]string)
params["Limit"] = fmt.Sprintf("%d", limit)
params["Offset"] = fmt.Sprintf("%d", offset)
if len(eipId) > 0 {
params["AddressIds.0"] = eipId
}
body, err := region.vpcRequest("DescribeAddresses", params)
if err != nil {
log.Errorf("DescribeEipAddresses fail %s", err)
return nil, 0, err
}
eips := make([]SEipAddress, 0)
err = body.Unmarshal(&eips, "AddressSet")
if err != nil {
log.Errorf("Unmarshal EipAddress details fail %s", err)
return nil, 0, err
}
total, _ := body.Int("TotalCount")
for i := 0; i < len(eips); i++ {
eips[i].region = region
}
return eips, int(total), nil
}
func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
eips, total, err := region.GetEips(eipId, 0, 1)
if err != nil {
return nil, err
}
if total != 1 {
return nil, cloudprovider.ErrNotFound
}
return &eips[0], nil
}
func (region *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType) (*SEipAddress, error) {
params := make(map[string]string)
params["Region"] = region.Region
addRessSet := []string{}
body, err := region.vpcRequest("AllocateAddresses", params)
if err != nil {
return nil, err
}
if err := body.Unmarshal(&addRessSet, "AddressSet"); err == nil && len(addRessSet) > 0 {
params["AddressId"] = addRessSet[0]
params["AddressName"] = name
if _, err := region.vpcRequest("ModifyAddressAttribute", params); err != nil {
return nil, err
} else if err := region.UpdateEipBandwidth(addRessSet[0], bwMbps); err != nil {
return nil, err
}
return region.GetEip(addRessSet[0])
}
return nil, cloudprovider.ErrNotFound
}
func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string) (cloudprovider.ICloudEIP, error) {
var ctype TInternetChargeType
switch chargeType {
case models.EIP_CHARGE_TYPE_BY_TRAFFIC:
ctype = InternetChargeByTraffic
case models.EIP_CHARGE_TYPE_BY_BANDWIDTH:
ctype = InternetChargeByBandwidth
}
return region.AllocateEIP(name, bwMbps, ctype)
}
func (region *SRegion) DeallocateEIP(eipId string) error {
params := make(map[string]string)
params["Region"] = region.Region
params["AddressIds.0"] = eipId
_, err := region.vpcRequest("ReleaseAddresses", params)
if err != nil {
log.Errorf("ReleaseAddresses fail %s", err)
}
return err
}
func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
params := make(map[string]string)
params["AllocationId"] = eipId
params["InstanceId"] = instanceId
_, err := region.vpcRequest("AssociateEipAddress", params)
if err != nil {
log.Errorf("AssociateEipAddress fail %s", err)
}
return err
}
func (region *SRegion) DissociateEip(eipId string) error {
params := make(map[string]string)
params["Region"] = region.Region
params["AddressId"] = eipId
_, err := region.vpcRequest("DisassociateAddress", params)
if err != nil {
log.Errorf("UnassociateEipAddress fail %s", err)
}
return err
}
func (region *SRegion) UpdateEipBandwidth(eipId string, bw int) error {
// params := make(map[string]string)
// params["Region"] = region.Region
// params["AddressIds.0"] = eipId
// params["InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bw)
// _, err := region.vpcRequest("ModifyAddressesBandwidth", params)
// return err
// 腾讯云这个接口目前有问题
return nil
}
+268
View File
@@ -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
}
+186
View File
@@ -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
}
}
+730
View File
@@ -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_SSDSSD云硬盘 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_SSDSSD云硬盘 默认取值: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-zA-Z]、[0-9] 和 [( ) ~ ! @ # $ % ^ & * - + = &#124; { } [ ] : ; ' , . ? / ]中的特殊符号。<br><li>Windows实例密码必须12到16位,至少包括三项[a-z][A-Z][0-9] 和 [( ) ~ ! @ # $ % ^ & * - + = { } [ ] : ; ' , . ? /]中的特殊符号。 若不指定该参数,则由系统随机生成密码,并通过站内信方式通知到用户。
KeyIds []string // 密钥ID列表。关联密钥后,就可以通过对应的私钥来访问实例;KeyId可通过接口DescribeKeyPairs获取,密钥与密码不能同时指定,同时Windows操作系统不支持指定密钥。当前仅支持购买的时候指定一个密钥。
KeepImageLogin string // 保持镜像的原始设置。该参数与Password或KeyIds.N不能同时指定。只有使用自定义镜像、共享镜像或外部导入镜像创建实例时才能指定该参数为TRUE。取值范围: TRUE:表示保持镜像的登录设置 FALSE:表示不保持镜像的登录设置 默认取值:FALSE。
}
type Tag struct {
Key string
Value string
}
type SInstance struct {
host *SHost
image *SImage
idisks []cloudprovider.ICloudDisk
Placement Placement
InstanceId string
InstanceType string
CPU int8
Memory int
RestrictState string //NORMAL EXPIRED PROTECTIVELY_ISOLATED
InstanceName string
InstanceChargeType InstanceChargeType //PREPAID:表示预付费,即包年包月 POSTPAID_BY_HOUR:表示后付费,即按量计费 CDHPAID:CDH付费,即只对CDH计费,不对CDH上的实例计费。
SystemDisk SystemDisk //实例系统盘信息。
DataDisks []DataDisk //实例数据盘信息。只包含随实例购买的数据盘。
PrivateIpAddresses []string //实例主网卡的内网IP列表。
PublicIpAddresses []string //实例主网卡的公网IP列表。
InternetAccessible InternetAccessible //实例带宽信息。
VirtualPrivateCloud VirtualPrivateCloud //实例所属虚拟私有网络信息。
ImageId string // 生产实例所使用的镜像ID。
RenewFlag string // 自动续费标识。取值范围:NOTIFY_AND_MANUAL_RENEW:表示通知即将过期,但不自动续费 NOTIFY_AND_AUTO_RENEW:表示通知即将过期,而且自动续费 DISABLE_NOTIFY_AND_MANUAL_RENEW:表示不通知即将过期,也不自动续费。
CreatedTime time.Time // 创建时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
ExpiredTime time.Time // 到期时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
OsName string // 操作系统名称。
SecurityGroupIds []string // 实例所属安全组。该参数可以通过调用 DescribeSecurityGroups 的返回值中的sgId字段来获取。
LoginSettings LoginSettings //实例登录设置。目前只返回实例所关联的密钥。
InstanceState string // 实例状态。取值范围:PENDING:表示创建中 LAUNCH_FAILED:表示创建失败 RUNNING:表示运行中 STOPPED:表示关机 STARTING:表示开机中 STOPPING:表示关机中 REBOOTING:表示重启中 SHUTDOWN:表示停止待销毁 TERMINATING:表示销毁中。
Tags []Tag
}
func (self *SRegion) GetInstances(zoneId string, ids []string, offset int, limit int) ([]SInstance, int, error) {
params := make(map[string]string)
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
}
+40
View File
@@ -0,0 +1,40 @@
package qcloud
import (
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/pkg/util/netutils"
)
type SInstanceNic struct {
instance *SInstance
ipAddr string
}
func (self *SInstanceNic) GetIP() string {
return self.ipAddr
}
func (self *SInstanceNic) GetMAC() string {
ip, _ := netutils.NewIPV4Addr(self.ipAddr)
return ip.ToMac("00:16:")
}
func (self *SInstanceNic) GetDriver() string {
return "virtio"
}
func (self *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork {
networkId := self.instance.VirtualPrivateCloud.VpcId
wires, err := self.instance.host.GetIWires()
if err != nil {
return nil
}
for i := 0; i < len(wires); i++ {
wire := wires[i].(*SWire)
net := wire.getNetworkById(networkId)
if net != nil {
return net
}
}
return nil
}
+42
View File
@@ -0,0 +1,42 @@
package qcloud
import "yunion.io/x/log"
// "time"
// {"CpuCoreCount":1,"EniQuantity":1,"GPUAmount":0,"GPUSpec":"","InstanceTypeFamily":"ecs.t1","InstanceTypeId":"ecs.t1.xsmall","LocalStorageCategory":"","MemorySize":0.500000}
// InstanceBandwidthRx":26214400,"InstanceBandwidthTx":26214400,"InstancePpsRx":4500000,"InstancePpsTx":4500000
type SInstanceType struct {
Zone string // 可用区。
InstanceType string // 实例机型。
InstanceFamily string // 实例机型系列。
GPU int // GPU核数,单位:核。
CPU int // CPU核数,单位:核。
Memory int // 内存容量,单位:GB。
CbsSupport string // 是否支持云硬盘。取值范围:TRUE:表示支持云硬盘;FALSE:表示不支持云硬盘。
InstanceTypeState string // 机型状态。取值范围:AVAILABLE:表示机型可用;UNAVAILABLE:表示机型不可用。
}
func (self *SRegion) GetInstanceTypes() ([]SInstanceType, error) {
params := make(map[string]string)
params["Region"] = self.Region
body, err := self.cvmRequest("DescribeInstanceTypeConfigs", params)
if err != nil {
log.Errorf("DescribeInstanceTypeConfigs fail %s", err)
return nil, err
}
instanceTypes := make([]SInstanceType, 0)
err = body.Unmarshal(&instanceTypes, "InstanceTypeConfigSet")
if err != nil {
log.Errorf("Unmarshal instance type details fail %s", err)
return nil, err
}
return instanceTypes, nil
}
func (self *SInstanceType) memoryMB() int {
return int(self.Memory * 1024)
}
+143
View File
@@ -0,0 +1,143 @@
package qcloud
import (
"fmt"
"strconv"
"time"
"github.com/aokoli/goutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
type SKeypair struct {
AssociatedInstanceIds []string
CreateTime time.Time
Description string
KeyId string
KeyName string
PublicKey string
}
func (self *SRegion) GetKeypairs(name string, keyIds []string, offset int, limit int) ([]SKeypair, int, error) {
if limit > 50 || limit <= 0 {
limit = 50
}
params := map[string]string{}
params["Limit"] = fmt.Sprintf("%d", limit)
params["Offset"] = fmt.Sprintf("%d", offset)
if len(keyIds) > 0 {
for i := 0; i < len(keyIds); i++ {
params[fmt.Sprintf("KeyIds.%d", i)] = keyIds[i]
}
} else {
if len(name) > 0 {
params["Filters.0.Name"] = "key-name"
params["Filters.0.Values.0"] = name
}
}
body, err := self.cvmRequest("DescribeKeyPairs", params)
if err != nil {
log.Errorf("GetKeypairs fail %s", err)
return nil, 0, err
}
keypairs := []SKeypair{}
err = body.Unmarshal(&keypairs, "KeyPairSet")
if err != nil {
log.Errorf("Unmarshal keypair fail %s", err)
return nil, 0, err
}
total, _ := body.Int("TotalCount")
return keypairs, int(total), nil
}
func (self *SRegion) ImportKeypair(name string, pubKey string) (*SKeypair, error) {
params := map[string]string{}
params["PublicKey"] = pubKey
params["ProjectId"] = "0"
params["KeyName"] = name
body, err := self.cvmRequest("ImportKeyPair", params)
if err != nil {
log.Errorf("ImportKeypair fail %s", err)
return nil, err
}
keypairID, err := body.GetString("KeyId")
if err != nil {
return nil, err
}
keypairs, total, err := self.GetKeypairs("", []string{keypairID}, 0, 1)
if err != nil {
return nil, err
}
if total != 1 {
return nil, cloudprovider.ErrNotFound
}
return &keypairs[0], nil
}
func (self *SRegion) AttachKeypair(instanceId string, keypairId string) error {
params := map[string]string{}
params["InstanceIds.0"] = instanceId
params["KeyIds.0"] = keypairId
_, err := self.cvmRequest("AssociateInstancesKeyPairs", params)
return err
}
func (self *SRegion) DetachKeyPair(instanceId string, keypairId string) error {
params := make(map[string]string)
params["InstanceIds.0"] = instanceId
params["KeyIds.0"] = keypairId
_, err := self.cvmRequest("DisassociateInstancesKeyPairs", params)
return err
}
func (self *SRegion) CreateKeyPair(name string) (*SKeypair, error) {
params := make(map[string]string)
params["KeyName"] = name
params["ProjectId"] = "0"
body, err := self.cvmRequest("CreateKeyPair", params)
keypair := SKeypair{}
err = body.Unmarshal(&keypair, "KeyPair")
if err != nil {
return nil, err
}
return &keypair, err
}
func (self *SRegion) lookUpAliyunKeypair(publicKey string) (string, error) {
keypairs, _, err := self.GetKeypairs("", []string{}, 0, 0)
if err != nil {
return "", err
}
for i := 0; i < len(keypairs); i++ {
if keypairs[i].PublicKey == publicKey {
return keypairs[i].KeyId, nil
}
}
return "", cloudprovider.ErrNotFound
}
func (self *SRegion) syncKeypair(publicKey string) (string, error) {
keypairId, e := self.lookUpAliyunKeypair(publicKey)
if e == nil {
return keypairId, nil
}
prefix, e := goutils.RandomAlphabetic(6)
if e != nil {
return "", fmt.Errorf("publicKey error %s", e)
}
name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
keypair, err := self.ImportKeypair(name, publicKey)
if err != nil {
return "", err
}
return keypair.KeyId, nil
}
+23
View File
@@ -0,0 +1,23 @@
package qcloud
var LatitudeAndLongitude = map[string]map[string]float32{
"ap-bangkok": {"latitude": 13.756330, "longitude": 100.501762}, // 腾讯云 亚太地区(曼谷)
"ap-beijing": {"latitude": 39.904202, "longitude": 116.407394}, // 腾讯云 华北地区(北京)
"ap-chengdu": {"latitude": 30.572815, "longitude": 104.066803}, // 腾讯云 西南地区(成都)
"ap-chongqing": {"latitude": 29.431585, "longitude": 106.912254}, // 腾讯云 西南地区(重庆)
"ap-guangzhou": {"latitude": 23.129110, "longitude": 113.264381}, // 腾讯云 华南地区(广州)
"ap-guangzhou-open": {"latitude": 23.126593, "longitude": 113.273415}, // 腾讯云 华南地区(广州Open)
"ap-hongkong": {"latitude": 22.396427, "longitude": 114.109497}, // 腾讯云 东南亚地区(香港)
"ap-mumbai": {"latitude": 19.075983, "longitude": 72.877655}, // 腾讯云 亚太地区(孟买)
"ap-seoul": {"latitude": 37.566536, "longitude": 126.977966}, // 腾讯云 东南亚地区(首尔)
"ap-shanghai": {"latitude": 31.230391, "longitude": 121.473701}, // 腾讯云 华东地区(上海)
"ap-shanghai-fsi": {"latitude": 31.311033, "longitude": 121.536217}, // 腾讯云 华东地区(上海金融)
"ap-shenzhen-fsi": {"latitude": 22.531544, "longitude": 114.025467}, // 腾讯云 华南地区(深圳金融)
"ap-singapore": {"latitude": 1.352083, "longitude": 103.819839}, // 腾讯云 东南亚地区(新加坡)
"ap-tokyo": {"latitude": 35.709026, "longitude": 139.731995}, // 腾讯云 亚太地区(东京)
"eu-frankfurt": {"latitude": 51.165691, "longitude": 10.451526}, // 腾讯云 欧洲地区(德国)
"eu-moscow": {"latitude": 55.755825, "longitude": 37.617298}, // 腾讯云 欧洲地区(莫斯科)
"na-ashburn": {"latitude": 37.431572, "longitude": -78.656891}, // 腾讯云 美国东部(弗吉尼亚)
"na-siliconvalley": {"latitude": 37.387474, "longitude": -122.057541}, // 腾讯云 美国西部(硅谷)
"na-toronto": {"latitude": 43.653225, "longitude": -79.383186}, // 腾讯云 北美地区(多伦多)
}
+128
View File
@@ -0,0 +1,128 @@
package qcloud
import (
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SLocalDisk struct {
storage *SLocalStorage
DiskId string
DiskSize float32
DisktType string
DiskUsage string
}
func (self *SLocalDisk) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SLocalDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SLocalDisk) Delete() error {
return nil
}
func (self *SLocalDisk) GetBillingType() string {
return ""
}
func (self *SLocalDisk) GetFsFormat() string {
return ""
}
func (self *SLocalDisk) GetIsNonPersistent() bool {
return false
}
func (self *SLocalDisk) GetDriver() string {
return "scsi"
}
func (self *SLocalDisk) GetCacheMode() string {
return "none"
}
func (self *SLocalDisk) GetMountpoint() string {
return ""
}
func (self *SLocalDisk) GetDiskFormat() string {
return "vhd"
}
func (self *SLocalDisk) GetDiskSizeMB() int {
return int(self.DiskSize) * 1024
}
func (self *SLocalDisk) GetIsAutoDelete() bool {
return true
}
func (self *SLocalDisk) GetExpiredAt() time.Time {
return time.Now()
}
func (self *SLocalDisk) GetDiskType() string {
switch self.DiskUsage {
case "SYSTEM_DISK":
return models.DISK_TYPE_SYS
case "DATA_DISK":
return models.DISK_TYPE_DATA
default:
return models.DISK_TYPE_DATA
}
}
func (self *SLocalDisk) Refresh() error {
return nil
}
func (self *SLocalDisk) Reset(snapshotId string) error {
return cloudprovider.ErrNotSupported
}
func (self *SLocalDisk) GetTemplateId() string {
return ""
}
func (self *SLocalDisk) GetStatus() string {
return models.DISK_READY
}
func (self *SLocalDisk) GetName() string {
return self.DiskId
}
func (self *SLocalDisk) GetId() string {
return self.DiskId
}
func (self *SLocalDisk) GetGlobalId() string {
return self.DiskId
}
func (self *SLocalDisk) IsEmulated() bool {
return false
}
func (self *SLocalDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
return nil, nil
}
func (self *SLocalDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
return nil, nil
}
func (self *SLocalDisk) GetIStorge() cloudprovider.ICloudStorage {
return self.storage
}
func (self *SLocalDisk) Resize(size int64) error {
return cloudprovider.ErrNotSupported
}
+98
View File
@@ -0,0 +1,98 @@
package qcloud
import (
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SLocalStorage struct {
zone *SZone
storageType string
}
func (self *SLocalStorage) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SLocalStorage) GetId() string {
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetId(), self.storageType)
}
func (self *SLocalStorage) GetName() string {
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerName, self.zone.GetId(), self.storageType)
}
func (self *SLocalStorage) GetGlobalId() string {
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetGlobalId(), self.storageType)
}
func (self *SLocalStorage) IsEmulated() bool {
return true
}
func (self *SLocalStorage) GetIZone() cloudprovider.ICloudZone {
return self.zone
}
func (self *SLocalStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
disks := []SLocalDisk{}
idisks := make([]cloudprovider.ICloudDisk, len(disks))
for i := 0; i < len(disks); i++ {
disks[i].storage = self
idisks[i] = &disks[i]
}
return idisks, nil
}
func (self *SLocalStorage) GetStorageType() string {
return strings.ToLower(self.storageType)
}
func (self *SLocalStorage) GetMediumType() string {
if strings.HasSuffix(self.storageType, "_BASIC") {
return models.DISK_TYPE_ROTATE
}
return models.DISK_TYPE_SSD
}
func (self *SLocalStorage) GetCapacityMB() int {
return 0 // unlimited
}
func (self *SLocalStorage) GetStorageConf() jsonutils.JSONObject {
conf := jsonutils.NewDict()
return conf
}
func (self *SLocalStorage) GetManagerId() string {
return self.zone.region.client.providerId
}
func (self *SLocalStorage) GetStatus() string {
return models.STORAGE_ONLINE
}
func (self *SLocalStorage) Refresh() error {
// do nothing
return nil
}
func (self *SLocalStorage) GetEnabled() bool {
return true
}
func (self *SLocalStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
return self.zone.region.getStoragecache()
}
func (self *SLocalStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SLocalStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) {
return &SLocalDisk{storage: self, DiskId: idStr}, nil
}
+132
View File
@@ -0,0 +1,132 @@
package qcloud
import (
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/pkg/util/netutils"
)
type SNetwork struct {
wire *SWire
CidrBlock string
Zone string
SubnetId string
VpcId string
SubnetName string
AvailableIpAddressCount int
CreatedTime time.Time
EnableBroadcast bool
IsDefault bool
RouteTableId string
}
func (self *SNetwork) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SNetwork) GetId() string {
return self.SubnetId
}
func (self *SNetwork) GetName() string {
if len(self.SubnetName) > 0 {
return self.SubnetName
}
return self.SubnetId
}
func (self *SNetwork) GetGlobalId() string {
return self.SubnetId
}
func (self *SNetwork) IsEmulated() bool {
return false
}
func (self *SNetwork) GetStatus() string {
return models.NETWORK_STATUS_AVAILABLE
}
func (self *SNetwork) Delete() error {
return self.wire.zone.region.DeleteNetwork(self.SubnetId)
}
func (self *SRegion) DeleteNetwork(networkId string) error {
params := make(map[string]string)
params["SubnetId"] = networkId
_, err := self.vpcRequest("DeleteSubnet", params)
return err
}
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
return self.wire
}
func (self *SNetwork) GetAllocTimeoutSeconds() int {
return 120 // 2 minutes
}
func (self *SNetwork) GetGateway() string {
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
endIp = endIp.StepDown() // 254
return endIp.String()
}
func (self *SNetwork) GetIpStart() string {
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
startIp = startIp.StepUp() // 1
return startIp.String()
}
func (self *SNetwork) GetIpEnd() string {
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
endIp = endIp.StepDown() // 254
endIp = endIp.StepDown() // 253
endIp = endIp.StepDown() // 252
return endIp.String()
}
func (self *SNetwork) GetIpMask() int8 {
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
return pref.MaskLen
}
func (self *SNetwork) GetIsPublic() bool {
// return self.IsDefault
return true
}
func (self *SNetwork) GetServerType() string {
return models.SERVER_TYPE_GUEST
}
func (self *SNetwork) Refresh() error {
log.Debugf("network refresh %s", self.SubnetId)
new, err := self.wire.zone.region.GetNetwork(self.SubnetId)
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SRegion) CreateNetwork(zoneId string, vpcId string, name string, cidr string, desc string) (string, error) {
params := make(map[string]string)
params["Zone"] = zoneId
params["VpcId"] = vpcId
params["CidrBlock"] = cidr
params["SubnetName"] = name
body, err := self.vpcRequest("CreateSubnet", params)
if err != nil {
return "", err
}
return body.GetString("Subnet", "SubnetId")
}
+1
View File
@@ -0,0 +1 @@
package provider
+84
View File
@@ -0,0 +1,84 @@
package provider
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/util/qcloud"
)
type 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
}
+258
View File
@@ -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(&regions, "RegionSet")
if err != nil {
log.Errorf("unmarshal json error %s", err)
return err
}
client.iregions = make([]cloudprovider.ICloudRegion, len(regions))
for i := 0; i < len(regions); i++ {
regions[i].client = client
client.iregions[i] = &regions[i]
}
return nil
}
func (client *SQcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
err := client.fetchRegions()
if err != nil {
return nil, err
}
subAccount := cloudprovider.SSubAccount{}
subAccount.Name = client.providerName
subAccount.Account = client.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
}
+528
View File
@@ -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
}
+415
View File
@@ -0,0 +1,415 @@
package qcloud
import (
"fmt"
"net"
"sort"
"strconv"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/secrules"
"yunion.io/x/pkg/utils"
)
type SecurityGroupPolicy struct {
vpc *SVpc
PolicyIndex int // 安全组规则索引号。
Protocol string // 协议, 取值: TCP,UDP, ICMP。
Port string // 端口(all, 离散port, range)。
ServiceTemplate ServiceTemplateSpecification // 协议端口ID或者协议端口组ID。ServiceTemplate和Protocol+Port互斥。
CidrBlock string // 网段或IP(互斥)。
SecurityGroupId string // 已绑定安全组的网段或IP。
AddressTemplate AddressTemplateSpecification // IP地址ID或者ID地址组ID。
Action string // ACCEPT 或 DROP。
PolicyDescription string // 安全组规则描述。
direction string
}
type ServiceTemplateSpecification struct {
ServiceId string // 协议端口ID,例如:ppm-f5n1f8da。
ServiceGroupId string // 协议端口组ID,例如:ppmg-f5n1f8da。
}
type AddressTemplateSpecification struct {
AddressId string // IP地址ID,例如:ipm-2uw6ujo6。
AddressGroupId string // IP地址组ID,例如:ipmg-2uw6ujo6。
}
type SecurityGroupPolicySet struct {
Version string
Egress []SecurityGroupPolicy // 出站规则。
Ingress []SecurityGroupPolicy // 入站规则。
}
type SSecurityGroup struct {
vpc *SVpc
SecurityGroupId string // 安全组实例ID,例如:sg-ohuuioma。
SecurityGroupName string // 安全组名称,可任意命名,但不得超过60个字符。
SecurityGroupDesc string // 安全组备注,最多100个字符。
ProjectId string // 项目id,默认0。可在qcloud控制台项目管理页面查询到。
IsDefault bool // 是否是默认安全组,默认安全组不支持删除。
CreatedTime time.Time // 安全组创建时间。
SecurityGroupPolicySet SecurityGroupPolicySet
}
type SecurityGroupRuleSet []SecurityGroupPolicy
func (v SecurityGroupRuleSet) Len() int {
return len(v)
}
func (v SecurityGroupRuleSet) Swap(i, j int) {
v[i], v[j] = v[j], v[i]
}
func (v SecurityGroupRuleSet) Less(i, j int) bool {
if v[i].PolicyIndex < v[j].PolicyIndex {
return true
} else if v[i].PolicyIndex == v[j].PolicyIndex {
return strings.Compare(v[i].String(), v[j].String()) <= 0
}
return false
}
func (self *SRegion) GetSecurityGroups(vpcId string, offset int, limit int) ([]SSecurityGroup, int, error) {
if limit > 50 || limit <= 0 {
limit = 50
}
params := make(map[string]string)
params["Limit"] = fmt.Sprintf("%d", limit)
params["Offset"] = fmt.Sprintf("%d", offset)
body, err := self.vpcRequest("DescribeSecurityGroups", params)
if err != nil {
log.Errorf("GetSecurityGroups fail %s", err)
return nil, 0, err
}
secgrps := make([]SSecurityGroup, 0)
err = body.Unmarshal(&secgrps, "SecurityGroupSet")
if err != nil {
log.Errorf("Unmarshal security groups fail %s", err)
return nil, 0, err
}
total, _ := body.Float("TotalCount")
return secgrps, int(total), nil
}
func (self *SSecurityGroup) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SSecurityGroup) GetId() string {
return self.SecurityGroupId
}
func (self *SSecurityGroup) GetGlobalId() string {
return self.SecurityGroupId
}
func (self *SSecurityGroup) GetDescription() string {
return self.SecurityGroupDesc
}
func (self *SSecurityGroup) GetName() string {
if len(self.SecurityGroupName) > 0 {
return self.SecurityGroupName
}
return self.SecurityGroupId
}
func (self *SecurityGroupPolicy) String() string {
rules := self.toRules()
result := []string{}
for _, rule := range rules {
result = append(result, rule.String())
}
return strings.Join(result, ";")
}
func parseCIDR(cidr string) (*net.IPNet, error) {
if strings.Index(cidr, "/") > 0 {
_, ipnet, err := net.ParseCIDR(cidr)
return ipnet, err
}
ip := net.ParseIP(cidr)
if ip == nil {
return nil, fmt.Errorf("Parse ip %s error", cidr)
}
return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}, nil
}
func (self *SecurityGroupPolicy) toRules() []secrules.SecurityRule {
result := []secrules.SecurityRule{}
rule := secrules.SecurityRule{
Action: secrules.SecurityRuleAllow,
Protocol: secrules.PROTO_ANY,
Direction: secrules.TSecurityRuleDirection(self.direction),
Ports: []int{},
PortStart: -1,
PortEnd: -1,
}
if len(self.SecurityGroupId) != 0 {
//安全组关联安全组的规则忽略
return nil
}
if strings.ToLower(self.Action) == "drop" {
rule.Action = secrules.SecurityRuleDeny
}
if utils.IsInStringArray(strings.ToLower(self.Protocol), []string{"tcp", "udp", "icmp"}) {
rule.Protocol = strings.ToLower(self.Protocol)
}
if strings.Index(self.Port, ",") > 0 {
for _, _port := range strings.Split(self.Port, ",") {
port, err := strconv.Atoi(_port)
if err != nil {
log.Errorf("parse secgroup port %s %s error %v", self.Port, _port, err)
continue
}
rule.Ports = append(rule.Ports, port)
}
} else if strings.Index(self.Port, "-") > 0 {
ports := strings.Split(self.Port, "-")
if len(ports) == 2 {
portStart, err := strconv.Atoi(ports[0])
if err != nil {
return nil
}
portEnd, err := strconv.Atoi(ports[1])
if err != nil {
return nil
}
rule.PortStart, rule.PortEnd = portStart, portEnd
}
} else if strings.ToLower(self.Port) != "all" {
port, err := strconv.Atoi(self.Port)
if err != nil {
return nil
}
rule.PortStart, rule.PortEnd = port, port
}
if len(self.AddressTemplate.AddressGroupId) > 0 {
addressGroup, total, err := self.vpc.region.AddressGroupList(self.AddressTemplate.AddressGroupId, "", 0, 1)
if err != nil {
log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err)
return nil
}
if total != 1 {
return nil
}
for i := 0; i < len(addressGroup[0].AddressTemplateIdSet); i++ {
rules, err := self.getAddressRules(rule, addressGroup[0].AddressTemplateIdSet[i])
if err != nil {
return nil
}
result = append(result, rules...)
}
} else if len(self.AddressTemplate.AddressId) > 0 {
rules, err := self.getAddressRules(rule, self.AddressTemplate.AddressId)
if err != nil {
return nil
}
result = append(result, rules...)
} else if len(self.CidrBlock) > 0 {
ipnet, err := parseCIDR(self.CidrBlock)
if err != nil {
return nil
}
rule.IPNet = ipnet
result = append(result, rule)
}
return result
}
func (self *SecurityGroupPolicy) getAddressRules(rule secrules.SecurityRule, addressId string) ([]secrules.SecurityRule, error) {
result := []secrules.SecurityRule{}
address, total, err := self.vpc.region.AddressList(addressId, "", 0, 1)
if err != nil {
log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err)
return nil, err
}
if total != 1 {
return nil, fmt.Errorf("failed to find address %s", addressId)
}
for _, ip := range address[0].AddressSet {
ipnet, err := parseCIDR(ip)
if err != nil {
return nil, nil
}
rule.IPNet = ipnet
result = append(result, rule)
}
return result, nil
}
func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
secgroup, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId)
if err != nil {
return nil, err
}
for i := 0; i < len(secgroup.SecurityGroupPolicySet.Egress); i++ {
secgroup.SecurityGroupPolicySet.Egress[i].direction = "out"
}
for i := 0; i < len(secgroup.SecurityGroupPolicySet.Ingress); i++ {
secgroup.SecurityGroupPolicySet.Ingress[i].direction = "in"
}
originRules := []SecurityGroupPolicy{}
originRules = append(secgroup.SecurityGroupPolicySet.Egress)
originRules = append(secgroup.SecurityGroupPolicySet.Ingress)
sort.Sort(SecurityGroupRuleSet(originRules))
rules := []secrules.SecurityRule{}
priority := 100
for _, rule := range originRules {
rule.vpc = self.vpc
subRules := rule.toRules()
for i := 0; i < len(subRules); i++ {
subRules[i].Priority = priority
}
if len(subRules) > 0 {
priority--
}
rules = append(rules, subRules...)
}
return rules, nil
}
func (self *SSecurityGroup) GetStatus() string {
return ""
}
func (self *SSecurityGroup) IsEmulated() bool {
return false
}
func (self *SSecurityGroup) Refresh() error {
if new, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId); err != nil {
return err
} else {
return jsonutils.Update(self, new)
}
}
func (self *SRegion) GetSecurityGroupDetails(secGroupId string) (*SSecurityGroup, error) {
params := make(map[string]string)
params["Region"] = self.Region
params["SecurityGroupId"] = secGroupId
body, err := self.vpcRequest("DescribeSecurityGroupPolicies", params)
if err != nil {
log.Errorf("DescribeSecurityGroupAttribute fail %s", err)
return nil, err
}
secgrp := SSecurityGroup{SecurityGroupId: secGroupId}
err = body.Unmarshal(&secgrp.SecurityGroupPolicySet, "SecurityGroupPolicySet")
if err != nil {
log.Errorf("Unmarshal security group details fail %s", err)
return nil, err
}
return &secgrp, nil
}
func (self *SRegion) DeleteSecurityGroup(secGroupId string) error {
params := make(map[string]string)
params["Region"] = self.Region
params["SecurityGroupId"] = secGroupId
_, err := self.vpcRequest("DeleteSecurityGroup", params)
return err
}
type AddressTemplate struct {
AddressSet []string
AddressTemplateId string
AddressTemplateName string
CreatedTime time.Time
}
func (self *SRegion) AddressList(addressId, addressName string, offset, limit int) ([]AddressTemplate, int, error) {
params := map[string]string{}
filter := 0
if len(addressId) > 0 {
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-id"
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressId
filter++
}
if len(addressName) > 0 {
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-name"
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressName
filter++
}
params["Offset"] = fmt.Sprintf("%d", offset)
if limit == 0 {
limit = 20
}
params["Limit"] = fmt.Sprintf("%d", limit)
body, err := self.vpcRequest("DescribeAddressTemplates", params)
if err != nil {
return nil, 0, err
}
addressTemplates := []AddressTemplate{}
err = body.Unmarshal(&addressTemplates, "AddressTemplateSet")
if err != nil {
return nil, 0, err
}
total, _ := body.Float("TotalCount")
return addressTemplates, int(total), nil
}
type AddressTemplateGroup struct {
AddressTemplateIdSet []string
AddressTemplateGroupName string
AddressTemplateGroupId string
CreatedTime time.Time
}
func (self *SRegion) AddressGroupList(groupId, groupName string, offset, limit int) ([]AddressTemplateGroup, int, error) {
params := map[string]string{}
filter := 0
if len(groupId) > 0 {
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-id"
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupId
filter++
}
if len(groupName) > 0 {
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-name"
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupName
filter++
}
params["Offset"] = fmt.Sprintf("%d", offset)
if limit == 0 {
limit = 20
}
params["Limit"] = fmt.Sprintf("%d", limit)
body, err := self.vpcRequest("DescribeAddressTemplateGroups", params)
if err != nil {
return nil, 0, err
}
addressTemplateGroups := []AddressTemplateGroup{}
err = body.Unmarshal(&addressTemplateGroups, "AddressTemplateGroupSet")
if err != nil {
return nil, 0, err
}
total, _ := body.Float("TotalCount")
return addressTemplateGroups, int(total), nil
}
func (self *SRegion) CreateSecurityGroup(name, description string) (*SSecurityGroup, error) {
params := make(map[string]string)
params["Region"] = self.Region
params["GroupName"] = name
params["GroupDescription"] = description
if len(description) == 0 {
params["GroupDescription"] = "Customize Create"
}
secgroup := SSecurityGroup{}
if body, err := self.vpcRequest("CreateSecurityGroup", params); err != nil {
return nil, err
} else if err := body.Unmarshal(&secgroup, "SecurityGroup"); err != nil {
return nil, err
}
return &secgroup, nil
}
+106
View File
@@ -0,0 +1,106 @@
package shell
import (
"context"
"os"
coslib "github.com/nelsonken/cos-go-sdk-v5/cos"
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type CosListOptions struct {
}
shellutils.R(&CosListOptions{}, "cos-list", "List COS buckets", func(cli *qcloud.SRegion, args *CosListOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
result, err := cos.GetBucketList(context.Background())
if err != nil {
return err
}
printList(result.Buckets.Bucket, len(result.Buckets.Bucket), 0, len(result.Buckets.Bucket), nil)
return nil
})
type CosListBucketOptions struct {
BUCKET string `help:"bucket name"`
}
shellutils.R(&CosListBucketOptions{}, "cos-bucket-list", "List content of a OSS bucket", func(cli *qcloud.SRegion, args *CosListBucketOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
result, err := cos.ListBucketContents(context.Background(), args.BUCKET, &coslib.QueryCondition{})
if err != nil {
return err
}
printList(result.Contents, len(result.Contents), 0, len(result.Contents), nil)
return nil
})
shellutils.R(&CosListBucketOptions{}, "cos-bucket-create", "Create a OSS bucket", func(cli *qcloud.SRegion, args *CosListBucketOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
return cos.CreateBucket(context.Background(), args.BUCKET, &coslib.AccessControl{})
})
type CosUploadOptions struct {
BUCKET string `help:"bucket name"`
KEY string `help:"Object key"`
FILE string `help:"Local file path"`
Acl string `help:"Object ACL" choices:"private|public-read|public-read-write"`
}
shellutils.R(&CosUploadOptions{}, "cos-upload", "Upload a file to a Cos bucket", func(cli *qcloud.SRegion, args *CosUploadOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
return cos.Bucket(args.BUCKET).UploadObjectBySlice(context.Background(), args.KEY, args.FILE, 3, nil)
})
type CosDownloadOptions struct {
BUCKET string `help:"bucket name"`
NAME string `help:"File name"`
}
shellutils.R(&CosDownloadOptions{}, "cos-download", "Download a file", func(cli *qcloud.SRegion, args *CosDownloadOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
//file
return cos.Bucket(args.BUCKET).DownloadObject(context.Background(), args.NAME, os.Stdout)
//return cos.Bucket(args.BUCKET).UploadObjectBySlice(context.Background(), args.KEY, args.FILE, 3, nil)
})
type CosObjectAclOptions struct {
BUCKET string `help:"bucket name"`
KEY string `help:"object key"`
ACL string `help:"ACL" choices:"private|public-read|public-read-write"`
}
shellutils.R(&CosObjectAclOptions{}, "cos-set-acl", "Set acl for a object", func(cli *qcloud.SRegion, args *CosObjectAclOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
return cos.SetBucketACL(context.Background(), args.KEY, &coslib.AccessControl{ACL: args.ACL})
})
type CosDeleteOptions struct {
BUCKET string `help:"bucket name"`
KEY string `help:"Object key"`
}
shellutils.R(&CosDeleteOptions{}, "cos-delete", "Delete a file from a Cos bucket", func(cli *qcloud.SRegion, args *CosDeleteOptions) error {
cos, err := cli.GetCosClient()
if err != nil {
return err
}
return cos.Bucket(args.BUCKET).DeleteObject(context.Background(), args.KEY)
})
}
+77
View File
@@ -0,0 +1,77 @@
package shell
import (
"fmt"
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type DiskListOptions struct {
Instance string `help:"Instance ID"`
Zone string `help:"Zone ID"`
Category string `help:"Disk category"`
Offset int `help:"List offset"`
Limit int `help:"List limit"`
}
shellutils.R(&DiskListOptions{}, "disk-list", "List disks", func(cli *qcloud.SRegion, args *DiskListOptions) error {
disks, total, e := cli.GetDisks(args.Instance, args.Zone, args.Category, nil, args.Offset, args.Limit)
if e != nil {
return e
}
printList(disks, total, args.Offset, args.Limit, []string{})
return nil
})
type DiskOptions struct {
ID string `help:"Disk ID"`
}
shellutils.R(&DiskOptions{}, "disk-delete", "Delete disks", func(cli *qcloud.SRegion, args *DiskOptions) error {
e := cli.DeleteDisk(args.ID)
if e != nil {
return e
}
return nil
})
shellutils.R(&DiskOptions{}, "disk-show", "Show disk", func(cli *qcloud.SRegion, args *DiskOptions) error {
disk, err := cli.GetDisk(args.ID)
if err != nil {
return err
}
printObject(disk)
return nil
})
type DiskCreateOptions struct {
ZONE string `help:"Zone ID"`
CATEGORY string `help:"Disk category" choices:"CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"`
NAME string `help:"Disk Name"`
SIZE int `help:"Disk Size GB"`
}
shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disk", func(cli *qcloud.SRegion, args *DiskCreateOptions) error {
diskId, err := cli.CreateDisk(args.ZONE, args.CATEGORY, args.NAME, args.SIZE, "")
if err != nil {
return err
}
fmt.Println(diskId)
return nil
})
type DiskResizeOptions struct {
ID string `help:"Disk ID"`
SIZE int64 `help:"Disk Size GB"`
}
shellutils.R(&DiskResizeOptions{}, "disk-resize", "Resize disk", func(cli *qcloud.SRegion, args *DiskResizeOptions) error {
return cli.ResizeDisk(args.ID, args.SIZE)
})
type DiskResetOptions struct {
ID string `help:"Disk ID"`
SNAPSHOT string `help:"Snapshot ID"`
}
shellutils.R(&DiskResetOptions{}, "disk-reset", "Reset disk", func(cli *qcloud.SRegion, args *DiskResetOptions) error {
return cli.ResetDisk(args.ID, args.SNAPSHOT)
})
}
+71
View File
@@ -0,0 +1,71 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type EipListOptions struct {
Eip string `help:"EIP ID"`
Offset int `help:"List offset"`
Limit int `help:"List limit"`
}
shellutils.R(&EipListOptions{}, "eip-list", "List eips", func(cli *qcloud.SRegion, args *EipListOptions) error {
eips, total, err := cli.GetEips(args.Eip, args.Offset, args.Limit)
if err != nil {
return err
}
printList(eips, total, args.Offset, args.Limit, []string{})
return nil
})
type EipAllocateOptions struct {
BANDWIDTH int `help:"EIP bandwoidth"`
NAME string `help:"EIP Name"`
ChargeType qcloud.TInternetChargeType `help:"EIP ChargeType"`
}
shellutils.R(&EipAllocateOptions{}, "eip-create", "Allocate an EIP", func(cli *qcloud.SRegion, args *EipAllocateOptions) error {
eip, err := cli.AllocateEIP(args.NAME, args.BANDWIDTH, args.ChargeType)
if err != nil {
return err
}
printObject(eip)
return nil
})
type EipReleaseOptions struct {
ID string `help:"EIP allocation ID"`
}
shellutils.R(&EipReleaseOptions{}, "eip-delete", "Release an EIP", func(cli *qcloud.SRegion, args *EipReleaseOptions) error {
return cli.DeallocateEIP(args.ID)
})
type EipShowOptions struct {
ID string `help:"EIP ID"`
}
shellutils.R(&EipShowOptions{}, "eip-show", "Show an EIP", func(cli *qcloud.SRegion, args *EipShowOptions) error {
eip, err := cli.GetEip(args.ID)
if err != nil {
return err
}
printObject(eip)
return nil
})
type EipAssociateOptions struct {
ID string `help:"EIP allocation ID"`
INSTANCE string `help:"Instance ID"`
}
shellutils.R(&EipAssociateOptions{}, "eip-associate", "Associate an EIP", func(cli *qcloud.SRegion, args *EipAssociateOptions) error {
return cli.AssociateEip(args.ID, args.INSTANCE)
})
type EipDissociateOptions struct {
ID string `help:"EIP allocation ID"`
}
shellutils.R(&EipDissociateOptions{}, "eip-dissociate", "Dissociate an EIP", func(cli *qcloud.SRegion, args *EipDissociateOptions) error {
return cli.DissociateEip(args.ID)
})
}
+55
View File
@@ -0,0 +1,55 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type ImageListOptions struct {
Status string `help:"Image status"`
Owner string `help:"Image owner" choices:"PRIVATE_IMAGE|PUBLIC_IMAGE|MARKET_IMAGE|SHARED_IMAGE"`
Image string `help:"Image Id"`
Name string `help:"Image Name"`
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&ImageListOptions{}, "image-list", "List images", func(cli *qcloud.SRegion, args *ImageListOptions) error {
imageIds := []string{}
if len(args.Image) > 0 {
imageIds = append(imageIds, args.Image)
}
images, total, err := cli.GetImages(args.Status, args.Owner, imageIds, args.Name, args.Offset, args.Limit)
if err != nil {
return err
}
printList(images, total, args.Offset, args.Limit, []string{})
return nil
})
type ImageCreateOptions struct {
NAME string `helo:"Image name"`
OSTYPE string `helo:"Operation system" choices:"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"`
OSARCH string `help:"OS Architecture" choices:"x86_64|i386"`
osVersion string `help:"OS Version"`
URL string `helo:"Cos URL"`
}
shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *qcloud.SRegion, args *ImageCreateOptions) error {
image, err := cli.ImportImage(args.NAME, args.OSARCH, args.OSTYPE, args.osVersion, args.URL)
if err != nil {
return err
}
printObject(image)
return nil
})
type ImageDeleteOptions struct {
ID string `helo:"Image ID"`
}
shellutils.R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *qcloud.SRegion, args *ImageDeleteOptions) error {
return cli.DeleteImage(args.ID)
})
}
+176
View File
@@ -0,0 +1,176 @@
package shell
import (
"fmt"
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type InstanceListOptions struct {
Id []string `help:"IDs of instances to show"`
Zone string `help:"Zone ID"`
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *qcloud.SRegion, args *InstanceListOptions) error {
instances, total, e := cli.GetInstances(args.Zone, args.Id, args.Offset, args.Limit)
if e != nil {
return e
}
printList(instances, total, args.Offset, args.Limit, []string{})
return nil
})
type InstanceCrateOptions struct {
NAME string `help:"name of instance"`
IMAGE string `help:"image ID"`
CPU int `help:"CPU count"`
MEMORYGB int `help:"MemoryGB"`
Disk []int `help:"Data disk sizes int GB"`
STORAGE string `help:"Storage type" choices:"LOCAL_BASIC|LOCAL_SSD|CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"`
NETWORK string `help:"Network ID"`
PASSWD string `help:"password"`
PublicKey string `help:"PublicKey"`
}
shellutils.R(&InstanceCrateOptions{}, "instance-create", "Create a instance", func(cli *qcloud.SRegion, args *InstanceCrateOptions) error {
instance, e := cli.CreateInstanceSimple(args.NAME, args.IMAGE, args.CPU, args.MEMORYGB, args.STORAGE, args.Disk, args.NETWORK, args.PASSWD, args.PublicKey)
if e != nil {
return e
}
printObject(instance)
return nil
})
type InstanceDiskOperationOptions struct {
ID string `help:"instance ID"`
DISK string `help:"disk ID"`
}
shellutils.R(&InstanceDiskOperationOptions{}, "instance-attach-disk", "Attach a disk to instance", func(cli *qcloud.SRegion, args *InstanceDiskOperationOptions) error {
err := cli.AttachDisk(args.ID, args.DISK)
if err != nil {
return err
}
return nil
})
shellutils.R(&InstanceDiskOperationOptions{}, "instance-detach-disk", "Detach a disk to instance", func(cli *qcloud.SRegion, args *InstanceDiskOperationOptions) error {
err := cli.DetachDisk(args.ID, args.DISK)
if err != nil {
return err
}
return nil
})
type InstanceOperationOptions struct {
ID string `help:"instance ID"`
}
shellutils.R(&InstanceOperationOptions{}, "instance-start", "Start a instance", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
err := cli.StartVM(args.ID)
if err != nil {
return err
}
return nil
})
shellutils.R(&InstanceOperationOptions{}, "instance-vnc", "Get a instance VNC url", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
url, err := cli.GetInstanceVNCUrl(args.ID)
if err != nil {
return err
}
fmt.Println(url)
return nil
})
type InstanceStopOptions struct {
ID string `help:"instance ID"`
Force bool `help:"Force stop instance"`
}
shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *qcloud.SRegion, args *InstanceStopOptions) error {
err := cli.StopVM(args.ID, args.Force)
if err != nil {
return err
}
return nil
})
shellutils.R(&InstanceOperationOptions{}, "instance-delete", "Delete a instance", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
err := cli.DeleteVM(args.ID)
if err != nil {
return err
}
return nil
})
/*
server-change-config 更改系统配置
server-reset
*/
type InstanceDeployOptions struct {
ID string `help:"instance ID"`
Name string `help:"new instance name"`
Hostname string `help:"new hostname"`
Keypair string `help:"Keypair Name"`
DeleteKeypair bool `help:"Remove SSH keypair"`
Password string `help:"new password"`
// ResetPassword bool `help:"Force reset password"`
Description string `help:"new instances description"`
}
shellutils.R(&InstanceDeployOptions{}, "instance-deploy", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceDeployOptions) error {
err := cli.DeployVM(args.ID, args.Name, args.Password, args.Keypair, args.DeleteKeypair, args.Description)
if err != nil {
return err
}
return nil
})
type InstanceRebuildRootOptions struct {
ID string `help:"instance ID"`
Image string `help:"Image ID"`
Password string `help:"pasword"`
Keypair string `help:"keypair name"`
Size int `help:"system disk size in GB"`
}
shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *qcloud.SRegion, args *InstanceRebuildRootOptions) error {
diskID, err := cli.ReplaceSystemDisk(args.ID, args.Image, args.Password, args.Keypair, args.Size)
if err != nil {
return err
}
fmt.Printf("New diskID is %s", diskID)
return nil
})
type InstanceChangeConfigOptions struct {
ID string `help:"instance ID"`
Ncpu int `help:"number of CPU"`
Vmem int `help:"MiB of memory"`
Disk []int `help:"Data disk sizes int GB"`
}
shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceChangeConfigOptions) error {
instance, e := cli.GetInstance(args.ID)
if e != nil {
return e
}
// todo : add create disks
err := cli.ChangeVMConfig(instance.Placement.Zone, args.ID, args.Ncpu, args.Vmem, nil)
if err != nil {
return err
}
return nil
})
type InstanceUpdatePasswordOptions struct {
ID string `help:"Instance ID"`
PASSWD string `help:"new password"`
}
shellutils.R(&InstanceUpdatePasswordOptions{}, "instance-update-password", "Update instance password", func(cli *qcloud.SRegion, args *InstanceUpdatePasswordOptions) error {
err := cli.UpdateInstancePassword(args.ID, args.PASSWD)
return err
})
}
+45
View File
@@ -0,0 +1,45 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type KeyPairListOptions struct {
Name string `help:"Keypair Name"`
IDs []string `help:"Keypari ids"`
Offset int `help:"List offset"`
Limit int `help:"List limit"`
}
shellutils.R(&KeyPairListOptions{}, "keypair-list", "List keypair", func(cli *qcloud.SRegion, args *KeyPairListOptions) error {
keypairs, total, err := cli.GetKeypairs(args.Name, args.IDs, args.Offset, args.Limit)
if err != nil {
return err
}
printList(keypairs, total, args.Offset, args.Limit, []string{})
return nil
})
type KeyPairCreateOptions struct {
NAME string `help:"Keypair Name"`
}
shellutils.R(&KeyPairCreateOptions{}, "keypair-create", "Create keypair", func(cli *qcloud.SRegion, args *KeyPairCreateOptions) error {
keypair, err := cli.CreateKeyPair(args.NAME)
if err != nil {
return err
}
printObject(keypair)
return nil
})
type KeyPairAssociateOptions struct {
KEYPAIRID string `help:"Keypair ID"`
INSTANCEID string `help:"Instance ID"`
}
shellutils.R(&KeyPairAssociateOptions{}, "keypair-associate-instance", "Attach Keypair to a instance", func(cli *qcloud.SRegion, args *KeyPairAssociateOptions) error {
return cli.AttachKeypair(args.INSTANCEID, args.KEYPAIRID)
})
}
+54
View File
@@ -0,0 +1,54 @@
package shell
import (
"fmt"
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type NetworkListOptions struct {
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&NetworkListOptions{}, "network-list", "List networks", func(cli *qcloud.SRegion, args *NetworkListOptions) error {
networks, total, e := cli.GetNetworks(nil, "", args.Offset, args.Limit)
if e != nil {
return e
}
printList(networks, total, args.Offset, args.Limit, []string{})
return nil
})
type NetworkOptions struct {
ID string `help:"Network ID"`
}
shellutils.R(&NetworkOptions{}, "network-delete", "Delete network", func(cli *qcloud.SRegion, args *NetworkOptions) error {
return cli.DeleteNetwork(args.ID)
})
shellutils.R(&NetworkOptions{}, "network-show", "Show network", func(cli *qcloud.SRegion, args *NetworkOptions) error {
network, err := cli.GetNetwork(args.ID)
if err != nil {
return err
}
printObject(network)
return nil
})
type NetworkCreateOptions struct {
ZONE string `help:"Zone ID"`
VPC string `help:"VPC ID"`
CIDR string `help:"Network CIDR"`
NAME string `help:"Network Name"`
}
shellutils.R(&NetworkCreateOptions{}, "network-create", "Create network", func(cli *qcloud.SRegion, args *NetworkCreateOptions) error {
networkId, err := cli.CreateNetwork(args.ZONE, args.VPC, args.NAME, args.CIDR, "")
if err != nil {
return err
}
fmt.Println(networkId)
return nil
})
}
+11
View File
@@ -0,0 +1,11 @@
package shell
import "yunion.io/x/onecloud/pkg/util/printutils"
func printList(data interface{}, total, offset, limit int, columns []string) {
printutils.PrintInterfaceList(data, total, offset, limit, columns)
}
func printObject(obj interface{}) {
printutils.PrintInterfaceObject(obj)
}
+16
View File
@@ -0,0 +1,16 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type RegionListOptions struct {
}
shellutils.R(&RegionListOptions{}, "region-list", "List regions", func(cli *qcloud.SRegion, args *RegionListOptions) error {
regions := cli.GetClient().GetRegions()
printList(regions, 0, 0, 0, nil)
return nil
})
}
+66
View File
@@ -0,0 +1,66 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type SecurityGroupListOptions struct {
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&SecurityGroupListOptions{}, "security-group-list", "List SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupListOptions) error {
secgrps, total, err := cli.GetSecurityGroups("", args.Limit, args.Offset)
if err != nil {
return err
}
printList(secgrps, total, args.Offset, args.Limit, []string{})
return nil
})
type SecurityGroupOptions struct {
ID string `help:"SecurityGroup ID"`
}
shellutils.R(&SecurityGroupOptions{}, "security-group-show", "Show SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error {
secgroup, err := cli.GetSecurityGroupDetails(args.ID)
if err != nil {
return err
}
printObject(secgroup)
return nil
})
shellutils.R(&SecurityGroupOptions{}, "security-group-delete", "Delete SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error {
return cli.DeleteSecurityGroup(args.ID)
})
type SecurityGroupCreateOptions struct {
NAME string `help:"SecurityGroup Name"`
Desc string `help:"SecurityGroup Description"`
}
shellutils.R(&SecurityGroupCreateOptions{}, "security-group-create", "Create SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupCreateOptions) error {
secgrp, err := cli.CreateSecurityGroup(args.NAME, args.Desc)
if err != nil {
return err
}
printObject(secgrp)
return nil
})
type AddressShowOptions struct {
Id string `help:"IP address ID"`
Name string `help:"IP address name"`
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&AddressShowOptions{}, "address-list", "Show address", func(cli *qcloud.SRegion, args *AddressShowOptions) error {
address, total, err := cli.AddressList(args.Id, args.Name, args.Offset, args.Limit)
if err != nil {
return err
}
printList(address, total, args.Offset, args.Limit, []string{})
return nil
})
}
+45
View File
@@ -0,0 +1,45 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type SnapshotListOptions struct {
DiskId string `help:"Disk ID"`
InstanceId string `help:"Instance ID"`
SnapshotIds []string `helo:"Snapshot ids"`
Name string `help:"Snapshot Name"`
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&SnapshotListOptions{}, "snapshot-list", "List snapshot", func(cli *qcloud.SRegion, args *SnapshotListOptions) error {
snapshots, total, err := cli.GetSnapshots(args.InstanceId, args.DiskId, args.Name, args.SnapshotIds, args.Offset, args.Limit)
if err != nil {
return err
}
printList(snapshots, total, args.Offset, args.Limit, []string{})
return nil
})
type SnapshotDeleteOptions struct {
ID string `help:"Snapshot ID"`
}
shellutils.R(&SnapshotDeleteOptions{}, "snapshot-delete", "Delete snapshot", func(cli *qcloud.SRegion, args *SnapshotDeleteOptions) error {
return cli.DeleteSnapshot(args.ID)
})
type SnapshotCreateOptions struct {
DISK string `help:"Disk ID"`
Name string `help:"Snapeshot Name"`
Desc string `help:"Snapshot Desc"`
}
shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "Create snapshot", func(cli *qcloud.SRegion, args *SnapshotCreateOptions) error {
_, err := cli.CreateSnapshot(args.DISK, args.Name, args.Desc)
return err
})
}
+41
View File
@@ -0,0 +1,41 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type VpcListOptions struct {
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *qcloud.SRegion, args *VpcListOptions) error {
vpcs, total, err := cli.GetVpcs(nil, args.Offset, args.Limit)
if err != nil {
return err
}
printList(vpcs, total, args.Offset, args.Limit, []string{})
return nil
})
type VpcCreateOptions struct {
NAME string `help:"Name for vpc"`
CIDR string `help:"Cidr for vpc" choices:"10.0.0.0/16|172.16.0.0/12|192.168.0.0/16"`
}
shellutils.R(&VpcCreateOptions{}, "vpc-create", "Create vpc", func(cli *qcloud.SRegion, args *VpcCreateOptions) error {
vpc, err := cli.CreateIVpc(args.NAME, "", args.CIDR)
if err != nil {
return err
}
printObject(vpc)
return nil
})
type VpcDeleteOptions struct {
ID string `help:"VPC ID or Name"`
}
shellutils.R(&VpcDeleteOptions{}, "vpc-delete", "Delete vpc", func(cli *qcloud.SRegion, args *VpcDeleteOptions) error {
return cli.DeleteVpc(args.ID)
})
}
+21
View File
@@ -0,0 +1,21 @@
package shell
import (
"yunion.io/x/onecloud/pkg/util/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type ZoneListOptions struct {
Limit int `help:"page size"`
Offset int `help:"page offset"`
}
shellutils.R(&ZoneListOptions{}, "zone-list", "List zones", func(cli *qcloud.SRegion, args *ZoneListOptions) error {
zones, err := cli.GetIZones()
if err != nil {
return err
}
printList(zones, len(zones), args.Offset, args.Limit, []string{})
return nil
})
}
+218
View File
@@ -0,0 +1,218 @@
package qcloud
import (
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SnapshotStatusType string
const (
SnapshotStatusAccomplished SnapshotStatusType = "NORMAL"
SnapshotStatusProgress SnapshotStatusType = "CREATING"
SnapshotStatusFailed SnapshotStatusType = "failed"
)
type SSnapshot struct {
region *SRegion
SnapshotId string // 快照ID。
Placement Placement // 快照所在的位置。
DiskUsage string // 创建此快照的云硬盘类型。取值范围:SYSTEM_DISK:系统盘 DATA_DISK:数据盘。
DiskId string // 创建此快照的云硬盘ID。
DiskSize int32 // 创建此快照的云硬盘大小,单位GB。
SnapshotState SnapshotStatusType // 快照的状态。取值范围: NORMAL:正常 CREATING:创建中 ROLLBACKING:回滚中 COPYING_FROM_REMOTE:跨地域复制快照拷贝中。
SnapshotName string // 快照名称,用户自定义的快照别名。调用ModifySnapshotAttribute可修改此字段。
Percent int // 快照创建进度百分比,快照创建成功后此字段恒为100。
CreateTime time.Time // 快照的创建时间。
DeadlineTime time.Time // 快照到期时间。如果快照为永久保留,此字段为空。
Encrypt bool // 是否为加密盘创建的快照。取值范围:true:该快照为加密盘创建的 false:非加密盘创建的快照。
IsPermanent bool // 是否为永久快照。取值范围: true:永久快照 false:非永久快照。
CopyingToRegions []string // 快照正在跨地域复制的目的地域,默认取值为[]。
CopyFromRemote bool // 是否为跨地域复制的快照。取值范围:true:表示为跨地域复制的快照。 false:本地域的快照。
}
func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
snapshots, total, err := self.GetSnapshots("", "", "", []string{snapshotId}, 0, 1)
if err != nil {
return nil, err
}
if total > 1 {
return nil, cloudprovider.ErrDuplicateId
}
if total == 0 {
return nil, cloudprovider.ErrNotFound
}
return &snapshots[0], nil
}
func (self *SSnapshot) GetStatus() string {
// NORMAL:正常
// CREATING:创建中
// ROLLBACKING:回滚中
// COPYING_FROM_REMOTE:跨地域复制快照拷贝中。
switch self.SnapshotState {
case "NORMAL", "COPYING_FROM_REMOTE":
return models.SNAPSHOT_READY
case "CREATING":
return models.SNAPSHOT_CREATING
case "ROLLBACKING":
return models.SNAPSHOT_ROLLBACKING
}
return models.SNAPSHOT_UNKNOWN
}
func (self *SSnapshot) IsEmulated() bool {
return false
}
func (self *SSnapshot) Refresh() error {
snapshots, total, err := self.region.GetSnapshots("", "", "", []string{self.SnapshotId}, 0, 1)
if err != nil {
return err
}
if total > 1 {
return cloudprovider.ErrDuplicateId
}
if total == 0 {
return cloudprovider.ErrNotFound
}
return jsonutils.Update(self, snapshots[0])
}
func (self *SRegion) GetSnapshots(instanceId string, diskId string, snapshotName string, snapshotIds []string, offset int, limit int) ([]SSnapshot, int, error) {
if limit > 50 || limit <= 0 {
limit = 50
}
params := make(map[string]string)
params["Limit"] = fmt.Sprintf("%d", limit)
params["Offset"] = fmt.Sprintf("%d", offset)
filter := 0
if len(instanceId) > 0 {
}
if len(diskId) > 0 {
params[fmt.Sprintf("Filters.%d.Name", filter)] = "disk-id"
params[fmt.Sprintf("Filters.%d.Values", filter)] = diskId
filter++
}
if len(snapshotName) > 0 {
params[fmt.Sprintf("Filters.%d.Name", filter)] = "snapshot-name"
params[fmt.Sprintf("Filters.%d.Values", filter)] = snapshotName
filter++
}
if snapshotIds != nil && len(snapshotIds) > 0 {
for index, snapshotId := range snapshotIds {
params[fmt.Sprintf("SnapshotIds.%d", index)] = snapshotId
}
}
snapshots := []SSnapshot{}
body, err := self.cbsRequest("DescribeSnapshots", params)
if err != nil {
log.Errorf("GetSnapshots fail %s", err)
return nil, 0, err
}
body.Unmarshal(&snapshots, "SnapshotSet")
if err != nil {
return nil, 0, err
}
total, _ := body.Float("TotalCount")
for i := 0; i < len(snapshots); i++ {
snapshots[i].region = self
}
return snapshots, int(total), nil
}
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
snapshots, total, err := self.GetSnapshots("", "", "", []string{}, 0, 50)
if err != nil {
return nil, err
}
for len(snapshots) < total {
var parts []SSnapshot
parts, total, err = self.GetSnapshots("", "", "", []string{}, len(snapshots), 50)
if err != nil {
return nil, err
}
snapshots = append(snapshots, parts...)
}
ret := make([]cloudprovider.ICloudSnapshot, len(snapshots))
for i := 0; i < len(snapshots); i++ {
ret[i] = &snapshots[i]
}
return ret, nil
}
func (self *SSnapshot) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SSnapshot) GetRegionId() string {
return self.region.GetId()
}
func (self *SSnapshot) GetManagerId() string {
return self.region.client.providerId
}
func (self *SSnapshot) GetSize() int32 {
return self.DiskSize
}
func (self *SSnapshot) GetDiskId() string {
return self.DiskId
}
func (self *SSnapshot) GetId() string {
return self.SnapshotId
}
func (self *SSnapshot) GetGlobalId() string {
return fmt.Sprintf("%s", self.SnapshotId)
}
func (self *SSnapshot) GetName() string {
return self.SnapshotName
}
func (self *SSnapshot) Delete() error {
if self.region == nil {
return fmt.Errorf("not init region for snapshot %s", self.SnapshotId)
}
return self.region.DeleteSnapshot(self.SnapshotId)
}
func (self *SSnapshot) GetDiskType() string {
switch self.DiskUsage {
case "SYSTEM_DISK":
return models.DISK_TYPE_SYS
case "DATA_DISK":
return models.DISK_TYPE_DATA
}
return models.DISK_TYPE_DATA
}
func (self *SRegion) DeleteSnapshot(snapshotId string) error {
params := map[string]string{"SnapshotIds.0": snapshotId}
_, err := self.cbsRequest("DeleteSnapshots", params)
return err
}
func (self *SRegion) CreateSnapshot(diskId, name, desc string) (string, error) {
params := make(map[string]string)
params["DiskId"] = diskId
params["SnapshotName"] = name
body, err := self.cbsRequest("CreateSnapshot", params)
if err != nil {
log.Errorf("CreateSnapshot fail %s", err)
return "", err
}
return body.GetString("SnapshotId")
}
+126
View File
@@ -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
}
+189
View File
@@ -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
}
}
+193
View File
@@ -0,0 +1,193 @@
package qcloud
import (
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/pkg/util/secrules"
)
type SVpc struct {
region *SRegion
iwires []cloudprovider.ICloudWire
secgroups []cloudprovider.ICloudSecurityGroup
CidrBlock string
CreatedTime time.Time
DhcpOptionsId string
DnsServerSet []string
DomainName string
EnableMulticast bool
IsDefault bool
VpcId string
VpcName string
}
func (self *SVpc) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SVpc) GetId() string {
return self.VpcId
}
func (self *SVpc) GetName() string {
if len(self.VpcName) > 0 {
return self.VpcName
}
return self.VpcId
}
func (self *SVpc) GetGlobalId() string {
return self.VpcId
}
func (self *SVpc) IsEmulated() bool {
return false
}
func (self *SVpc) GetIsDefault() bool {
return self.IsDefault
}
func (self *SVpc) GetCidrBlock() string {
return self.CidrBlock
}
func (self *SVpc) GetStatus() string {
return models.VPC_STATUS_AVAILABLE
}
func (self *SVpc) Delete() error {
return self.region.DeleteVpc(self.VpcId)
}
func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
secgroups := make([]SSecurityGroup, 0)
for {
parts, total, err := self.region.GetSecurityGroups(self.VpcId, len(secgroups), 50)
if err != nil {
return nil, err
}
secgroups = append(secgroups, parts...)
if len(secgroups) >= total {
break
}
}
isecgroups := make([]cloudprovider.ICloudSecurityGroup, len(secgroups))
for i := 0; i < len(secgroups); i++ {
secgroups[i].vpc = self
isecgroups[i] = &secgroups[i]
}
return isecgroups, nil
}
func (self *SVpc) getWireByZoneId(zoneId string) *SWire {
for i := 0; i <= len(self.iwires); i++ {
wire := self.iwires[i].(*SWire)
if wire.zone.Zone == zoneId {
return wire
}
}
return nil
}
func (self *SVpc) fetchNetworks() error {
networks, total, err := self.region.GetNetworks(nil, self.VpcId, 0, 50)
if err != nil {
return err
}
if total > len(networks) {
networks, _, err = self.region.GetNetworks(nil, self.VpcId, 0, total)
if err != nil {
return err
}
}
for i := 0; i < len(networks); i += 1 {
wire := self.getWireByZoneId(networks[i].Zone)
networks[i].wire = wire
wire.addNetwork(&networks[i])
}
return nil
}
func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) {
if self.iwires == nil {
err := self.fetchNetworks()
if err != nil {
return nil, err
}
}
for i := 0; i < len(self.iwires); i += 1 {
if self.iwires[i].GetGlobalId() == wireId {
return self.iwires[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) {
if self.iwires == nil {
err := self.fetchNetworks()
if err != nil {
return nil, err
}
}
return self.iwires, nil
}
func (self *SVpc) GetManagerId() string {
return self.region.client.providerId
}
func (self *SVpc) GetRegion() cloudprovider.ICloudRegion {
return self.region
}
func (self *SVpc) Refresh() error {
new, err := self.region.getVpc(self.VpcId)
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SVpc) SyncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) (string, error) {
secgrpId := ""
// if secgroup, err := self.region.getSecurityGroupByTag(self.VpcId, secgroupId); err != nil {
// if secgrpId, err = self.region.createSecurityGroup(self.VpcId, name, ""); err != nil {
// return "", err
// } else if err := self.region.addTagToSecurityGroup(secgrpId, "id", secgroupId, 1); err != nil {
// return "", err
// }
// //addRules
// log.Debugf("Add Rules for %s", secgrpId)
// for _, rule := range rules {
// if err := self.region.addSecurityGroupRule(secgrpId, &rule); err != nil {
// return "", err
// }
// }
// } else {
// //syncRules
// secgrpId = secgroup.SecurityGroupId
// log.Debugf("Sync Rules for %s", secgroup.GetName())
// if secgroup.GetName() != name {
// if err := self.region.modifySecurityGroup(secgrpId, name, ""); err != nil {
// log.Errorf("Change SecurityGroup name to %s failed: %v", name, err)
// }
// }
// self.region.syncSecgroupRules(secgrpId, rules)
// }
return secgrpId, nil
}
func (self *SVpc) addWire(wire *SWire) {
if self.iwires == nil {
self.iwires = make([]cloudprovider.ICloudWire, 0)
}
self.iwires = append(self.iwires, wire)
}
+142
View File
@@ -0,0 +1,142 @@
package qcloud
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
type SWire struct {
zone *SZone
vpc *SVpc
inetworks []cloudprovider.ICloudNetwork
}
func (self *SWire) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SWire) GetId() string {
return fmt.Sprintf("%s-%s", self.vpc.GetId(), self.zone.GetId())
}
func (self *SWire) GetName() string {
return self.GetId()
}
func (self *SWire) IsEmulated() bool {
return true
}
func (self *SWire) GetStatus() string {
return "available"
}
func (self *SWire) Refresh() error {
return nil
}
func (self *SWire) GetGlobalId() string {
return fmt.Sprintf("%s-%s", self.vpc.GetGlobalId(), self.zone.GetGlobalId())
}
func (self *SWire) GetIVpc() cloudprovider.ICloudVpc {
return self.vpc
}
func (self *SWire) GetIZone() cloudprovider.ICloudZone {
return self.zone
}
func (self *SWire) GetBandwidth() int {
return 10000
}
func (self *SWire) GetNetworkById(networkId string) *SNetwork {
networks, err := self.GetINetworks()
if err != nil {
return nil
}
log.Debugf("search for networks %d", len(networks))
for i := 0; i < len(networks); i += 1 {
log.Debugf("search %s", networks[i].GetName())
network := networks[i].(*SNetwork)
if network.SubnetId == networkId {
return network
}
}
return nil
}
func (self *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
networkId, err := self.zone.region.CreateNetwork(self.zone.Zone, self.vpc.VpcId, name, cidr, desc)
if err != nil {
log.Errorf("CreateNetwork error %s", err)
return nil, err
}
self.inetworks = nil
network := self.GetNetworkById(networkId)
if network == nil {
log.Errorf("cannot find vswitch after create????")
return nil, cloudprovider.ErrNotFound
}
return network, nil
}
func (self *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) {
networks, err := self.GetINetworks()
if err != nil {
return nil, err
}
for i := 0; i < len(networks); i += 1 {
if networks[i].GetGlobalId() == netid {
return networks[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SWire) getNetworkById(networkId string) *SNetwork {
networks, err := self.GetINetworks()
if err != nil {
return nil
}
log.Debugf("search for networks %d", len(networks))
for i := 0; i < len(networks); i += 1 {
log.Debugf("search %s", networks[i].GetName())
network := networks[i].(*SNetwork)
if network.SubnetId == networkId {
return network
}
}
return nil
}
func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) {
if self.inetworks == nil {
err := self.vpc.fetchNetworks()
if err != nil {
return nil, err
}
}
return self.inetworks, nil
}
func (self *SWire) addNetwork(network *SNetwork) {
if self.inetworks == nil {
self.inetworks = make([]cloudprovider.ICloudNetwork, 0)
}
find := false
for i := 0; i < len(self.inetworks); i += 1 {
if self.inetworks[i].GetId() == network.SubnetId {
find = true
break
}
}
if !find {
self.inetworks = append(self.inetworks, network)
}
}
+202
View File
@@ -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
}
@@ -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
View File
@@ -0,0 +1 @@
module github.com/konsorten/go-windows-terminal-sequences
+2 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,5 @@
// +build go1.11,!windows
// +build go1.11
// +build aix darwin dragonfly freebsd linux netbsd openbsd
package dns
+1 -1
View File
@@ -1,4 +1,4 @@
// +build !go1.11 windows
// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
package dns
+13 -38
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,3 +1,5 @@
// +build !windows
package dns
import (
+37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,8 @@
// +build !windows
package logrus
import "io"
func initTerminal(w io.Writer) {
}
+6 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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