Merge pull request #4428 from tb365/feature/tb-ctyun-operations

ctyun operations
This commit is contained in:
yunion-ci-robot
2020-01-16 22:54:08 +08:00
committed by GitHub
22 changed files with 1526 additions and 99 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ type ImageOptionalOptions struct {
OsLang string `help:"OS Language" choices:"zh_CN|en_US"`
Preference int64 `help:"Disk preferences"`
Notes string `help:"Notes about the image"`
Hypervisor []string `help:"Prefer hypervisor type" choices:"kvm|esxi|baremetal|container|openstack"`
Hypervisor []string `help:"Prefer hypervisor type" choices:"kvm|esxi|baremetal|container|openstack|ctyun"`
DiskDriver string `help:"Perfer disk driver" choices:"virtio|scsi|pvscsi|ide|sata"`
NetDriver string `help:"Preferred network driver" choices:"virtio|e1000|vmxnet3"`
}
-2
View File
@@ -679,8 +679,6 @@ yunion.io/x/pkg v0.0.0-20200103043034-27c6f82160fa h1:+7zYi8MhaOW/53/7FOERnhQqAU
yunion.io/x/pkg v0.0.0-20200103043034-27c6f82160fa/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
yunion.io/x/sqlchemy v0.0.0-20191226074733-6eb73845bfb7 h1:nK047S9fIjIBkB9Yyo2e6itUA7TpmFsd7/a6mGKjNU0=
yunion.io/x/sqlchemy v0.0.0-20191226074733-6eb73845bfb7/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
yunion.io/x/sqlchemy v0.0.0-20200114051901-dfdc01cac3c3 h1:4OA/7udM060GPhj1hqX80Tu+lC2ziAcMrKpo+EG5LZA=
yunion.io/x/sqlchemy v0.0.0-20200114051901-dfdc01cac3c3/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
yunion.io/x/structarg v0.0.0-20190809075558-115bed041de3 h1:bfC8EhXYvyGYldRWlzxiCM39Zfj3s3+zham9mW2h2LE=
@@ -634,6 +634,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx conte
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil, nil
}
log.Errorf("ihost.GetIVMById fail %s", err)
return nil, err
}
+214 -20
View File
@@ -16,10 +16,13 @@ package ctyun
import (
"context"
"fmt"
"strconv"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
@@ -257,13 +260,44 @@ func (self *SDisk) GetAccessPath() string {
}
func (self *SDisk) Delete(ctx context.Context) error {
return cloudprovider.ErrNotImplemented
_, err := self.storage.zone.region.DeleteDisk(self.GetId())
if err != nil {
return errors.Wrap(err, "SDisk.Delete")
}
return nil
}
func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) {
return nil, cloudprovider.ErrNotImplemented
jobId, err := self.storage.zone.region.CreateSnapshot(name, self.GetId(), desc)
if err != nil {
return nil, errors.Wrap(err, "Disk.CreateISnapshot.CreateSnapshot")
}
snapshotId := ""
err = cloudprovider.Wait(10*time.Second, 1800*time.Second, func() (b bool, err error) {
statusJson, err := self.storage.zone.region.GetVbsJob(jobId)
if err != nil {
return false, err
}
if status, _ := statusJson.GetString("status"); status == "SUCCESS" {
snapshotId, _ = statusJson.GetString("entities", "snapshot_id")
return true, nil
} else if status == "FAILED" {
return false, fmt.Errorf("CreateSnapshot job %s failed", jobId)
} else {
return false, nil
}
})
if err != nil {
return nil, errors.Wrap(err, "Disk.CreateISnapshot.Wait")
}
return self.storage.zone.region.GetSnapshot(self.GetId(), snapshotId)
}
// POST http://ctyun-api-url/apiproxy/v3/ondemand/createVBS
func (self *SDisk) GetISnapshot(idStr string) (cloudprovider.ICloudSnapshot, error) {
return self.storage.zone.region.GetSnapshot(self.GetId(), idStr)
}
@@ -289,15 +323,61 @@ func (self *SDisk) GetExtSnapshotPolicyIds() ([]string, error) {
}
func (self *SDisk) Resize(ctx context.Context, newSizeMB int64) error {
return cloudprovider.ErrNotImplemented
jobId, err := self.storage.zone.region.ResizeDisk(self.GetId(), strconv.Itoa(int(newSizeMB/1024)))
if err != nil {
return errors.Wrap(err, "Disk.Resize")
}
err = cloudprovider.Wait(10*time.Second, 1800*time.Second, func() (b bool, err error) {
statusJson, err := self.storage.zone.region.GetVbsJob(jobId)
if err != nil {
return false, err
}
if status, _ := statusJson.GetString("status"); status == "SUCCESS" {
return true, nil
} else if status == "FAILED" {
return false, fmt.Errorf("Resize job %s failed", jobId)
} else {
return false, nil
}
})
if err != nil {
return errors.Wrap(err, "Disk.Resize.Wait")
}
return nil
}
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
return "", cloudprovider.ErrNotImplemented
jobId, err := self.storage.zone.region.RestoreDisk(self.GetId(), snapshotId)
if err != nil {
return "", errors.Wrap(err, "Disk.Reset")
}
err = cloudprovider.Wait(10*time.Second, 1800*time.Second, func() (b bool, err error) {
statusJson, err := self.storage.zone.region.GetVbsJob(jobId)
if err != nil {
return false, err
}
if status, _ := statusJson.GetString("status"); status == "SUCCESS" {
return true, nil
} else if status == "FAILED" {
return false, fmt.Errorf("Reset job %s failed", jobId)
} else {
return false, nil
}
})
if err != nil {
return "", errors.Wrap(err, "Disk.Reset.Wait")
}
return self.GetId(), nil
}
func (self *SDisk) Rebuild(ctx context.Context) error {
return cloudprovider.ErrNotImplemented
return cloudprovider.ErrNotSupported
}
func (self *SDisk) GetDiskDetails() (*DiskDetails, error) {
@@ -414,26 +494,140 @@ func (self *SRegion) CreateDisk(zoneId, name, diskType, size string) (*SDisk, er
"createVolumeInfo": diskParams,
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createVolume", params)
disks, err := self.GetDisks()
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateDisk.GetDisks")
}
diskIds := []string{}
for i := range disks {
diskIds = append(diskIds, disks[i].GetId())
}
_, err = self.client.DoPost("/apiproxy/v3/ondemand/createVolume", params)
if err != nil {
return nil, errors.Wrap(err, "Region.CreateDisk.DoPost")
}
disk := &SDisk{}
err = resp.Unmarshal(disk)
if err != nil {
return nil, errors.Wrap(err, "Region.CreateDisk.Unmarshal")
}
// 查询job结果一直报错,目前先用替代办法查找新硬盘ID,可能不准确。后续需要替换其它方法
diskId := ""
cloudprovider.Wait(3*time.Second, 300*time.Second, func() (b bool, err error) {
disks, err := self.GetDisks()
if err != nil {
return false, err
}
izone, err := self.GetIZoneById(disk.AvailabilityZone)
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateDisk.GetIZoneById")
}
for i := range disks {
if !utils.IsInStringArray(disks[i].GetId(), diskIds) {
diskId = disks[i].GetId()
return true, nil
}
}
disk.storage = &SStorage{
zone: izone.(*SZone),
storageType: disk.VolumeType,
}
return false, nil
})
return disk, nil
return self.GetDisk(diskId)
}
func (self *SRegion) CreateSnapshot(name, volumeId, desc string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"volumeId": jsonutils.NewString(volumeId),
"name": jsonutils.NewString(name),
"description": jsonutils.NewString(desc),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createVBS", params)
if err != nil {
return "", errors.Wrap(err, "Region.CreateSnapshot.DoPost")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "Region.CreateSnapshot.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) DeleteDisk(volumeId string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"volumeId": jsonutils.NewString(volumeId),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/deleteVolume", params)
if err != nil {
msg, _ := resp.GetString("message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.DeleteDisk.DoPost")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.DeleteDisk.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) ResizeDisk(volumeId string, newSizeGB string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"volumeId": jsonutils.NewString(volumeId),
"newSize": jsonutils.NewString(newSizeGB),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/expandVolumeSize", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.ResizeDisk.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.ResizeDisk.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.ResizeDisk.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) RestoreDisk(volumeId, backupId string) (string, error) {
diskBackupParams := jsonutils.NewDict()
diskBackupParams.Set("regionId", jsonutils.NewString(self.GetId()))
diskBackupParams.Set("backupId", jsonutils.NewString(backupId))
diskBackupParams.Set("volumeId", jsonutils.NewString(volumeId))
params := map[string]jsonutils.JSONObject{
"diskBackup": diskBackupParams,
}
resp, err := self.client.DoPost("/apiproxy/v3/restoreDiskBackup", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.RestoreDisk.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.RestoreDisk.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.RestoreDisk.Unmarshal")
}
return jobId, nil
}
+118 -10
View File
@@ -15,6 +15,8 @@
package ctyun
import (
"fmt"
"strconv"
"strings"
"time"
@@ -185,19 +187,34 @@ func (self *SEip) GetInternetChargeType() string {
}
func (self *SEip) Delete() error {
return cloudprovider.ErrNotImplemented
return self.region.DeleteEip(self.GetId())
}
func (self *SEip) Associate(instanceId string) error {
return cloudprovider.ErrNotImplemented
nics, err := self.region.GetNics(instanceId)
if err != nil {
return errors.Wrap(err, "Eip.Associate.GetNics")
}
if len(nics) == 0 {
return errors.Wrap(fmt.Errorf("no network card found"), "Eip.Associate.GetINics")
}
return self.region.AssociateEip(self.GetId(), nics[0].PortID)
}
func (self *SEip) Dissociate() error {
return cloudprovider.ErrNotImplemented
err := self.region.DissociateEip(self.GetId())
if err != nil {
return err
}
err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 5*time.Second, 10*time.Second, 180*time.Second)
return err
}
func (self *SEip) ChangeBandwidth(bw int) error {
return cloudprovider.ErrNotImplemented
return self.region.ChangeBandwidthEip(self.GetName(), self.GetId(), strconv.Itoa(bw))
}
type Profile struct {
@@ -257,7 +274,7 @@ func (self *SRegion) GetEip(eipId string) (*SEip, error) {
}
}
func (self *SRegion) CreateEip(zoneId, name, size, shareType string) (*SEip, error) {
func (self *SRegion) CreateEip(zoneId, name, size, shareType, chargeType string) (*SEip, error) {
eipParams := jsonutils.NewDict()
eipParams.Set("regionId", jsonutils.NewString(self.GetId()))
eipParams.Set("zoneId", jsonutils.NewString(zoneId))
@@ -265,22 +282,113 @@ func (self *SRegion) CreateEip(zoneId, name, size, shareType string) (*SEip, err
eipParams.Set("type", jsonutils.NewString("5_telcom"))
eipParams.Set("size", jsonutils.NewString(size))
eipParams.Set("shareType", jsonutils.NewString(shareType))
eipParams.Set("chargeMode", jsonutils.NewString(chargeType))
params := map[string]jsonutils.JSONObject{
"createIpInfo": eipParams,
}
eip := &SEip{}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createIp", params)
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateEip.DoPost")
}
err = resp.Unmarshal(eip, "returnObj")
eipId, err := resp.GetString("returnObj", "id")
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateEip.Unmarshal")
return nil, errors.Wrap(err, "SRegion.CreateEip.GetEipId")
}
eip.region = self
return eip, nil
return self.GetEip(eipId)
}
func (self *SRegion) DeleteEip(publicIpId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"publicIpId": jsonutils.NewString(publicIpId),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/deleteIp", params)
if err != nil {
return errors.Wrap(err, "SRegion.DeleteEip.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj")
if !ok {
msg, _ := resp.GetString("message")
return errors.Wrap(fmt.Errorf(msg), "SRegion.DeleteEip.JobFailed")
}
return nil
}
// 这里networkCardId 实际指的是port id
func (self *SRegion) AssociateEip(publicIpId, networkCardId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"publicIpId": jsonutils.NewString(publicIpId),
"networkCardId": jsonutils.NewString(networkCardId),
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/bindIp", params)
if err != nil {
return errors.Wrap(err, "SRegion.AssociateEip.DoPost")
}
return nil
}
func (self *SRegion) DissociateEip(publicIpId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"publicIpId": jsonutils.NewString(publicIpId),
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/unbindIp", params)
if err != nil {
return errors.Wrap(err, "SRegion.DissociateEip.DoPost")
}
return nil
}
type SBandwidth struct {
PublicipInfo []PublicipInfo `json:"publicip_info"`
EnterpriseProjectID string `json:"enterprise_project_id"`
Name string `json:"name"`
ID string `json:"id"`
ShareType string `json:"share_type"`
Size int64 `json:"size"`
TenantID string `json:"tenant_id"`
ChargeMode string `json:"charge_mode"`
BandwidthType string `json:"bandwidth_type"`
}
type PublicipInfo struct {
PublicipType string `json:"publicip_type"`
PublicipAddress string `json:"publicip_address"`
IPVersion int64 `json:"ip_version"`
PublicipID string `json:"publicip_id"`
}
func (self *SRegion) ChangeBandwidthEip(name, publicIpId, sizeMb string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"publicIpId": jsonutils.NewString(publicIpId),
"name": jsonutils.NewString(name),
"size": jsonutils.NewString(sizeMb),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/upgradeNetwork", params)
if err != nil {
return errors.Wrap(err, "SRegion.ChangeBandwidthEip.DoPost")
}
bandwidth := SBandwidth{}
err = resp.Unmarshal(&bandwidth, "returnObj")
if err != nil {
return errors.Wrap(err, "SRegion.ChangeBandwidthEip.Unmarshal")
}
return nil
}
+49 -1
View File
@@ -16,6 +16,8 @@ package ctyun
import (
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
@@ -160,7 +162,53 @@ func (self *SHost) GetVersion() string {
}
func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
return nil, cloudprovider.ErrNotImplemented
network, err := self.zone.region.GetNetwork(desc.ExternalNetworkId)
if err != nil {
return nil, errors.Wrap(err, "Host.CreateVM.GetNetwork")
}
jobId, err := self.zone.region.CreateInstance(self.zone.GetId(), desc.Name, desc.ExternalImageId, desc.OsType, desc.SysDisk.StorageType, desc.InstanceType, network.VpcID, desc.ExternalNetworkId, desc.ExternalSecgroupId, desc.Password)
if err != nil {
return nil, errors.Wrap(err, "Host.CreateVM.CreateInstance")
}
vmId := ""
err = cloudprovider.Wait(10*time.Second, 600*time.Second, func() (b bool, err error) {
statusJson, err := self.zone.region.GetJob(jobId)
if err != nil {
if strings.Contains(err.Error(), "job fail") {
return false, err
}
return false, nil
}
if status, _ := statusJson.GetString("status"); status == "SUCCESS" {
jobs, err := statusJson.GetArray("entities", "sub_jobs")
if err != nil {
return false, err
}
if len(jobs) > 0 {
vmId, err = jobs[0].GetString("entities", "server_id")
if err != nil {
return false, err
}
} else {
return false, fmt.Errorf("CreateVM empty sub jobs")
}
return true, nil
} else if status == "FAILED" {
return false, fmt.Errorf("CreateVM job %s failed", jobId)
} else {
return false, nil
}
})
if err != nil {
return nil, errors.Wrap(err, "Disk.CreateISnapshot.Wait")
}
return self.zone.region.GetVMById(vmId)
}
func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
+1 -1
View File
@@ -98,7 +98,7 @@ func (self *SImage) GetMetadata() *jsonutils.JSONDict {
}
func (self *SImage) Delete(ctx context.Context) error {
return cloudprovider.ErrNotImplemented
return cloudprovider.ErrNotSupported
}
func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
+581 -21
View File
@@ -17,6 +17,7 @@ package ctyun
import (
"context"
"fmt"
"strconv"
"strings"
"time"
@@ -402,11 +403,18 @@ func (self *SInstance) getSecurityGroupIdsByMasterOrderId(orderId string) ([]str
}
func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
return cloudprovider.ErrNotImplemented
return self.host.zone.region.AssignSecurityGroup(self.GetId(), secgroupId)
}
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
return cloudprovider.ErrNotImplemented
for i := 0; i < len(secgroupIds); i++ {
err := self.host.zone.region.AssignSecurityGroup(self.GetId(), secgroupIds[i])
if err != nil {
return errors.Wrap(err, "Instance.SetSecurityGroups")
}
}
return nil
}
func (self *SInstance) GetHypervisor() string {
@@ -414,35 +422,144 @@ func (self *SInstance) GetHypervisor() string {
}
func (self *SInstance) StartVM(ctx context.Context) error {
return cloudprovider.ErrNotImplemented
err := self.host.zone.region.StartVM(self.GetId())
if err != nil {
return errors.Wrap(err, "Instance.StartVM")
}
return cloudprovider.WaitStatus(self, api.VM_RUNNING, 5*time.Second, 300*time.Second)
}
func (self *SInstance) StopVM(ctx context.Context, isForce bool) error {
return cloudprovider.ErrNotImplemented
err := self.host.zone.region.StopVM(self.GetId())
if err != nil {
return errors.Wrap(err, "Instance.StopVM")
}
return cloudprovider.WaitStatus(self, api.VM_READY, 5*time.Second, 300*time.Second)
}
func (self *SInstance) DeleteVM(ctx context.Context) error {
return cloudprovider.ErrNotImplemented
err := self.host.zone.region.DeleteVM(self.GetId())
if err != nil {
return errors.Wrap(err, "SInstance.DeleteVM")
}
return cloudprovider.WaitDeleted(self, 10*time.Second, 180*time.Second)
}
func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
return cloudprovider.ErrNotImplemented
return cloudprovider.ErrNotSupported
}
func (self *SInstance) UpdateUserData(userData string) error {
return cloudprovider.ErrNotImplemented
return cloudprovider.ErrNotSupported
}
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
return "", cloudprovider.ErrNotImplemented
func (self *SInstance) RebuildRoot(ctx context.Context, config *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
currentImage, err := self.GetImage()
if err != nil {
return "", errors.Wrap(err, "Instance.RebuildRoot")
}
publicKeyName := ""
if len(config.PublicKey) > 0 {
publicKeyName, err = self.host.zone.region.syncKeypair(config.PublicKey)
if err != nil {
return "", errors.Wrap(err, "Instance.RebuildRoot.syncKeypair")
}
}
jobId := ""
if currentImage.GetId() != config.ImageId {
jobId, err = self.host.zone.region.SwitchVMOs(self.GetId(), config.Password, publicKeyName, config.ImageId)
if err != nil {
return "", errors.Wrap(err, "SInstance.RebuildRoot.SwitchVMOs")
}
} else {
jobId, err = self.host.zone.region.RebuildVM(self.GetId(), config.Password, publicKeyName)
if err != nil {
return "", errors.Wrap(err, "SInstance.RebuildRoot.RebuildVM")
}
}
err = cloudprovider.Wait(10*time.Second, 1800*time.Second, func() (b bool, err error) {
statusJson, err := self.host.zone.region.GetJob(jobId)
if err != nil {
if strings.Contains(err.Error(), "job fail") {
return false, err
}
return false, nil
}
if status, _ := statusJson.GetString("status"); status == "SUCCESS" {
return true, nil
} else if status == "FAILED" {
return false, fmt.Errorf("RebuildRoot job %s failed", jobId)
} else {
return false, nil
}
})
if err != nil {
return "", errors.Wrap(err, "Instance.RebuildRoot.Wait")
}
err = self.Refresh()
if err != nil {
return "", err
}
idisks, err := self.GetIDisks()
if err != nil {
return "", err
}
if len(idisks) == 0 {
return "", fmt.Errorf("server %s has no volume attached.", self.GetId())
}
return idisks[0].GetId(), nil
}
func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
return cloudprovider.ErrNotImplemented
if len(password) == 0 {
return cloudprovider.ErrNotSupported
}
// 只支持重置密码
return self.host.zone.region.ResetVMPassword(self.GetId(), password)
}
func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
return cloudprovider.ErrNotImplemented
jobId, err := self.host.zone.region.ChangeVMConfig(self.GetId(), config.InstanceType)
if err != nil {
return errors.Wrap(err, "Instance.ChangeConfig")
}
err = cloudprovider.Wait(10*time.Second, 1800*time.Second, func() (b bool, err error) {
statusJson, err := self.host.zone.region.GetJob(jobId)
if err != nil {
if strings.Contains(err.Error(), "job fail") {
return false, err
}
return false, nil
}
if status, _ := statusJson.GetString("status"); status == "SUCCESS" {
return true, nil
} else if status == "FAILED" {
return false, fmt.Errorf("ChangeConfig job %s failed", jobId)
} else {
return false, nil
}
})
if err != nil {
return errors.Wrap(err, "Instance.ChangeConfig.Wait")
}
return nil
}
// http://ctyun-api-url/apiproxy/v3/queryVncUrl
@@ -458,20 +575,113 @@ func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
return ret, nil
}
func (self *SInstance) NextDeviceName() (string, error) {
details, err := self.GetDetails()
if err != nil {
return "", errors.Wrap(err, "SInstance.NextDeviceName.GetDetails")
}
disks := []*SDisk{}
for i := range details.Volumes {
disk, err := self.host.zone.region.GetDisk(details.Volumes[i].ID)
if err != nil {
return "", errors.Wrap(err, "SInstance.NextDeviceName.GetDisk")
}
disks = append(disks, disk)
}
prefix := "s"
if len(disks) > 0 && strings.Contains(disks[0].GetMountpoint(), "/vd") {
prefix = "v"
}
currents := []string{}
for _, disk := range disks {
currents = append(currents, strings.ToLower(disk.GetMountpoint()))
}
for i := 0; i < 25; i++ {
device := fmt.Sprintf("/dev/%sd%s", prefix, string(98+i))
if ok, _ := utils.InStringArray(device, currents); !ok {
return device, nil
}
}
return "", fmt.Errorf("disk devicename out of index, current deivces: %s", currents)
}
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
return cloudprovider.ErrNotImplemented
device, err := self.NextDeviceName()
if err != nil {
return errors.Wrap(err, "Instance.AttachDisk.NextDeviceName")
}
_, err = self.host.zone.region.AttachDisk(self.GetId(), diskId, device)
if err != nil {
return errors.Wrap(err, "Instance.AttachDisk")
}
disk, err := self.host.zone.region.GetDisk(diskId)
if err != nil {
return errors.Wrap(err, "AttachDisk.GetDisk")
}
err = cloudprovider.WaitStatusWithDelay(disk, api.DISK_READY, 10*time.Second, 5*time.Second, 180*time.Second)
if err != nil {
return errors.Wrap(err, "Instance.DetachDisk.WaitStatusWithDelay")
}
if disk.Status != "in-use" {
return errors.Wrap(fmt.Errorf("disk status %s", disk.Status), "Instance.DetachDisk.Status")
}
return nil
}
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
return cloudprovider.ErrNotImplemented
disk, err := self.host.zone.region.GetDisk(diskId)
if err != nil {
return errors.Wrap(err, "DetachDisk.Wait")
}
if len(disk.Attachments) == 0 {
return errors.Wrap(err, "Instance.DetachDisk")
}
_, err = self.host.zone.region.DetachDisk(self.GetId(), diskId, disk.Attachments[0].Device)
if err != nil {
return errors.Wrap(err, "Instance.DetachDisk")
}
err = cloudprovider.WaitStatusWithDelay(disk, api.DISK_READY, 10*time.Second, 5*time.Second, 180*time.Second)
if err != nil {
return errors.Wrap(err, "Instance.DetachDisk.WaitStatusWithDelay")
}
if disk.Status != "available" {
return errors.Wrap(fmt.Errorf("disk status %s", disk.Status), "Instance.DetachDisk.Status")
}
return nil
}
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
return cloudprovider.ErrNotImplemented
_, err := self.host.zone.region.CreateDisk(self.host.zone.GetId(), uuid, driver, strconv.Itoa(sizeMb))
if err != nil {
return errors.Wrap(err, "Instance.CreateDisk")
}
return err
}
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
return cloudprovider.ErrNotImplemented
_, err := self.host.zone.region.RenewVM(self.GetId(), &bc)
if err != nil {
return errors.Wrap(err, "Instance.Renew.RenewVM")
}
return nil
}
func (self *SInstance) GetError() error {
@@ -549,7 +759,11 @@ func (self *SRegion) GetInstanceVNCUrl(vmId string) (string, error) {
return ret.URL, nil
}
func (self *SRegion) CreateInstance(zoneId, name, imageId, volumetype, flavorRef, vpcid, subnetId, secGroupId, adminPass string) error {
/*
创建主机接口目前没有绑定密钥的参数选项,不支持绑定密码。
但是重装系统接口支持绑定密钥
*/
func (self *SRegion) CreateInstance(zoneId, name, imageId, osType, volumetype, flavorRef, vpcid, subnetId, secGroupId, adminPass string) (string, error) {
rootParams := jsonutils.NewDict()
rootParams.Set("volumetype", jsonutils.NewString(volumetype))
@@ -572,8 +786,7 @@ func (self *SRegion) CreateInstance(zoneId, name, imageId, volumetype, flavorRef
serverParams.Set("imageRef", jsonutils.NewString(imageId))
serverParams.Set("root_volume", rootParams)
serverParams.Set("flavorRef", jsonutils.NewString(flavorRef))
// todo: fix me
serverParams.Set("osType", jsonutils.NewString("Linux"))
serverParams.Set("osType", jsonutils.NewString(osType))
serverParams.Set("vpcid", jsonutils.NewString(vpcid))
serverParams.Set("security_groups", secgroupParams)
serverParams.Set("nics", nicParams)
@@ -588,12 +801,25 @@ func (self *SRegion) CreateInstance(zoneId, name, imageId, volumetype, flavorRef
"createVMInfo": vmParams,
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/createVM", params)
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createVM", params)
if err != nil {
return errors.Wrap(err, "SRegion.CreateInstance.DoPost")
return "", errors.Wrap(err, "SRegion.CreateInstance.DoPost")
}
return nil
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.CreateInstance.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.CreateInstance.Unmarshal")
}
return jobId, nil
}
// vm & nic job
@@ -637,3 +863,337 @@ func (self *SRegion) GetVbsJob(jobId string) (jsonutils.JSONObject, error) {
return ret, nil
}
// POST http://ctyun-api-url/apiproxy/v3/addSecurityGroup 绑定安全组
func (self *SRegion) AssignSecurityGroup(vmId, securityGroupRuleId string) error {
securityParams := jsonutils.NewDict()
securityParams.Set("regionId", jsonutils.NewString(self.GetId()))
securityParams.Set("vmId", jsonutils.NewString(vmId))
securityParams.Set("securityGroupRuleId", jsonutils.NewString(securityGroupRuleId))
params := map[string]jsonutils.JSONObject{
"securityGroup": securityParams,
}
_, err := self.client.DoPost("/apiproxy/v3/addSecurityGroup", params)
if err != nil {
return errors.Wrap(err, "SRegion.AssignSecurityGroup.DoPost")
}
return nil
}
// POST http://ctyun-api-url/apiproxy/v3/removeSecurityGroup 解绑安全组
func (self *SRegion) UnsignSecurityGroup(vmId, securityGroupRuleId string) error {
securityParams := jsonutils.NewDict()
securityParams.Set("regionId", jsonutils.NewString(self.GetId()))
securityParams.Set("vmId", jsonutils.NewString(vmId))
securityParams.Set("securityGroupRuleId", jsonutils.NewString(securityGroupRuleId))
params := map[string]jsonutils.JSONObject{
"securityGroup": securityParams,
}
_, err := self.client.DoPost("/apiproxy/v3/removeSecurityGroup", params)
if err != nil {
return errors.Wrap(err, "SRegion.UnsignSecurityGroup.DoPost")
}
return nil
}
func (self *SRegion) StartVM(vmId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/startVM", params)
if err != nil {
return errors.Wrap(err, "SRegion.StartVm.DoPost")
}
return nil
}
func (self *SRegion) StopVM(vmId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/stopVM", params)
if err != nil {
return errors.Wrap(err, "SRegion.StopVM.DoPost")
}
return nil
}
func (self *SRegion) DeleteVM(vmId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/deleteVM", params)
if err != nil {
return errors.Wrap(err, "SRegion.DeleteVM.DoPost")
}
return nil
}
func (self *SRegion) RestartVM(vmId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
"type": jsonutils.NewString("SOFT"),
}
_, err := self.client.DoPost("/apiproxy/v3/ondemand/restartVM", params)
if err != nil {
return errors.Wrap(err, "SRegion.RestartVM.DoPost")
}
return nil
}
func (self *SRegion) SwitchVMOs(vmId, adminPass, keyName, imageRef string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
"imageRef": jsonutils.NewString(imageRef),
}
if len(keyName) > 0 {
params["keyName"] = jsonutils.NewString(keyName)
} else if len(adminPass) > 0 {
params["adminPass"] = jsonutils.NewString(adminPass)
} else {
return "", errors.Wrap(fmt.Errorf("require public key or password"), "SRegion.SwitchVMOs")
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/switchSys", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.SwitchVMOs.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.SwitchVMOs.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.SwitchVMOs.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) RebuildVM(vmId, adminPass, keyName string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
}
if len(keyName) > 0 {
params["keyName"] = jsonutils.NewString(keyName)
} else if len(adminPass) > 0 {
params["adminPass"] = jsonutils.NewString(adminPass)
} else {
return "", errors.Wrap(fmt.Errorf("require public key or password"), "SRegion.RebuildVM")
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/reInstallSys", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.RebuildVM.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.RebuildVM.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.RebuildVM.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) AttachDisk(vmId, volumeId, device string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"volumeId": jsonutils.NewString(volumeId),
"vmId": jsonutils.NewString(vmId),
"device": jsonutils.NewString(device),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/attachVolume", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.AttachDisk.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.AttachDisk.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.AttachDisk.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) DetachDisk(vmId, volumeId, device string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"volumeId": jsonutils.NewString(volumeId),
"vmId": jsonutils.NewString(vmId),
"device": jsonutils.NewString(device),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/uninstallVolume", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.DetachDisk.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.DetachDisk.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.DetachDisk.Unmarshal")
}
return jobId, nil
}
func (self *SRegion) ChangeVMConfig(vmId, flavorId string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
"flavorId": jsonutils.NewString(flavorId),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/upgradeVM", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.ChangeVMConfig.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return "", errors.Wrap(fmt.Errorf(msg), "SRegion.ChangeVMConfig.JobFailed")
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.ChangeVMConfig.Unmarshal")
}
return jobId, nil
}
// POST http://ctyun-api-url/apiproxy/v3/order/placeRenewOrder 续订
func (self *SRegion) RenewVM(vmId string, bc *billing.SBillingCycle) ([]string, error) {
if bc == nil {
return nil, errors.Wrap(fmt.Errorf("SBillingCycle is nil"), "Region.RenewVM")
}
resourcePackage := jsonutils.NewDict()
month := bc.GetMonths()
switch {
case month <= 11:
resourcePackage.Set("cycleCount", jsonutils.NewString(strconv.Itoa(month)))
resourcePackage.Set("cycleType", jsonutils.NewString("3"))
case month == 12:
resourcePackage.Set("cycleCount", jsonutils.NewString("1"))
resourcePackage.Set("cycleType", jsonutils.NewString("5"))
case month == 24:
resourcePackage.Set("cycleCount", jsonutils.NewString("1"))
resourcePackage.Set("cycleType", jsonutils.NewString("6"))
case month == 36:
resourcePackage.Set("cycleCount", jsonutils.NewString("1"))
resourcePackage.Set("cycleType", jsonutils.NewString("7"))
default:
return nil, errors.Wrap(fmt.Errorf("unsupported month duration %d. expected 1~11, 12, 24, 36", month), "Region.RenewVM")
}
vmIds := jsonutils.NewArray()
vmIds.Add(jsonutils.NewString(vmId))
resourcePackage.Set("resourceIds", vmIds)
params := map[string]jsonutils.JSONObject{
"resourceDetailJson": resourcePackage,
}
resp, err := self.client.DoPost("/apiproxy/v3/order/placeRenewOrder", params)
if err != nil {
return nil, errors.Wrap(err, "SRegion.RenewVM.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "submitted")
if !ok {
msg, _ := resp.GetString("returnObj", "message")
return nil, errors.Wrap(fmt.Errorf(msg), "SRegion.RenewVM.JobFailed")
}
type OrderPlacedEventsElement struct {
ErrorMessage string `json:"errorMessage"`
Submitted bool `json:"submitted"`
NewOrderID string `json:"newOrderId"`
NewOrderNo string `json:"newOrderNo"`
TotalPrice int64 `json:"totalPrice"`
}
orders := []OrderPlacedEventsElement{}
err = resp.Unmarshal(&orders, "returnObj", "orderPlacedEvents")
if err != nil {
return nil, errors.Wrap(err, "SRegion.RenewVM.Unmarshal")
}
orderIds := []string{}
for i := range orders {
orderIds = append(orderIds, orders[i].NewOrderID)
}
return orderIds, nil
}
func (self *SRegion) ResetVMPassword(vmId, password string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vmId": jsonutils.NewString(vmId),
"password": jsonutils.NewString(password),
}
_, err := self.client.DoPost("/apiproxy/v3/resetVmPassword", params)
if err != nil {
return errors.Wrap(err, "SRegion.ResetVMPassword.DoPost")
}
return nil
}
+135 -1
View File
@@ -14,6 +14,140 @@
package ctyun
// http://ctyun-api-url/apiproxy/v3/order/getZoneConfig
import (
"fmt"
"strconv"
"time"
"github.com/aokoli/goutils"
"golang.org/x/crypto/ssh"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
)
// GET http://ctyun-api-url/apiproxy/v3/querySSH
// POST http://ctyun-api-url/apiproxy/v3/deleteSSH
type SKeypair struct {
Fingerprint string `json:"fingerprint"`
Name string `json:"name"`
PublicKey string `json:"public_key"`
}
func (self *SRegion) getFingerprint(publicKey string) (string, error) {
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey))
if err != nil {
return "", fmt.Errorf("publicKey error %s", err)
}
return ssh.FingerprintSHA256(pk), nil
}
// GET http://ctyun-api-url/apiproxy/v3/querySSH
func (self *SRegion) GetKeypairs() ([]SKeypair, int, error) {
params := map[string]string{
"regionId": self.GetId(),
}
resp, err := self.client.DoGet("/apiproxy/v3/querySSH", params)
if err != nil {
return nil, 0, errors.Wrap(err, "SRegion.GetKeypairs.DoGet")
}
keypairs := []jsonutils.JSONObject{}
err = resp.Unmarshal(&keypairs, "returnObj", "keypairs")
if err != nil {
return nil, 0, errors.Wrap(err, "SRegion.GetKeypairs.Unmarshal")
}
ret := []SKeypair{}
for i := range keypairs {
k, err := keypairs[i].Get("keypair")
if err != nil {
return nil, 0, errors.Wrap(err, "SRegion.GetKeypairs")
}
keypair := SKeypair{}
err = k.Unmarshal(&keypair)
if err != nil {
return nil, 0, errors.Wrap(err, "SRegion.GetKeypairs.Unmarshal")
}
ret = append(ret, keypair)
}
return ret, len(ret), nil
}
func (self *SRegion) GetKeypair(name string) (*SKeypair, error) {
keypairs, _, err := self.GetKeypairs()
if err != nil {
return nil, errors.Wrap(err, "SRegion.GetKeypair.GetKeypairs")
}
for i := range keypairs {
if keypairs[i].Name == name {
return &keypairs[i], nil
}
}
return nil, errors.Wrap(errors.ErrNotFound, "SRegion.GetKeypair")
}
func (self *SRegion) lookUpKeypair(publicKey string) (string, error) {
keypairs, _, err := self.GetKeypairs()
if err != nil {
return "", err
}
fingerprint, err := self.getFingerprint(publicKey)
if err != nil {
return "", err
}
for _, keypair := range keypairs {
if keypair.Fingerprint == fingerprint {
return keypair.Name, nil
}
}
return "", fmt.Errorf("keypair not found %s", err)
}
// POST http://ctyun-api-url/apiproxy/v3/createSSH
func (self *SRegion) ImportKeypair(name, publicKey string) (*SKeypair, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"name": jsonutils.NewString(name),
"publicKey": jsonutils.NewString(publicKey),
}
_, err := self.client.DoPost("/apiproxy/v3/createSSH", params)
if err != nil {
return nil, errors.Wrap(err, "SRegion.ImportKeypair.DoPost")
}
return self.GetKeypair(name)
}
func (self *SRegion) importKeypair(publicKey string) (string, error) {
prefix, e := goutils.RandomAlphabetic(6)
if e != nil {
return "", fmt.Errorf("publicKey error %s", e)
}
name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
if k, e := self.ImportKeypair(name, publicKey); e != nil {
return "", fmt.Errorf("keypair import error %s", e)
} else {
return k.Name, nil
}
}
func (self *SRegion) syncKeypair(publicKey string) (string, error) {
name, e := self.lookUpKeypair(publicKey)
if e == nil {
return name, nil
}
return self.importKeypair(publicKey)
}
+53 -7
View File
@@ -15,6 +15,10 @@
package ctyun
import (
"fmt"
"strconv"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
@@ -133,7 +137,7 @@ func (self *SNetwork) GetPublicScope() rbacutils.TRbacScope {
}
func (self *SNetwork) Delete() error {
return cloudprovider.ErrNotImplemented
return self.vpc.region.DeleteNetwork(self.vpc.GetId(), self.GetId())
}
func (self *SNetwork) GetAllocTimeoutSeconds() int {
@@ -218,13 +222,18 @@ func (self *SRegion) GetNetwork(subnetId string) (*SNetwork, error) {
return network, err
}
func (self *SRegion) CreateNetwork(vpcId, zoneId, name, cidr, gatewayIp, dhcpEnable string) (*SNetwork, error) {
func (self *SRegion) CreateNetwork(vpcId, zoneId, name, cidr, dhcpEnable string) (*SNetwork, error) {
gateway, err := getDefaultGateWay(cidr)
if err != nil {
return nil, err
}
networkParams := jsonutils.NewDict()
networkParams.Set("regionId", jsonutils.NewString(self.GetId()))
networkParams.Set("zoneId", jsonutils.NewString(zoneId))
networkParams.Set("name", jsonutils.NewString(name))
networkParams.Set("cidr", jsonutils.NewString(cidr))
networkParams.Set("gatewayIp", jsonutils.NewString(gatewayIp))
networkParams.Set("gatewayIp", jsonutils.NewString(gateway))
networkParams.Set("dhcpEnable", jsonutils.NewString(dhcpEnable))
networkParams.Set("vpcId", jsonutils.NewString(vpcId))
// DNS地址,如果主机需要访问公网就需要填写该值,不填写就不能使用DNS解析
@@ -235,16 +244,53 @@ func (self *SRegion) CreateNetwork(vpcId, zoneId, name, cidr, gatewayIp, dhcpEna
"jsonStr": networkParams,
}
network := &SNetwork{}
resp, err := self.client.DoPost("/apiproxy/v3/createSubnet", params)
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateNetwork.DoPost")
}
err = resp.Unmarshal(network, "returnObj")
netId, err := resp.GetString("returnObj", "id")
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateNetwork.Unmarshal")
return nil, errors.Wrap(err, "SRegion.CreateNetwork.GetString")
}
return network, err
err = cloudprovider.WaitCreated(10*time.Second, 180*time.Second, func() bool {
network, err := self.getNetwork(netId)
if err != nil {
log.Debugf("SRegion.CreateNetwork.getNetwork")
return false
}
if len(network.VpcID) == 0 {
return false
} else {
return true
}
})
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateNetwork.GetVpc")
}
return self.GetNetwork(netId)
}
func (self *SRegion) DeleteNetwork(vpcId, subnetId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vpcId": jsonutils.NewString(vpcId),
"subnetId": jsonutils.NewString(subnetId),
}
resp, err := self.client.DoPost("/apiproxy/v3/deleteSubnet", params)
if err != nil {
return errors.Wrap(err, "SRegion.DeleteNetwork.DoPost")
}
var statusCode int
err = resp.Unmarshal(&statusCode, "statusCode")
if statusCode != 800 {
return errors.Wrap(fmt.Errorf(strconv.Itoa(statusCode)), "SRegion.DeleteNetwork.Failed")
}
return nil
}
+61 -7
View File
@@ -16,6 +16,7 @@ package ctyun
import (
"fmt"
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
@@ -142,7 +143,17 @@ func (self *SRegion) GetISecurityGroupByName(vpcId string, name string) (cloudpr
}
func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
return nil, cloudprovider.ErrNotImplemented
secgroup, err := self.CreateSecurityGroup(conf.VpcId, conf.Name)
if err != nil {
return nil, errors.Wrap(err, "Region.CreateISecurityGroup")
}
err = self.syncSecgroupRules(secgroup.GetId(), conf.Rules)
if err != nil {
return nil, errors.Wrap(err, "Region.CreateISecurityGroup.syncSecgroupRules")
}
return secgroup, nil
}
func (self *SRegion) GetId() string {
@@ -257,20 +268,63 @@ func (self *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) {
return self.GetDisk(id)
}
func (self *SRegion) DeleteSecurityGroup(vpcId, secgroupId string) error {
return cloudprovider.ErrNotImplemented
func (self *SRegion) DeleteSecurityGroup(securityGroupId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"securityGroupId": jsonutils.NewString(securityGroupId),
}
resp, err := self.client.DoPost("statusCode", params)
if err != nil {
return errors.Wrap(err, "SRegion.DeleteSecurityGroup.DoPost")
}
var statusCode int
err = resp.Unmarshal(&statusCode, "statusCode")
if statusCode != 800 {
return errors.Wrap(fmt.Errorf(strconv.Itoa(statusCode)), "SRegion.DeleteSecurityGroup.JobFailed")
}
return nil
}
func (self *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) {
return "", cloudprovider.ErrNotImplemented
if len(secgroupId) > 0 {
_, err := self.GetSecurityGroupDetails(secgroupId)
if err == cloudprovider.ErrNotFound {
secgroupId = ""
} else if err != nil {
return "", errors.Wrapf(err, "self.GetSecurityGroupDetails(%s)", secgroupId)
}
}
if len(secgroupId) == 0 {
secgroup, err := self.CreateSecurityGroup(vpcId, name)
if err != nil {
return "", errors.Wrap(err, "self.CreateSecurityGroup")
}
secgroupId = secgroup.GetId()
}
rules = SecurityRuleSetToAllowSet(rules)
return secgroupId, self.syncSecgroupRules(secgroupId, rules)
}
func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) {
return nil, cloudprovider.ErrNotImplemented
return self.CreateVpc(name, cidr)
}
func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
return nil, cloudprovider.ErrNotImplemented
zones, err := self.GetIZones()
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateEIP.GetIZones")
}
if len(zones) == 0 {
return nil, errors.Wrap(errors.ErrNotFound, "SRegion.CreateEIP.GetIZones")
}
return self.CreateEip(zones[0].GetId(), eip.Name, strconv.Itoa(eip.BandwidthMbps), "PER", eip.ChargeType)
}
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
@@ -310,7 +364,7 @@ func (self *SRegion) UpdateSnapshotPolicy(*cloudprovider.SnapshotPolicyInput, st
return cloudprovider.ErrNotImplemented
}
func (self *SRegion) DeleteSnapshotPolicy(string) error {
func (self *SRegion) DeleteSnapshotPolicy(policyId string) error {
return cloudprovider.ErrNotImplemented
}
+233 -11
View File
@@ -15,20 +15,21 @@
package ctyun
import (
"fmt"
"net"
"sort"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/secrules"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
type SSecurityGroup struct {
region *SRegion
vpc *SVpc
ID string `json:"id"`
ResSecurityGroupID string `json:"resSecurityGroupId"`
Name string `json:"name"`
AccountID string `json:"accountId"`
@@ -40,12 +41,179 @@ type SSecurityGroup struct {
Status int64 `json:"status"`
}
// 将安全组规则全部转换为等价的allow规则
func SecurityRuleSetToAllowSet(srs secrules.SecurityRuleSet) secrules.SecurityRuleSet {
inRuleSet := secrules.SecurityRuleSet{}
outRuleSet := secrules.SecurityRuleSet{}
for _, rule := range srs {
if rule.Direction == secrules.SecurityRuleIngress {
inRuleSet = append(inRuleSet, rule)
}
if rule.Direction == secrules.SecurityRuleEgress {
outRuleSet = append(outRuleSet, rule)
}
}
sort.Sort(inRuleSet)
sort.Sort(outRuleSet)
inRuleSet = inRuleSet.AllowList()
// out方向空规则默认全部放行
if outRuleSet.Len() == 0 {
_, ipNet, _ := net.ParseCIDR("0.0.0.0/0")
outRuleSet = append(outRuleSet, secrules.SecurityRule{
Priority: 0,
Action: secrules.SecurityRuleAllow,
IPNet: ipNet,
Protocol: secrules.PROTO_ANY,
Direction: secrules.SecurityRuleEgress,
PortStart: -1,
PortEnd: -1,
})
}
outRuleSet = outRuleSet.AllowList()
ret := secrules.SecurityRuleSet{}
ret = append(ret, inRuleSet...)
ret = append(ret, outRuleSet...)
return ret
}
func (self *SSecurityGroup) GetRulesWithExtId() ([]secrules.SecurityRule, error) {
_rules, err := self.region.GetSecurityGroupRules(self.GetId())
if err != nil {
return nil, errors.Wrap(err, "SSecurityGroup.GetRulesWithExtId.GetSecurityGroupRules")
}
rules := make([]secrules.SecurityRule, 0)
for _, r := range _rules {
if !compatibleSecurityGroupRule(r) {
continue
}
rule, err := self.GetSecurityRule(r, true)
if err != nil {
return rules, err
}
rules = append(rules, rule)
}
return rules, nil
}
func (self *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.SecurityRule) error {
var DeleteRules []secrules.SecurityRule
var AddRules []secrules.SecurityRule
if secgroup, err := self.GetSecurityGroupDetails(secgroupId); err != nil {
return errors.Wrapf(err, "syncSecgroupRules.GetSecurityGroupDetails(%s)", secgroupId)
} else {
remoteRules, err := secgroup.GetRulesWithExtId()
if err != nil {
return errors.Wrap(err, "secgroup.GetRulesWithExtId")
}
sort.Sort(secrules.SecurityRuleSet(rules))
sort.Sort(secrules.SecurityRuleSet(remoteRules))
i, j := 0, 0
for i < len(rules) || j < len(remoteRules) {
if i < len(rules) && j < len(remoteRules) {
permissionStr := remoteRules[j].String()
ruleStr := rules[i].String()
cmp := strings.Compare(permissionStr, ruleStr)
if cmp == 0 {
// DeleteRules = append(DeleteRules, remoteRules[j])
// AddRules = append(AddRules, rules[i])
i += 1
j += 1
} else if cmp > 0 {
DeleteRules = append(DeleteRules, remoteRules[j])
j += 1
} else {
AddRules = append(AddRules, rules[i])
i += 1
}
} else if i >= len(rules) {
DeleteRules = append(DeleteRules, remoteRules[j])
j += 1
} else if j >= len(remoteRules) {
AddRules = append(AddRules, rules[i])
i += 1
}
}
}
for _, r := range DeleteRules {
// r.Description 实际存储的是ruleId
if err := self.delSecurityGroupRule(r.Description); err != nil {
log.Errorf("delSecurityGroupRule %v error: %s", r, err.Error())
return err
}
}
for _, r := range AddRules {
if err := self.addSecurityGroupRules(secgroupId, &r); err != nil {
log.Errorf("addSecurityGroupRule %v error: %s", r, err.Error())
return err
}
}
return nil
}
func (self *SRegion) delSecurityGroupRule(secGrpRuleId string) error {
return self.DeleteSecurityGroupRule(secGrpRuleId)
}
func (self *SRegion) addSecurityGroupRules(secGrpId string, rule *secrules.SecurityRule) error {
direction := ""
if rule.Direction == secrules.SecurityRuleIngress {
direction = "ingress"
} else {
direction = "egress"
}
protocal := rule.Protocol
if rule.Protocol == secrules.PROTO_ANY {
protocal = ""
}
// imcp协议默认为any
if rule.Protocol == secrules.PROTO_ICMP {
return self.addSecurityGroupRule(secGrpId, direction, "-1", "-1", protocal, rule.IPNet.String())
}
if len(rule.Ports) > 0 {
for _, port := range rule.Ports {
portStr := fmt.Sprintf("%d", port)
err := self.addSecurityGroupRule(secGrpId, direction, portStr, portStr, protocal, rule.IPNet.String())
if err != nil {
return err
}
}
} else {
portStart := fmt.Sprintf("%d", rule.PortStart)
portEnd := fmt.Sprintf("%d", rule.PortEnd)
err := self.addSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocal, rule.IPNet.String())
if err != nil {
return err
}
}
return nil
}
func (self *SSecurityGroup) SyncRules(rules []secrules.SecurityRule) error {
return cloudprovider.ErrNotImplemented
rules = SecurityRuleSetToAllowSet(rules)
return self.region.syncSecgroupRules(self.ResSecurityGroupID, rules)
}
func (self *SSecurityGroup) Delete() error {
return cloudprovider.ErrNotImplemented
return self.region.DeleteSecurityGroup(self.GetId())
}
func (self *SSecurityGroup) GetId() string {
@@ -252,10 +420,14 @@ func (self *SRegion) CreateSecurityGroup(vpcId, name string) (*SSecurityGroup, e
return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.DoPost")
}
ret := &SSecurityGroup{}
err = resp.Unmarshal(ret, "returnObj")
secgroupId, err := resp.GetString("returnObj", "id")
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.Unmarshal")
return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.GetSecgroupId")
}
secgroup, err := self.GetSecurityGroupDetails(secgroupId)
if err != nil {
return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.GetISecurityGroupById")
}
vpc, err := self.GetVpc(vpcId)
@@ -263,7 +435,57 @@ func (self *SRegion) CreateSecurityGroup(vpcId, name string) (*SSecurityGroup, e
return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.GetVpc")
}
ret.vpc = vpc
ret.region = self
return ret, nil
secgroup.vpc = vpc
secgroup.region = self
return secgroup, nil
}
func (self *SRegion) DeleteSecurityGroupRule(securityGroupRuleId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"securityGroupRuleId": jsonutils.NewString(securityGroupRuleId),
}
_, err := self.client.DoPost("/apiproxy/v3/deleteSecurityGroupRule", params)
if err != nil {
return errors.Wrap(err, "SRegion.DeleteSecurityGroupRule.DoPost")
}
return err
}
func (self *SRegion) addSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocol, ipNet string) error {
secgroupObj := jsonutils.NewDict()
secgroupObj.Add(jsonutils.NewString(self.GetId()), "regionId")
secgroupObj.Add(jsonutils.NewString(secGrpId), "securityGroupId")
secgroupObj.Add(jsonutils.NewString(direction), "direction")
secgroupObj.Add(jsonutils.NewString(ipNet), "remoteIpPrefix")
secgroupObj.Add(jsonutils.NewString("IPv4"), "ethertype")
// 端口为空或者1-65535
if len(portStart) > 0 && portStart != "0" && portStart != "-1" {
secgroupObj.Add(jsonutils.NewString(portStart), "portRangeMin")
}
if len(portEnd) > 0 && portEnd != "0" && portEnd != "-1" {
secgroupObj.Add(jsonutils.NewString(portEnd), "portRangeMax")
}
if len(protocol) > 0 {
secgroupObj.Add(jsonutils.NewString(protocol), "protocol")
}
params := map[string]jsonutils.JSONObject{
"jsonStr": secgroupObj,
}
resp, err := self.client.DoPost("/apiproxy/v3/createSecurityGroupRule", params)
if err != nil {
return errors.Wrap(err, "SRegion.DoPost")
}
rule := SSecurityGroupRule{}
err = resp.Unmarshal(&rule, "returnObj")
if err != nil {
return errors.Wrap(err, "SRegion.Unmarshal")
}
return nil
}
+6 -5
View File
@@ -32,13 +32,14 @@ func init() {
})
type EipCreateOptions struct {
ZoneId string `help:"zone id"`
Name string `help:"eip name"`
Size string `help:"size"`
ShareType string `help:"share type" choice:"PER|WHOLE"`
ZoneId string `help:"zone id"`
Name string `help:"eip name"`
Size string `help:"size"`
ShareType string `help:"share type" choice:"PER|WHOLE"`
ChargeMode string `help:"charge mode" choice:"bandwidth|traffic"`
}
shellutils.R(&EipCreateOptions{}, "eip-create", "Create eip", func(cli *ctyun.SRegion, args *EipCreateOptions) error {
eip, e := cli.CreateEip(args.ZoneId, args.Name, args.Size, args.ShareType)
eip, e := cli.CreateEip(args.ZoneId, args.Name, args.Size, args.ShareType, args.ChargeMode)
if e != nil {
return e
}
+2 -1
View File
@@ -37,6 +37,7 @@ func init() {
NAME string `help:"name of instance"`
ADMINPASS string `help:"admin password of instance"`
ImageId string `help:"image Id of instance"`
OsType string `help:"Os type of image"`
VolumeType string `help:"volume type of instance"`
Flavor string `help:"Flavor of instance"`
VpcId string `help:"Vpc of instance"`
@@ -45,7 +46,7 @@ func init() {
}
shellutils.R(&InstanceCreateOptions{}, "instance-create", "Create intance", func(cli *ctyun.SRegion, args *InstanceCreateOptions) error {
e := cli.CreateInstance(args.ZoneId, args.NAME, args.ImageId, args.VolumeType, args.Flavor, args.VpcId, args.SubnetId, args.SecGroupId, args.ADMINPASS)
_, e := cli.CreateInstance(args.ZoneId, args.NAME, args.ImageId, args.OsType, args.VolumeType, args.Flavor, args.VpcId, args.SubnetId, args.SecGroupId, args.ADMINPASS)
if e != nil {
return e
}
+1 -1
View File
@@ -41,7 +41,7 @@ func init() {
DhcpEnable string `help:"gateway ip" choice:"true|false"`
}
shellutils.R(&NetworkCreateOptions{}, "subnet-create", "Create subnet", func(cli *ctyun.SRegion, args *NetworkCreateOptions) error {
vpc, e := cli.CreateNetwork(args.VpcId, args.ZoneId, args.Name, args.Cidr, args.GatewayIp, args.DhcpEnable)
vpc, e := cli.CreateNetwork(args.VpcId, args.ZoneId, args.Name, args.Cidr, args.DhcpEnable)
if e != nil {
return e
}
+35 -2
View File
@@ -15,12 +15,13 @@
package ctyun
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
type SSnapshot struct {
@@ -111,7 +112,12 @@ func (self *SSnapshot) GetDiskType() string {
}
func (self *SSnapshot) Delete() error {
return cloudprovider.ErrNotImplemented
_, err := self.region.DeleteSnapshot(self.GetId())
if err != nil {
return errors.Wrap(err, "Snapshot.Delete.DeleteSnapshot")
}
return nil
}
func (self *SRegion) GetSnapshot(diskId string, snapshotId string) (*SSnapshot, error) {
@@ -157,3 +163,30 @@ func (self *SRegion) GetSnapshots(diskId string) ([]SSnapshot, error) {
return ret, nil
}
func (self *SRegion) DeleteSnapshot(vbsId string) (string, error) {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vbsId": jsonutils.NewString(vbsId),
}
resp, err := self.client.DoPost("/apiproxy/v3/ondemand/deleteVBS", params)
if err != nil {
return "", errors.Wrap(err, "SRegion.DeleteSnapshot.DoPost")
}
var ok bool
err = resp.Unmarshal(&ok, "returnObj", "status")
if !ok {
msg, _ := resp.GetString("message")
return "", fmt.Errorf("SRegion.DeleteSnapshot.JobFailed %s", msg)
}
var jobId string
err = resp.Unmarshal(&jobId, "returnObj", "data")
if err != nil {
return "", errors.Wrap(err, "SRegion.DeleteSnapshot.Unmarshal")
}
return jobId, nil
}
+8 -1
View File
@@ -16,9 +16,11 @@ package ctyun
import (
"fmt"
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
@@ -121,7 +123,12 @@ func (self *SStorage) GetEnabled() bool {
}
func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
return nil, cloudprovider.ErrNotImplemented
disk, err := self.zone.region.CreateDisk(self.zone.GetId(), self.GetName(), self.GetStorageType(), strconv.Itoa(sizeGb))
if err != nil {
return nil, errors.Wrap(err, "Storage.CreateIDisk.CreateDisk")
}
return disk, nil
}
func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) {
+3 -3
View File
@@ -33,15 +33,15 @@ type SStoragecache struct {
}
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
return nil, cloudprovider.ErrNotImplemented
return nil, cloudprovider.ErrNotSupported
}
func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) {
return nil, cloudprovider.ErrNotImplemented
return nil, cloudprovider.ErrNotSupported
}
func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, isForce bool) (string, error) {
return "", cloudprovider.ErrNotImplemented
return "", cloudprovider.ErrNotSupported
}
func GetBucketName(regionId string, imageId string) string {
+15 -1
View File
@@ -123,7 +123,7 @@ func (self *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) {
}
func (self *SVpc) Delete() error {
return cloudprovider.ErrNotImplemented
return self.region.DeleteVpc(self.GetId())
}
func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) {
@@ -212,3 +212,17 @@ func (self *SRegion) GetVpc(vpcId string) (*SVpc, error) {
vpc.region = self
return vpc, nil
}
func (self *SRegion) DeleteVpc(vpcId string) error {
params := map[string]jsonutils.JSONObject{
"regionId": jsonutils.NewString(self.GetId()),
"vpcId": jsonutils.NewString(vpcId),
}
_, err := self.client.DoPost("/apiproxy/v3/deleteVPC", params)
if err != nil {
return errors.Wrap(err, "SRegion.DeleteVpc.DoPost")
}
return nil
}
+6 -1
View File
@@ -107,7 +107,12 @@ func getDefaultGateWay(cidr string) (string, error) {
}
func (self *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
return nil, cloudprovider.ErrNotImplemented
network, err := self.region.CreateNetwork(self.vpc.GetId(), self.inetworks[0].(*SNetwork).ZoneID, name, cidr, "true")
if err != nil {
return nil, errors.Wrap(err, "SWire.CreateINetwork.CreateNetwork")
}
return network, nil
}
func (self *SWire) addNetwork(network *SNetwork) {
+1 -1
View File
@@ -203,7 +203,7 @@ func handleServerRemoteConsole(ctx context.Context, w http.ResponseWriter, r *ht
return
}
switch info.Protocol {
case session.ALIYUN, session.QCLOUD, session.OPENSTACK, session.VMRC, session.ZSTACK:
case session.ALIYUN, session.QCLOUD, session.OPENSTACK, session.VMRC, session.ZSTACK, session.CTYUN:
responsePublicCloudConsole(info, w)
case session.VNC, session.SPICE, session.WMKS:
handleDataSession(info, w, url.Values{"password": {info.GetPassword()}})
+2 -1
View File
@@ -32,6 +32,7 @@ const (
WMKS = "wmks"
VMRC = "vmrc"
ZSTACK = "zstack"
CTYUN = "ctyun"
)
type RemoteConsoleInfo struct {
@@ -109,7 +110,7 @@ func (info *RemoteConsoleInfo) GetConnectParams() (string, error) {
return info.getAliyunURL()
case QCLOUD:
return info.getQcloudURL()
case OPENSTACK, VMRC, ZSTACK:
case OPENSTACK, VMRC, ZSTACK, CTYUN:
return info.Url, nil
default:
return "", fmt.Errorf("Can't convert protocol %s to connect params", info.Protocol)