ucloud sync

This commit is contained in:
TangBin
2019-03-18 16:22:52 +08:00
parent 50d101de87
commit 00d4f18396
28 changed files with 3766 additions and 1 deletions
+2
View File
@@ -46,6 +46,7 @@ const (
CLOUD_PROVIDER_AWS = "Aws"
CLOUD_PROVIDER_HUAWEI = "Huawei"
CLOUD_PROVIDER_OPENSTACK = "OpenStack"
CLOUD_PROVIDER_UCLOUD = "Ucloud"
CLOUD_PROVIDER_HEALTH_NORMAL = "normal" // 远端处于健康状态
CLOUD_PROVIDER_HEALTH_SUSPENDED = "suspended" // 远端处于冻结状态
@@ -64,6 +65,7 @@ var (
CLOUD_PROVIDER_AWS,
CLOUD_PROVIDER_HUAWEI,
CLOUD_PROVIDER_OPENSTACK,
CLOUD_PROVIDER_UCLOUD,
}
)
+5
View File
@@ -143,6 +143,7 @@ const (
HYPERVISOR_AWS = "aws"
HYPERVISOR_HUAWEI = "huawei"
HYPERVISOR_OPENSTACK = "openstack"
HYPERVISOR_UCLOUD = "ucloud"
// HYPERVISOR_DEFAULT = HYPERVISOR_KVM
HYPERVISOR_DEFAULT = HYPERVISOR_KVM
@@ -161,6 +162,7 @@ var HYPERVISORS = []string{HYPERVISOR_KVM,
HYPERVISOR_QCLOUD,
HYPERVISOR_HUAWEI,
HYPERVISOR_OPENSTACK,
HYPERVISOR_UCLOUD,
}
var PUBLIC_CLOUD_HYPERVISORS = []string{
@@ -170,6 +172,7 @@ var PUBLIC_CLOUD_HYPERVISORS = []string{
HYPERVISOR_QCLOUD,
HYPERVISOR_HUAWEI,
HYPERVISOR_OPENSTACK,
HYPERVISOR_UCLOUD,
}
// var HYPERVISORS = []string{HYPERVISOR_ALIYUN}
@@ -185,6 +188,7 @@ var HYPERVISOR_HOSTTYPE = map[string]string{
HYPERVISOR_QCLOUD: HOST_TYPE_QCLOUD,
HYPERVISOR_HUAWEI: HOST_TYPE_HUAWEI,
HYPERVISOR_OPENSTACK: HOST_TYPE_OPENSTACK,
HYPERVISOR_UCLOUD: HOST_TYPE_UCLOUD,
}
var HOSTTYPE_HYPERVISOR = map[string]string{
@@ -198,6 +202,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{
HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD,
HOST_TYPE_HUAWEI: HYPERVISOR_HUAWEI,
HOST_TYPE_OPENSTACK: HYPERVISOR_OPENSTACK,
HOST_TYPE_UCLOUD: HYPERVISOR_UCLOUD,
}
type SGuestManager struct {
+1
View File
@@ -51,6 +51,7 @@ const (
HOST_TYPE_AZURE = "azure"
HOST_TYPE_HUAWEI = "huawei"
HOST_TYPE_OPENSTACK = "openstack"
HOST_TYPE_UCLOUD = "ucloud"
HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR
+5 -1
View File
@@ -62,6 +62,10 @@ const (
// openstack
STORAGE_OPENSTACK_ISCSI = "iscsi"
// Ucloud storage type
STORAGE_UCLOUD_SATA = "SATA" // 普通盘
STORAGE_UCLOUD_SSD = "SSD" // SSD盘
)
const (
@@ -91,7 +95,7 @@ var (
STORAGE_GP2_SSD, STORAGE_IO1_SSD, STORAGE_ST1_HDD, STORAGE_SC1_HDD, STORAGE_STANDARD_HDD,
STORAGE_LOCAL_BASIC, STORAGE_LOCAL_SSD, STORAGE_CLOUD_BASIC, STORAGE_CLOUD_PREMIUM,
STORAGE_HUAWEI_SSD, STORAGE_HUAWEI_SAS, STORAGE_HUAWEI_SATA,
STORAGE_OPENSTACK_ISCSI,
STORAGE_OPENSTACK_ISCSI, STORAGE_UCLOUD_SATA, STORAGE_UCLOUD_SSD,
}
STORAGE_LIMITED_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_NAS, STORAGE_RBD, STORAGE_NFS}
+1
View File
@@ -25,6 +25,7 @@ import (
_ "yunion.io/x/onecloud/pkg/util/huawei/provider"
_ "yunion.io/x/onecloud/pkg/util/openstack/provider"
_ "yunion.io/x/onecloud/pkg/util/qcloud/provider"
_ "yunion.io/x/onecloud/pkg/util/ucloud/provider"
)
func StartService() {
+174
View File
@@ -0,0 +1,174 @@
package ucloud
import (
"context"
"crypto/sha1"
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/httputils"
)
const UCLOUD_API_HOST = "https://api.ucloud.cn"
// API返回结果对应的字段名
var UCLOUD_API_RESULT_KEYS = map[string]string{
"GetProjectList": "ProjectSet",
"GetRegion": "Regions",
"DescribeVPC": "DataSet",
"DescribeImage": "ImageSet",
"DescribeIsolationGroup": "IsolationGroupSet",
"DescribeUHostInstance": "UHostSet",
"DescribeUHostTags": "TagSet",
"DescribeUDSet": "UDSet",
"DescribeUDisk": "DataSet",
"DescribeUDiskSnapshot": "DataSet",
"DescribeEIP": "EIPSet",
"DescribeFirewall": "DataSet",
"DescribeSubnet": "DataSet",
}
type SParams struct {
data jsonutils.JSONDict
}
type SUcloudError struct {
Action string `json:"Action"`
Message string `json:"Message"`
RetCode int64 `json:"RetCode"`
}
func (self *SUcloudError) Error() string {
return fmt.Sprintf("Do %s failed, code: %d, %s", self.Action, self.RetCode, self.Message)
}
func NewUcloudParams() SParams {
data := jsonutils.NewDict()
return SParams{data: *data}
}
func (self *SParams) Set(key string, value interface{}) {
switch v := value.(type) {
case string:
self.data.Set(key, jsonutils.NewString(v))
case int64:
self.data.Set(key, jsonutils.NewInt(v))
case int:
self.data.Set(key, jsonutils.NewInt(int64(v)))
case bool:
self.data.Set(key, jsonutils.NewBool(v))
case float64:
self.data.Set(key, jsonutils.NewFloat(v))
case float32:
self.data.Set(key, jsonutils.NewFloat(float64(v)))
case []string:
self.data.Set(key, jsonutils.NewStringArray(v))
default:
log.Debugf("unsuported params type %T", value)
}
}
func (self *SParams) SetAction(action string) {
self.data.Set("Action", jsonutils.NewString(action))
}
func (self *SParams) SetPagination(limit, offset int) {
if limit == 0 {
limit = 20
}
self.data.Set("Limit", jsonutils.NewInt(int64(limit)))
self.data.Set("Offset", jsonutils.NewInt(int64(offset)))
}
func (self *SParams) String() string {
return self.data.String()
}
func (self *SParams) PrettyString() string {
return self.data.PrettyString()
}
func (self *SParams) GetParams() jsonutils.JSONDict {
return self.data
}
// https://docs.ucloud.cn/api/summary/signature
func BuildParams(params SParams, privateKey string) jsonutils.JSONObject {
data := params.GetParams()
// remove old Signature
data.Remove("Signature")
// 排序并计算signture
keys := data.SortedKeys()
lst := []string{}
for _, k := range keys {
lst = append(lst, k)
v, _ := data.GetString(k)
lst = append(lst, v)
}
raw := strings.Join(lst, "") + privateKey
signture := fmt.Sprintf("%x", sha1.Sum([]byte(raw)))
data.Set("Signature", jsonutils.NewString(signture))
return &data
}
func GetSignature(params SParams, privateKey string) string {
sign, _ := BuildParams(params, privateKey).GetString("Signature")
return sign
}
func parseUcloudResponse(resp jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := &SUcloudError{}
e := resp.Unmarshal(err)
if e != nil {
return nil, e
}
if err.RetCode > 0 {
return nil, err
}
return resp, nil
}
func jsonRequest(client *SUcloudClient, params SParams) (jsonutils.JSONObject, error) {
ctx := context.Background()
MAX_RETRY := 3
retry := 0
for retry < MAX_RETRY {
_, resp, err := httputils.JSONRequest(
client.httpClient,
ctx,
httputils.POST,
UCLOUD_API_HOST,
nil,
BuildParams(params, client.accessKeySecret),
client.Debug)
if err == nil {
return parseUcloudResponse(resp)
}
switch e := err.(type) {
case *httputils.JSONClientError:
if e.Code >= 500 {
time.Sleep(3 * time.Second)
retry += 1
continue
} else {
return nil, err
}
default:
return nil, err
}
}
return nil, fmt.Errorf("timeout for request: %s", params)
}
+54
View File
@@ -0,0 +1,54 @@
package ucloud
import (
"testing"
"yunion.io/x/jsonutils"
)
func TestGetSignature(t *testing.T) {
type args struct {
params SParams
privateKey string
}
_obj, _ := jsonutils.ParseString(`{
"Password" : "VUNsb3VkLmNu",
"Region" : "cn-bj2",
"Zone" : "cn-bj2-04",
"ImageId" : "f43736e1-65a5-4bea-ad2e-8a46e18883c2",
"CPU" : 2,
"Memory" : 2048,
"DiskSpace" : 10,
"LoginMode" : "Password",
"Action" : "CreateUHostInstance",
"Name" : "Host01",
"ChargeType" : "Month",
"Quantity" : 1,
"PublicKey" : "ucloudsomeone@example.com1296235120854146120"
}`)
obj := _obj.(*jsonutils.JSONDict)
tests := []struct {
name string
args args
want string
}{
{
name: "Ucloud api signature validate",
args: args{
params: SParams{data: *obj},
privateKey: "46f09bb9fab4f12dfc160dae12273d5332b5debe",
},
want: "4f9ef5df2abab2c6fccd1e9515cb7e2df8c6bb65",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetSignature(tt.args.params, tt.args.privateKey); got != tt.want {
t.Errorf("GetSignature() = %v, want %v", got, tt.want)
}
})
}
}
+59
View File
@@ -0,0 +1,59 @@
package ucloud
// https://docs.ucloud.cn/api/summary/regionlist
var UCLOUD_REGION_NAMES = map[string]string{
"cn-bj1": "北京一",
"cn-bj2": "北京二",
"cn-sh": "上海金融云",
"cn-sh2": "上海二",
"cn-gd": "广州",
"hk": "香港",
"us-ca": "洛杉矶",
"us-ws": "华盛顿",
"ge-fra": "法兰克福",
"th-bkk": "曼谷",
"kr-seoul": "首尔",
"sg": "新加坡",
"tw-tp": "台北",
"tw-kh": "高雄",
"jpn-tky": "东京",
"rus-mosc": "莫斯科",
"uae-dubai": "迪拜",
"idn-jakarta": "雅加达",
"ind-mumbai": "孟买",
"bra-saopaulo": "圣保罗",
"uk-london": "伦敦",
"afr-nigeria": "拉各斯",
"vn-sng": "胡志明市",
}
var UCLOUD_ZONE_NAMES = map[string]string{
"cn-bj1-01": "北京一可用区A",
"cn-bj2-02": "北京二可用区B",
"cn-bj2-03": "北京二可用区C",
"cn-bj2-04": "北京二可用区D",
"cn-bj2-05": "北京二可用区E",
"cn-sh-01": "上海一可用区A",
"cn-sh2-01": "上海二可用区A",
"cn-sh2-02": "上海二可用区B",
"cn-gd-02": "广州可用区B",
"hk-01": "香港可用区A",
"hk-02": "香港可用区B",
"us-ca-01": "洛杉矶可用区A",
"us-ws-01": "华盛顿可用区A",
"ge-fra-01": "法兰克福可用区A",
"th-bkk-01": "曼谷可用区A",
"kr-seoul-01": "首尔可用区A",
"sg-01": "新加坡可用区A",
"tw-kh-01": "高雄可用区A",
"tw-tp-01": "台北可用区A",
"jpn-tky-01": "东京可用区A",
"rus-mosc-01": "莫斯科可用区A",
"uae-dubai-01": "迪拜可用区A",
"idn-jakarta-01": "雅加达可用区A",
"ind-mumbai-01": "孟买可用区A",
"bra-saopaulo-01": "圣保罗可用区A",
"uk-london-01": "伦敦可用区A",
"afr-nigeria-01": "拉各斯可用区A",
"vn-sng-01": "胡志明市可用区A",
}
+277
View File
@@ -0,0 +1,277 @@
package ucloud
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/models"
"yunion.io/x/pkg/utils"
)
// https://docs.ucloud.cn/api/udisk-api/describe_udisk
type SDisk struct {
storage *SStorage
Status string `json:"Status"`
DeviceName string `json:"DeviceName"`
UHostID string `json:"UHostId"`
Tag string `json:"Tag"`
Version string `json:"Version"`
Name string `json:"Name"`
Zone string `json:"Zone"`
UHostIP string `json:"UHostIP"`
DiskType string `json:"DiskType"`
UDataArkMode string `json:"UDataArkMode"`
SnapshotLimit int `json:"SnapshotLimit"`
ExpiredTime int64 `json:"ExpiredTime"`
SnapshotCount int `json:"SnapshotCount"`
IsExpire string `json:"IsExpire"`
UDiskID string `json:"UDiskId"`
ChargeType string `json:"ChargeType"`
UHostName string `json:"UHostName"`
CreateTime int64 `json:"CreateTime"`
SizeGB int `json:"Size"`
}
func (self *SDisk) GetProjectId() string {
return self.storage.zone.region.client.projectId
}
func (self *SDisk) GetId() string {
return self.UDiskID
}
func (self *SDisk) GetName() string {
if len(self.Name) == 0 {
return self.GetId()
}
return self.Name
}
func (self *SDisk) GetGlobalId() string {
return self.GetId()
}
func (self *SDisk) GetStatus() string {
switch self.Status {
case "Available":
return models.DISK_READY
case "Attaching":
return models.DISK_ATTACHING
case "InUse":
return models.DISK_READY
case "Detaching":
return models.DISK_DETACHING
case "Initializating":
return models.DISK_ALLOCATING
case "Failed":
return models.DISK_ALLOC_FAILED
case "Cloning":
return models.DISK_SAVING // ??????
case "Restoring":
return models.DISK_BACKUP_STARTALLOC // ???
case "RestoreFailed":
return models.DISK_BACKUP_ALLOC_FAILED // ??
default:
return models.DISK_UNKNOWN
}
}
func (self *SDisk) Refresh() error {
new, err := self.storage.zone.region.GetDisk(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SDisk) IsEmulated() bool {
return false
}
func (self *SDisk) GetMetadata() *jsonutils.JSONDict {
// todo: add price key
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(models.HYPERVISOR_UCLOUD), "hypervisor")
return data
}
// Year,Month,Dynamic,Trial
func (self *SDisk) GetBillingType() string {
switch self.ChargeType {
case "Year", "Month":
return models.BILLING_TYPE_PREPAID
default:
return models.BILLING_TYPE_POSTPAID
}
}
func (self *SDisk) GetExpiredAt() time.Time {
return time.Unix(self.ExpiredTime, 0)
}
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
return self.storage, nil
}
func (self *SDisk) GetDiskFormat() string {
return "vhd"
}
func (self *SDisk) GetDiskSizeMB() int {
return self.SizeGB * 1024
}
func (self *SDisk) GetIsAutoDelete() bool {
if self.DiskType == "SystemDisk" {
return true
}
return false
}
func (self *SDisk) GetTemplateId() string {
if strings.Contains(self.DiskType, "SystemDisk") && len(self.UHostID) > 0 {
ins, err := self.storage.zone.region.GetInstanceByID(self.UHostID)
if err != nil {
log.Errorf(err.Error())
}
return ins.ImageID
}
return ""
}
func (self *SDisk) GetDiskType() string {
if strings.Contains(self.DiskType, "SystemDisk") {
return models.DISK_TYPE_SYS
}
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 self.DeviceName
}
func (self *SDisk) GetAccessPath() string {
return ""
}
func (self *SDisk) Delete(ctx context.Context) error {
panic("implement me")
}
func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) {
panic("implement me")
}
func (self *SDisk) getSnapshot(snapshotId string) (*SSnapshot, error) {
snapshot, err := self.storage.zone.region.GetSnapshotById(snapshotId)
return &snapshot, err
}
func (self *SDisk) GetISnapshot(idStr string) (cloudprovider.ICloudSnapshot, error) {
snapshot, err := self.getSnapshot(idStr)
return snapshot, err
}
func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
snapshots, err := self.storage.zone.region.GetSnapshots(self.GetId(), "")
if err != nil {
return nil, err
}
isnapshots := make([]cloudprovider.ICloudSnapshot, len(snapshots))
for i := 0; i < len(snapshots); i++ {
isnapshots[i] = &snapshots[i]
}
return isnapshots, nil
}
func (self *SDisk) Resize(ctx context.Context, newSizeMB int64) error {
panic("implement me")
}
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
panic("implement me")
}
func (self *SDisk) Rebuild(ctx context.Context) error {
panic("implement me")
}
func (self *SRegion) GetDisk(diskId string) (*SDisk, error) {
if len(diskId) == 0 {
return nil, fmt.Errorf("GetDisk id should not empty")
}
disks, err := self.GetDisks("", "", []string{diskId})
if err != nil {
return nil, err
}
if len(disks) == 1 {
return &disks[0], nil
} else if len(disks) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("GetDisk %s %d found", diskId, len(disks))
}
}
// https://docs.ucloud.cn/api/udisk-api/describe_udisk
// diskType DataDisk|SystemDisk (DataDisk表示数据盘,SystemDisk表示系统盘)
func (self *SRegion) GetDisks(zoneId string, diskType string, diskIds []string) ([]SDisk, error) {
disks := make([]SDisk, 0)
params := NewUcloudParams()
if len(zoneId) > 0 {
params.Set("Zone", zoneId)
}
if len(diskType) > 0 {
params.Set("DiskType", diskType)
}
err := self.DoListAll("DescribeUDisk", params, &disks)
if err != nil {
return nil, err
}
if diskIds != nil && len(diskIds) > 0 {
filtedDisks := make([]SDisk, 0)
for _, disk := range disks {
if utils.IsInStringArray(disk.UDiskID, diskIds) {
filtedDisks = append(filtedDisks, disk)
}
}
return filtedDisks, nil
}
return disks, nil
}
+181
View File
@@ -0,0 +1,181 @@
package ucloud
import (
"time"
"github.com/coredns/coredns/plugin/pkg/log"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/compute/models"
)
// https://docs.ucloud.cn/api/unet-api/describe_eip
type SEip struct {
region *SRegion
BandwidthMb int `json:"Bandwidth"`
BandwidthType int `json:"BandwidthType"`
ChargeType string `json:"ChargeType"`
CreateTime int64 `json:"CreateTime"`
EIPAddr []EIPAddr `json:"EIPAddr"`
EIPID string `json:"EIPId"`
Expire bool `json:"Expire"`
ExpireTime int64 `json:"ExpireTime"`
Name string `json:"Name"`
PayMode string `json:"PayMode"`
Remark string `json:"Remark"`
Resource Resource `json:"Resource"`
ShareBandwidthSet ShareBandwidthSet `json:"ShareBandwidthSet"`
Status string `json:"Status"`
Tag string `json:"Tag"`
Weight int `json:"Weight"`
}
func (self *SEip) GetProjectId() string {
return self.region.client.projectId
}
type EIPAddr struct {
IP string `json:"IP"`
OperatorName string `json:"OperatorName"`
}
type Resource struct {
ResourceID string `json:"ResourceID"`
ResourceName string `json:"ResourceName"`
ResourceType string `json:"ResourceType"`
Zone string `json:"Zone"`
}
type ShareBandwidthSet struct {
ShareBandwidth int `json:"ShareBandwidth"`
ShareBandwidthID string `json:"ShareBandwidthId"`
ShareBandwidthName string `json:"ShareBandwidthName"`
}
func (self *SEip) GetId() string {
return self.EIPID
}
func (self *SEip) GetName() string {
if len(self.Name) == 0 {
return self.GetId()
}
return self.Name
}
func (self *SEip) GetGlobalId() string {
return self.GetId()
}
// 弹性IP的资源绑定状态, 枚举值为: used: 已绑定, free: 未绑定, freeze: 已冻结
func (self *SEip) GetStatus() string {
switch self.Status {
case "used":
return models.EIP_STATUS_ASSOCIATE // ?
case "free":
return models.EIP_STATUS_READY
case "freeze":
return models.EIP_STATUS_UNKNOWN
default:
return models.EIP_STATUS_UNKNOWN
}
}
func (self *SEip) Refresh() error {
if self.IsEmulated() {
return nil
}
new, err := self.region.GetEipById(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SEip) IsEmulated() bool {
return false
}
func (self *SEip) GetMetadata() *jsonutils.JSONDict {
return nil
}
// 付费方式, 枚举值为: Year, 按年付费; Month, 按月付费; Dynamic, 按小时付费; Trial, 试用. 按小时付费和试用这两种付费模式需要开通权限.
func (self *SEip) GetBillingType() string {
switch self.ChargeType {
case "Year", "Month":
return models.BILLING_TYPE_PREPAID
default:
return models.BILLING_TYPE_POSTPAID
}
}
func (self *SEip) GetExpiredAt() time.Time {
return time.Unix(self.ExpireTime, 0)
}
func (self *SEip) GetIpAddr() string {
if len(self.EIPAddr) > 1 {
log.Warning("GetIpAddr %d eip addr found", len(self.EIPAddr))
} else if len(self.EIPAddr) == 0 {
return ""
}
return self.EIPAddr[0].IP
}
func (self *SEip) GetMode() string {
return models.EIP_MODE_STANDALONE_EIP
}
func (self *SEip) GetAssociationType() string {
return "server"
}
// 已绑定的资源类型, 枚举值为: uhost, 云主机;natgwNAT网关;ulb:负载均衡器;upm: 物理机; hadoophost: 大数据集群;fortresshost:堡垒机;udockhost:容器;udhost:私有专区主机;vpngwIPSec VPNucdr:云灾备;dbaudit:数据库审计。
func (self *SEip) GetAssociationExternalId() string {
if self.Resource.ResourceType == "uhost" {
return self.Resource.ResourceID
} else if self.Resource.ResourceType != "" {
log.Warningf("GetAssociationExternalId bind with %s %s.expect bind with uhost", self.Resource.ResourceType, self.Resource.ResourceID)
}
return ""
}
func (self *SEip) GetBandwidth() int {
return self.BandwidthMb
}
// 弹性IP的计费模式, 枚举值为: "Bandwidth", 带宽计费; "Traffic", 流量计费; "ShareBandwidth",共享带宽模式. 默认为 "Bandwidth".
func (self *SEip) GetInternetChargeType() string {
switch self.PayMode {
case "Bandwidth":
return models.EIP_CHARGE_TYPE_BY_BANDWIDTH
case "Traffic":
return models.EIP_CHARGE_TYPE_BY_TRAFFIC
default:
return models.EIP_CHARGE_TYPE_BY_TRAFFIC
}
}
func (self *SEip) GetManagerId() string {
return self.region.client.providerId
}
func (self *SEip) Delete() error {
panic("implement me")
}
func (self *SEip) Associate(instanceId string) error {
panic("implement me")
}
func (self *SEip) Dissociate() error {
panic("implement me")
}
func (self *SEip) ChangeBandwidth(bw int) error {
panic("implement me")
}
+154
View File
@@ -0,0 +1,154 @@
package ucloud
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SHost struct {
zone *SZone
projectId string
}
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 self.GetId()
}
func (self *SHost) GetStatus() string {
return models.HOST_STATUS_RUNNING
}
func (self *SHost) Refresh() error {
return nil
}
func (self *SHost) IsEmulated() bool {
return true
}
func (self *SHost) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
vms, err := self.zone.GetInstances()
if err != nil {
return nil, err
}
ivms := make([]cloudprovider.ICloudVM, len(vms))
for i := 0; i < len(vms); i += 1 {
vms[i].host = self
ivms[i] = &vms[i]
}
return ivms, nil
}
func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
vm, err := self.zone.region.GetInstanceByID(id)
vm.host = self
return &vm, err
}
func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
return self.zone.GetIWires()
}
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
return self.zone.GetIStorages()
}
func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
return self.zone.GetIStorageById(id)
}
func (self *SHost) GetEnabled() bool {
return true
}
func (self *SHost) GetHostStatus() string {
return models.HOST_ONLINE
}
func (self *SHost) GetAccessIp() string {
return ""
}
func (self *SHost) GetAccessMac() string {
return ""
}
func (self *SHost) GetSysInfo() jsonutils.JSONObject {
info := jsonutils.NewDict()
info.Add(jsonutils.NewString(CLOUD_PROVIDER_UCLOUD), "manufacture")
return info
}
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) GetStorageType() string {
return models.DISK_TYPE_HYBRID
}
func (self *SHost) GetHostType() string {
return models.HOST_TYPE_UCLOUD
}
func (self *SHost) GetIsMaintenance() bool {
return false
}
func (self *SHost) GetVersion() string {
return UCLOUD_API_VERSION
}
func (self *SHost) GetManagerId() string {
return self.zone.region.client.providerId
}
func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
return nil, cloudprovider.ErrNotSupported
}
+200
View File
@@ -0,0 +1,200 @@
package ucloud
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/imagetools"
)
type SImage struct {
storageCache *SStoragecache
Zone string `json:"Zone"`
ImageDescription string `json:"ImageDescription"`
OSName string `json:"OsName"`
ImageID string `json:"ImageId"`
State string `json:"State"`
ImageName string `json:"ImageName"`
OSType string `json:"OsType"`
CreateTime int64 `json:"CreateTime"`
ImageType string `json:"ImageType"`
ImageSizeGB int64 `json:"ImageSize"`
}
func (self *SImage) GetMinRamSizeMb() int {
return 0
}
func (self *SImage) GetId() string {
return self.ImageID
}
func (self *SImage) GetName() string {
if len(self.ImageName) == 0 {
return self.GetId()
}
return self.ImageName
}
func (self *SImage) GetGlobalId() string {
return self.GetId()
}
// 镜像状态, 可用:Available,制作中:Making 不可用:Unavailable
func (self *SImage) GetStatus() string {
switch self.State {
case "Available":
return cloudprovider.IMAGE_STATUS_ACTIVE
case "Making":
return cloudprovider.IMAGE_STATUS_QUEUED
case "Unavailable":
return cloudprovider.IMAGE_STATUS_KILLED
default:
return cloudprovider.IMAGE_STATUS_KILLED
}
}
func (self *SImage) Refresh() error {
new, err := self.storageCache.region.GetImage(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SImage) IsEmulated() bool {
return false
}
func (self *SImage) GetMetadata() *jsonutils.JSONDict {
imageInfo := imagetools.NormalizeImageInfo(self.ImageName, "", "", "", "")
data := jsonutils.NewDict()
if len(imageInfo.OsArch) > 0 {
data.Add(jsonutils.NewString(imageInfo.OsArch), "os_arch")
}
if len(imageInfo.OsType) > 0 {
data.Add(jsonutils.NewString(imageInfo.OsType), "os_name")
}
if len(imageInfo.OsDistro) > 0 {
data.Add(jsonutils.NewString(imageInfo.OsDistro), "os_distribution")
}
if len(imageInfo.OsVersion) > 0 {
data.Add(jsonutils.NewString(imageInfo.OsVersion), "os_version")
}
return data
}
func (self *SImage) Delete(ctx context.Context) error {
panic("implement me")
}
func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
return self.storageCache
}
func (self *SImage) GetSize() int64 {
return self.ImageSizeGB * 1024 * 1024 * 1024
}
// 镜像类型。标准镜像:Base,镜像市场:Business, 自定义镜像:Custom,默认返回所有类型
func (self *SImage) GetImageType() string {
switch self.ImageType {
case "Base":
return cloudprovider.CachedImageTypeSystem
case "Custom":
return cloudprovider.CachedImageTypeCustomized
case "Business":
return cloudprovider.CachedImageTypeShared
default:
return cloudprovider.CachedImageTypeCustomized
}
}
func (self *SImage) GetImageStatus() string {
switch self.State {
case "Available":
return models.CACHED_IMAGE_STATUS_READY
case "Making":
return models.CACHED_IMAGE_STATUS_CACHING
case "Unavailable":
return models.CACHED_IMAGE_STATUS_CACHE_FAILED
default:
return models.CACHED_IMAGE_STATUS_CACHE_FAILED
}
}
func (self *SImage) GetOsType() string {
imageInfo := imagetools.NormalizeImageInfo(self.ImageName, "", "", "", "")
return imageInfo.OsType
}
func (self *SImage) GetOsDist() string {
imageInfo := imagetools.NormalizeImageInfo(self.ImageName, "", "", "", "")
return imageInfo.OsDistro
}
func (self *SImage) GetOsVersion() string {
imageInfo := imagetools.NormalizeImageInfo(self.ImageName, "", "", "", "")
return imageInfo.OsVersion
}
func (self *SImage) GetOsArch() string {
imageInfo := imagetools.NormalizeImageInfo(self.ImageName, "", "", "", "")
return imageInfo.OsArch
}
func (self *SImage) GetMinOsDiskSizeGb() int {
return int(self.ImageSizeGB)
}
func (self *SImage) GetImageFormat() string {
return ""
}
func (self *SImage) GetCreateTime() time.Time {
return time.Unix(self.CreateTime, 0)
}
// https://docs.ucloud.cn/api/uhost-api/describe_image
func (self *SRegion) GetImage(imageId string) (SImage, error) {
params := NewUcloudParams()
params.Set("ImageId", imageId)
images := make([]SImage, 0)
err := self.DoListAll("DescribeImage", params, &images)
if err != nil {
return SImage{}, err
}
if len(images) == 1 {
return images[0], nil
} else if len(images) == 0 {
return SImage{}, cloudprovider.ErrNotFound
} else {
return SImage{}, fmt.Errorf("GetImage %s %d found.", imageId, len(images))
}
}
// https://docs.ucloud.cn/api/uhost-api/describe_image
// ImageType 标准镜像:Base,镜像市场:Business, 自定义镜像:Custom,默认返回所有类型
func (self *SRegion) GetImages(imageType string, imageId string) ([]SImage, error) {
params := NewUcloudParams()
if len(imageId) > 0 {
params.Set("ImageId", imageId)
}
if len(imageType) > 0 {
params.Set("ImageType", imageType)
}
images := make([]SImage, 0)
err := self.DoListAll("DescribeImage", params, &images)
return images, err
}
+371
View File
@@ -0,0 +1,371 @@
package ucloud
import (
"context"
"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/onecloud/pkg/util/billing"
"yunion.io/x/pkg/util/osprofile"
)
type SInstance struct {
host *SHost
UHostID string `json:"UHostId"`
Zone string `json:"Zone"`
LifeCycle string `json:"LifeCycle"`
OSName string `json:"OsName"`
ImageID string `json:"ImageId"`
BasicImageID string `json:"BasicImageId"`
BasicImageName string `json:"BasicImageName"`
Tag string `json:"Tag"`
Name string `json:"Name"`
Remark string `json:"Remark"`
State string `json:"State"`
NetworkState string `json:"NetworkState"`
HostType string `json:"HostType"`
StorageType string `json:"StorageType"`
TotalDiskSpace int `json:"TotalDiskSpace"`
DiskSet []DiskSet `json:"DiskSet"`
NetCapability string `json:"NetCapability"`
IPSet []IPSet `json:"IPSet"`
SubnetType string `json:"SubnetType"`
ChargeType string `json:"ChargeType"`
ExpireTime int64 `json:"ExpireTime"`
AutoRenew string `json:"AutoRenew"`
IsExpire string `json:"IsExpire"`
UHostType string `json:"UHostType"`
OSType string `json:"OsType"`
CreateTime int64 `json:"CreateTime"`
CPU int `json:"CPU"`
GPU int `json:"GPU"`
MemoryMB int `json:"Memory"`
TimemachineFeature string `json:"TimemachineFeature"`
HotplugFeature bool `json:"HotplugFeature"`
NetCapFeature bool `json:"NetCapFeature"`
BootDiskState string `json:"BootDiskState"`
}
func (self *SInstance) GetProjectId() string {
return self.host.zone.region.client.projectId
}
func (self *SInstance) GetError() error {
panic("implement me")
}
type DiskSet struct {
DiskID string `json:"DiskId"`
Drive string `json:"Drive"`
Size int `json:"Size"`
Encrypted string `json:"Encrypted"`
Type string `json:"Type"`
}
type IPSet struct {
Type string `json:"Type"`
IP string `json:"IP"`
IPId string `json:"IPId"` // IP资源ID (内网IP无对应的资源ID)
MAC string `json:"Mac"`
VPCID string `json:"VPCId"`
SubnetID string `json:"SubnetId"`
}
type SVncInfo struct {
VNCIP string `json:"VncIP"`
VNCPassword string `json:"VncPassword"`
UHostID string `json:"UHostId"`
Action string `json:"Action"`
VNCPort string `json:"VncPort"`
}
func (self *SInstance) GetId() string {
return self.UHostID
}
func (self *SInstance) GetName() string {
if len(self.Name) == 0 {
return self.GetId()
}
return self.Name
}
func (self *SInstance) GetGlobalId() string {
return self.GetId()
}
// 实例状态,枚举值:
// >初始化: Initializing;
// >启动中: Starting;
// > 运行中: Running;
// > 关机中: Stopping;
// >关机: Stopped
// >安装失败: Install Fail;
// >重启中: Rebooting
func (self *SInstance) GetStatus() string {
switch self.State {
case "Running":
return models.VM_RUNNING
case "Stopped":
return models.VM_READY
case "Rebooting":
return models.VM_STOPPING
case "Initializing":
return models.VM_INIT
case "Starting":
return models.VM_STARTING
case "Stopping":
return models.VM_STOPPING
case "Install Fail":
return models.VM_CREATE_FAILED
default:
return models.VM_UNKNOWN
}
}
func (self *SInstance) Refresh() error {
new, err := self.host.zone.region.GetInstanceByID(self.GetId())
new.host = self.host
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SInstance) IsEmulated() bool {
return false
}
func (self *SInstance) GetMetadata() *jsonutils.JSONDict {
data := jsonutils.NewDict()
// todo: add price key here
data.Add(jsonutils.NewString(self.host.zone.GetGlobalId()), "zone_ext_id")
if len(self.BasicImageID) > 0 {
if image, err := self.host.zone.region.GetImage(self.BasicImageID); err != nil {
log.Errorf("Failed to find image %s for instance %s", self.BasicImageID, self.GetName())
} else if meta := image.GetMetadata(); meta != nil {
data.Update(meta)
}
}
secgroups, err := self.GetSecurityGroups()
if err != nil {
log.Errorf(err.Error())
}
secgroupIds := jsonutils.NewArray()
for _, secgroup := range secgroups {
secgroupIds.Add(jsonutils.NewString(secgroup.GetId()))
}
data.Add(secgroupIds, "secgroupIds")
return data
}
// 计费模式,枚举值为: Year,按年付费; Month,按月付费; Dynamic,按需付费(需开启权限);
func (self *SInstance) GetBillingType() string {
switch self.ChargeType {
case "Year", "Month":
return models.BILLING_TYPE_PREPAID
default:
return models.BILLING_TYPE_POSTPAID
}
}
func (self *SInstance) GetExpiredAt() time.Time {
return time.Unix(self.ExpireTime, 0)
}
func (self *SInstance) GetCreateTime() time.Time {
return time.Unix(self.CreateTime, 0)
}
func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
return self.host
}
func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
diskIds := make([]string, 0)
for _, disk := range self.DiskSet {
diskIds = append(diskIds, disk.DiskID)
}
disks, err := self.host.zone.region.GetDisks("", "", diskIds)
if err != nil {
return nil, err
}
idisks := make([]cloudprovider.ICloudDisk, len(disks))
for i := 0; i < len(disks); i += 1 {
idisks[i] = &disks[i]
// 将系统盘放到第0个位置
if disks[i].GetDiskType() == models.DISK_TYPE_SYS {
_temp := idisks[0]
idisks[0] = &disks[i]
idisks[i] = _temp
}
}
return idisks, nil
}
func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
nics := make([]cloudprovider.ICloudNic, 0)
for _, ip := range self.IPSet {
if len(ip.SubnetID) == 0 {
continue
}
nic := SInstanceNic{instance: self, ipAddr: ip.IP}
nics = append(nics, &nic)
}
return nics, nil
}
// 国际: InternationBGP: BGP,内网: Private
func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
for _, ip := range self.IPSet {
if len(ip.IPId) > 0 {
eip, err := self.host.zone.region.GetEipById(ip.IPId)
if err != nil {
return nil, err
}
return &eip, nil
}
}
return nil, nil
}
func (self *SInstance) GetVcpuCount() int8 {
return int8(self.CPU)
}
func (self *SInstance) GetVmemSizeMB() int {
return self.MemoryMB
}
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 {
return osprofile.NormalizeOSType(self.OSType)
}
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) GetInstanceType() string {
return self.UHostType
}
func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
panic("implement me")
}
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
panic("implement me")
}
func (self *SInstance) GetHypervisor() string {
return models.HYPERVISOR_UCLOUD
}
func (self *SInstance) StartVM(ctx context.Context) error {
panic("implement me")
}
func (self *SInstance) StopVM(ctx context.Context, isForce bool) error {
panic("implement me")
}
func (self *SInstance) DeleteVM(ctx context.Context) error {
panic("implement me")
}
func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
panic("implement me")
}
func (self *SInstance) UpdateUserData(userData string) error {
panic("implement me")
}
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
panic("implement me")
}
func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error {
panic("implement me")
}
func (self *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error {
panic("implement me")
}
func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
panic("implement me")
}
func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
return self.host.zone.region.GetInstanceVNCUrl(self.GetId())
}
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
panic("implement me")
}
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
panic("implement me")
}
func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
panic("implement me")
}
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
panic("implement me")
}
func (self *SInstance) GetSecurityGroups() ([]SSecurityGroup, error) {
return self.host.zone.region.GetSecurityGroups("", self.GetId())
}
// https://docs.ucloud.cn/api/uhost-api/get_uhost_instance_vnc_info
// todo: implement me
func (self *SRegion) GetInstanceVNCUrl(instanceId string) (jsonutils.JSONObject, error) {
params := NewUcloudParams()
params.Set("UHostId", instanceId)
vnc := SVncInfo{}
err := self.DoAction("GetUHostInstanceVncInfo", params, &vnc)
if err != nil {
return nil, err
}
return jsonutils.Marshal(&vnc), nil
}
+35
View File
@@ -0,0 +1,35 @@
package ucloud
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 {
for _, ip := range self.instance.IPSet {
if ip.IP == self.ipAddr {
network, _ := self.instance.host.zone.region.getNetwork(ip.SubnetID)
return network
}
}
return nil
}
+30
View File
@@ -0,0 +1,30 @@
package ucloud
import "yunion.io/x/onecloud/pkg/cloudprovider"
// https://docs.ucloud.cn/api/summary/regionlist
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"cn-bj1": {Latitude: 39.9041999, Longitude: 116.4073963, City: "Beijing", CountryCode: "CN"},
"cn-bj2": {Latitude: 39.9041999, Longitude: 116.4073963, City: "Beijing", CountryCode: "CN"},
"cn-sh": {Latitude: 31.2303904, Longitude: 121.4737021, City: "Shanghai", CountryCode: "CN"},
"cn-sh2": {Latitude: 31.2303904, Longitude: 121.4737021, City: "Shanghai", CountryCode: "CN"},
"cn-gd": {Latitude: 23.12911, Longitude: 113.264385, City: "Guangzhou", CountryCode: "CN"},
"hk": {Latitude: 22.396428, Longitude: 114.109497, City: "Hong Kong", CountryCode: "CN"},
"us-ca": {Latitude: 34.0522342, Longitude: -118.2436849, City: "Los Angeles", CountryCode: "US"},
"us-ws": {Latitude: 38.9071923, Longitude: -77.0368707, City: "Washington", CountryCode: "US"},
"ge-fra": {Latitude: 50.1109221, Longitude: 8.6821267, City: "Frankfurt", CountryCode: "GE"},
"th-bkk": {Latitude: 13.7563309, Longitude: 100.5017651, City: "Bangkok", CountryCode: "TH"},
"kr-seoul": {Latitude: 37.566535, Longitude: 126.9779692, City: "Seoul", CountryCode: "KR"},
"sg": {Latitude: 1.352083, Longitude: 103.819836, City: "Singapore", CountryCode: "SG"},
"tw-tp": {Latitude: 25.0329694, Longitude: 121.5654177, City: "Taipei", CountryCode: "CN"},
"tw-kh": {Latitude: 22.6272784, Longitude: 120.3014353, City: "Kaohsiung", CountryCode: "CN"},
"jpn-tky": {Latitude: 35.7090259, Longitude: 139.7319925, City: "Tokyo", CountryCode: "JP"},
"rus-mosc": {Latitude: 55.755826, Longitude: 37.6172999, City: "Moscow", CountryCode: "RU"},
"uae-dubai": {Latitude: 25.2048493, Longitude: 55.2707828, City: "Dubai", CountryCode: "UA"},
"idn-jakarta": {Latitude: -6.2087634, Longitude: 106.845599, City: "Jakarta", CountryCode: "ID"},
"ind-mumbai": {Latitude: 19.0759837, Longitude: 72.8776559, City: "Mumbai", CountryCode: "IN"},
"bra-saopaulo": {Latitude: -23.5505199, Longitude: -46.6333094, City: "São Paulo", CountryCode: "BR"},
"uk-london": {Latitude: 51.5073509, Longitude: -0.1277583, City: "London", CountryCode: "UK"},
"afr-nigeria": {Latitude: 6.5243793, Longitude: 3.3792057, City: "Lagos", CountryCode: "AF"},
"vn-sng": {Latitude: 10.8230989, Longitude: 106.6296638, City: "Ho Chi Minh", CountryCode: "VN"},
}
+145
View File
@@ -0,0 +1,145 @@
package ucloud
import (
"fmt"
"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"
)
// https://docs.ucloud.cn/api/vpc2.0-api/describe_subnet
type SNetwork struct {
wire *SWire
CreateTime int64 `json:"CreateTime"`
Gateway string `json:"Gateway"`
HasNATGW bool `json:"HasNATGW"`
Name string `json:"Name"`
Netmask string `json:"Netmask"`
Remark string `json:"Remark"`
RouteTableID string `json:"RouteTableId"`
Subnet string `json:"Subnet"`
SubnetID string `json:"SubnetId"`
SubnetName string `json:"SubnetName"`
SubnetType int `json:"SubnetType"`
Tag string `json:"Tag"`
VPCID string `json:"VPCId"`
VPCName string `json:"VPCName"`
VRouterID string `json:"VRouterId"`
Zone string `json:"Zone"`
}
func (self *SNetwork) GetProjectId() string {
return self.wire.region.client.projectId
}
func (self *SNetwork) GetId() string {
return self.SubnetID
}
func (self *SNetwork) GetName() string {
if len(self.SubnetName) > 0 {
return self.SubnetName
}
return self.GetId()
}
func (self *SNetwork) GetGlobalId() string {
return self.GetId()
}
func (self *SNetwork) GetStatus() string {
return models.NETWORK_STATUS_AVAILABLE
}
func (self *SNetwork) Refresh() error {
log.Debugf("network refresh %s", self.GetId())
new, err := self.wire.region.getNetwork(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SNetwork) IsEmulated() bool {
return false
}
func (self *SNetwork) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
return self.wire
}
func (self *SNetwork) GetIpStart() string {
pref, _ := netutils.NewIPV4Prefix(self.Subnet + "/" + self.Netmask)
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
startIp = startIp.StepUp() // 1
return startIp.String()
}
func (self *SNetwork) GetIpEnd() string {
pref, _ := netutils.NewIPV4Prefix(self.Subnet + "/" + self.Netmask)
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.Subnet + "/" + self.Netmask)
return pref.MaskLen
}
func (self *SNetwork) GetGateway() string {
pref, _ := netutils.NewIPV4Prefix(self.Subnet + "/" + self.Netmask)
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
endIp = endIp.StepDown() // 254
return endIp.String()
}
func (self *SNetwork) GetServerType() string {
return models.NETWORK_TYPE_GUEST
}
func (self *SNetwork) GetIsPublic() bool {
return true
}
func (self *SNetwork) Delete() error {
panic("implement me")
}
func (self *SNetwork) GetAllocTimeoutSeconds() int {
return 120 // 2 minutes
}
// https://docs.ucloud.cn/api/vpc2.0-api/describe_subnet
func (self *SRegion) getNetwork(networkId string) (*SNetwork, error) {
if len(networkId) == 0 {
return nil, fmt.Errorf("getNetwork network id should not be empty")
}
networks := make([]SNetwork, 0)
params := NewUcloudParams()
params.Set("SubnetId", networkId)
err := self.DoListAll("DescribeSubnet", params, &networks)
if err != nil {
return nil, err
}
if len(networks) == 1 {
return &networks[0], nil
} else if len(networks) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("getNetwork %s %d found", networkId, len(networks))
}
}
+50
View File
@@ -0,0 +1,50 @@
package ucloud
import "yunion.io/x/jsonutils"
// https://docs.ucloud.cn/api/summary/get_project_list
type SProject struct {
ProjectID string `json:"ProjectId"`
ProjectName string `json:"ProjectName"`
ParentID string `json:"ParentId"`
ParentName string `json:"ParentName"`
CreateTime int64 `json:"CreateTime"`
IsDefault bool `json:"IsDefault"`
MemberCount int64 `json:"MemberCount"`
ResourceCount int64 `json:"ResourceCount"`
}
func (self *SProject) GetId() string {
return self.ProjectID
}
func (self *SProject) GetName() string {
return self.ProjectName
}
func (self *SProject) GetGlobalId() string {
return self.GetId()
}
func (self *SProject) GetStatus() string {
return ""
}
func (self *SProject) Refresh() error {
return nil
}
func (self *SProject) IsEmulated() bool {
return false
}
func (self *SProject) GetMetadata() *jsonutils.JSONDict {
return jsonutils.NewDict()
}
func (self *SUcloudClient) FetchProjects() ([]SProject, error) {
params := NewUcloudParams()
projects := make([]SProject, 0)
err := self.DoListAll("GetProjectList", params, &projects)
return projects, err
}
+142
View File
@@ -0,0 +1,142 @@
package provider
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/ucloud"
)
// tag:finished
type SUcloudProviderFactory struct {
}
func (self *SUcloudProviderFactory) GetId() string {
return ucloud.CLOUD_PROVIDER_UCLOUD
}
func (self *SUcloudProviderFactory) GetName() string {
return ucloud.CLOUD_PROVIDER_UCLOUD_CN
}
func (self *SUcloudProviderFactory) ValidateChangeBandwidth(instanceId string, bandwidth int64) error {
return nil
}
func (self *SUcloudProviderFactory) IsPublicCloud() bool {
return true
}
func (self *SUcloudProviderFactory) IsOnPremise() bool {
return false
}
func (self *SUcloudProviderFactory) IsSupportPrepaidResources() bool {
return true
}
func (self *SUcloudProviderFactory) NeedSyncSkuFromCloud() bool {
return false
}
func (self *SUcloudProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) error {
accessKeyID, _ := data.GetString("access_key_id")
if len(accessKeyID) == 0 {
return httperrors.NewMissingParameterError("access_key_id")
}
accessKeySecret, _ := data.GetString("access_key_secret")
if len(accessKeySecret) == 0 {
return httperrors.NewMissingParameterError("access_key_secret")
}
data.Set("account", jsonutils.NewString(accessKeyID))
data.Set("secret", jsonutils.NewString(accessKeySecret))
return nil
}
func (self *SUcloudProviderFactory) ValidateUpdateCloudaccountCredential(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject, cloudaccount string) (*cloudprovider.SCloudaccount, error) {
accessKeyID, _ := data.GetString("access_key_id")
if len(accessKeyID) == 0 {
return nil, httperrors.NewMissingParameterError("access_key_id")
}
accessKeySecret, _ := data.GetString("access_key_secret")
if len(accessKeySecret) == 0 {
return nil, httperrors.NewMissingParameterError("access_key_secret")
}
account := &cloudprovider.SCloudaccount{
Account: accessKeyID,
Secret: accessKeySecret,
}
return account, nil
}
func (self *SUcloudProviderFactory) GetProvider(providerId, providerName, url, account, secret string) (cloudprovider.ICloudProvider, error) {
client, err := ucloud.NewUcloudClient(providerId, providerName, account, secret, false)
if err != nil {
return nil, err
}
return &SUcloudProvider{
SBaseProvider: cloudprovider.NewBaseProvider(self),
client: client,
}, nil
}
func init() {
factory := SUcloudProviderFactory{}
cloudprovider.RegisterFactory(&factory)
}
type SUcloudProvider struct {
cloudprovider.SBaseProvider
client *ucloud.SUcloudClient
}
func (self *SUcloudProvider) GetIProjects() ([]cloudprovider.ICloudProject, error) {
projects, err := self.client.FetchProjects()
if err != nil {
return nil, err
}
iprojects := make([]cloudprovider.ICloudProject, len(projects))
for i := range projects {
iprojects[i] = &projects[i]
}
return iprojects, nil
}
func (self *SUcloudProvider) GetSysInfo() (jsonutils.JSONObject, error) {
regions := self.client.GetIRegions()
info := jsonutils.NewDict()
info.Add(jsonutils.NewInt(int64(len(regions))), "region_count")
info.Add(jsonutils.NewString(ucloud.UCLOUD_API_VERSION), "api_version")
return info, nil
}
func (self *SUcloudProvider) GetVersion() string {
return ucloud.UCLOUD_API_VERSION
}
func (self *SUcloudProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
return self.client.GetSubAccounts()
}
func (self *SUcloudProvider) GetIRegions() []cloudprovider.ICloudRegion {
return self.client.GetIRegions()
}
func (self *SUcloudProvider) GetIRegionById(extId string) (cloudprovider.ICloudRegion, error) {
return self.client.GetIRegionById(extId)
}
func (self *SUcloudProvider) GetBalance() (float64, error) {
return 0.0, nil
}
func (self *SUcloudProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) {
return nil, cloudprovider.ErrNotImplemented
}
+504
View File
@@ -0,0 +1,504 @@
package ucloud
import (
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/pkg/util/secrules"
)
type SRegion struct {
client *SUcloudClient
RegionID string
izones []cloudprovider.ICloudZone
ivpcs []cloudprovider.ICloudVpc
storageCache *SStoragecache
latitude float64
longitude float64
fetchLocation bool
}
func (self *SRegion) GetId() string {
return self.RegionID
}
func (self *SRegion) GetName() string {
if name, exist := UCLOUD_REGION_NAMES[self.GetId()]; exist {
return name
}
return self.GetId()
}
func (self *SRegion) GetGlobalId() string {
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_UCLOUD, self.GetId())
}
func (self *SRegion) GetStatus() string {
return models.CLOUD_REGION_STATUS_INSERVER
}
func (self *SRegion) Refresh() error {
return nil
}
func (self *SRegion) IsEmulated() bool {
return false
}
func (self *SRegion) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
if info, ok := LatitudeAndLongitude[self.GetId()]; ok {
return info
}
return cloudprovider.SGeographicInfo{}
}
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) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
if self.ivpcs == nil {
err := self.fetchInfrastructure()
if err != nil {
return nil, err
}
}
return self.ivpcs, nil
}
// https://docs.ucloud.cn/api/unet-api/describe_eip
func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
params := NewUcloudParams()
eips := make([]SEip, 0)
err := self.DoListAll("DescribeEIP", params, &eips)
if err != nil {
return nil, err
}
ieips := []cloudprovider.ICloudEIP{}
for _, eip := range eips {
eip.region = self
ieips = append(ieips, &eip)
}
return ieips, nil
}
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 += 1 {
if ivpcs[i].GetGlobalId() == id {
return ivpcs[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
if izones[i].GetGlobalId() == id {
return izones[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetEipById(eipId string) (SEip, error) {
params := NewUcloudParams()
params.Set("EIPIds.0", eipId)
eips := make([]SEip, 0)
err := self.DoListAll("DescribeEIP", params, &eips)
if err != nil {
return SEip{}, err
}
if len(eips) == 1 {
return eips[0], nil
} else {
return SEip{}, fmt.Errorf("GetEipById %d eip found", len(eips))
}
}
func (self *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) {
eip, err := self.GetEipById(id)
return &eip, err
}
// https://docs.ucloud.cn/api/unet-api/delete_firewall
func (self *SRegion) DeleteSecurityGroup(vpcId, secgroupId string) error {
params := NewUcloudParams()
params.Set("FWId", secgroupId)
return self.DoAction("DeleteFirewall", params, nil)
}
// https://docs.ucloud.cn/api/unet-api/describe_firewall
// 绑定防火墙组的资源类型,默认为全部资源类型。枚举值为:"unatgw"NAT网关; "uhost",云主机; "upm",物理云主机; "hadoophost"hadoop节点; "fortresshost",堡垒机; "udhost",私有专区主机;"udockhost",容器;"dbaudit",数据库审计.
// todo: 是否需要过滤出仅绑定云主机的安全组?
func (self *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) {
if len(secgroupId) > 0 {
_, err := self.GetSecurityGroupById(secgroupId)
if err == cloudprovider.ErrNotSupported {
secgroupId = ""
} else if err != nil {
return "", err
}
}
if len(secgroupId) == 0 {
extID, err := self.CreateSecurityGroup(name, desc)
if err != nil {
return "", err
}
secgroupId = extID
}
// todo: implement me
return secgroupId, self.syncSecgroupRules(secgroupId, rules)
}
func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) {
params := NewUcloudParams()
params.Set("Name", name)
params.Set("Remark", desc)
for i, cidr := range strings.Split(cidr, ",") {
params.Set(fmt.Sprintf("Network.%d", i), cidr)
}
vpcId := ""
err := self.DoAction("CreateVPC", params, &vpcId)
if err != nil {
return nil, err
}
return self.GetIVpcById(vpcId)
}
// https://docs.ucloud.cn/api/unet-api/allocate_eip
// 增加共享带宽模式ShareBandwidth
func (self *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) {
params := NewUcloudParams()
params.Set("OperatorName", bgpType)
params.Set("Bandwidth", bwMbps)
params.Set("Name", name)
var payMode string
switch chargeType {
case models.EIP_CHARGE_TYPE_BY_TRAFFIC:
payMode = "Traffic"
case models.EIP_CHARGE_TYPE_BY_BANDWIDTH:
payMode = "Bandwidth"
}
params.Set("PayMode", payMode)
eips := make([]SEip, 0)
err := self.DoAction("AllocateEIP", params, &eips)
if err != nil {
return nil, err
}
if len(eips) == 1 {
return &eips[0], nil
} else {
return nil, fmt.Errorf("CreateEIP %d eip created", len(eips))
}
}
// https://docs.ucloud.cn/api/udisk-api/describe_udisk_snapshot
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
params := NewUcloudParams()
snapshots := make([]SSnapshot, 0)
err := self.DoListAll("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
isnapshots := make([]cloudprovider.ICloudSnapshot, 0)
for _, snapshot := range snapshots {
snapshot.region = self
isnapshots = append(isnapshots, &snapshot)
}
return isnapshots, nil
}
func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
params := NewUcloudParams()
params.Set("SnapshotId", snapshotId)
snapshots := make([]SSnapshot, 0)
err := self.DoListAll("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
if len(snapshots) == 1 {
snapshot := snapshots[0]
snapshot.region = self
return &snapshot, nil
} else {
return nil, fmt.Errorf("GetISnapshotById %s %d snapshot found", snapshotId, len(snapshots))
}
}
func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) {
iHosts := make([]cloudprovider.ICloudHost, 0)
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
iZoneHost, err := izones[i].GetIHosts()
if err != nil {
return nil, err
}
iHosts = append(iHosts, iZoneHost...)
}
return iHosts, 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) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
iStores := make([]cloudprovider.ICloudStorage, 0)
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
iZoneStores, err := izones[i].GetIStorages()
if err != nil {
return nil, err
}
iStores = append(iStores, iZoneStores...)
}
return iStores, nil
}
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) getStoragecache() *SStoragecache {
if self.storageCache == nil {
self.storageCache = &SStoragecache{region: self}
}
return self.storageCache
}
func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
storageCache := self.getStoragecache()
return []cloudprovider.ICloudStoragecache{storageCache}, nil
}
func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
storageCache := self.getStoragecache()
if storageCache.GetGlobalId() == id {
return storageCache, nil
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetProvider() string {
return CLOUD_PROVIDER_UCLOUD
}
func (self *SRegion) DoListAll(action string, params SParams, result interface{}) error {
params.Set("Region", self.GetId())
return self.client.DoListAll(action, params, result)
}
// return total,lenght,error
func (self *SRegion) DoListPart(action string, limit int, offset int, params SParams, result interface{}) (int, int, error) {
params.Set("Region", self.GetId())
return self.client.DoListPart(action, limit, offset, params, result)
}
func (self *SRegion) DoAction(action string, params SParams, result interface{}) error {
params.Set("Region", self.GetId())
return self.client.DoAction(action, params, result)
}
func (self *SRegion) fetchInfrastructure() error {
if err := self.fetchZones(); err != nil {
return err
}
if err := self.fetchIVpcs(); err != nil {
return err
}
for i := 0; i < len(self.ivpcs); i += 1 {
vpc := self.ivpcs[i].(*SVPC)
wire := SWire{region: self, vpc: vpc}
vpc.addWire(&wire)
for j := 0; j < len(self.izones); j += 1 {
zone := self.izones[j].(*SZone)
zone.addWire(&wire)
}
}
return nil
}
func (self *SRegion) fetchZones() error {
type Region struct {
RegionID int64 `json:"RegionId"`
RegionName string `json:"RegionName"`
IsDefault bool `json:"IsDefault"`
BitMaps string `json:"BitMaps"`
Region string `json:"Region"`
Zone string `json:"Zone"`
}
params := NewUcloudParams()
regions := make([]Region, 0)
err := self.client.DoListAll("GetRegion", params, &regions)
if err != nil {
return err
}
for _, r := range regions {
if r.Region != self.GetId() {
continue
}
szone := SZone{}
szone.ZoneId = r.Zone
szone.RegionId = r.Region
szone.region = self
self.izones = append(self.izones, &szone)
}
return nil
}
func (self *SRegion) fetchIVpcs() error {
vpcs := make([]SVPC, 0)
params := NewUcloudParams()
err := self.DoListAll("DescribeVPC", params, &vpcs)
if err != nil {
return err
}
for _, vpc := range vpcs {
vpc.region = self
self.ivpcs = append(self.ivpcs, &vpc)
}
return nil
}
// https://docs.ucloud.cn/api/uhost-api/describe_uhost_instance
func (self *SRegion) GetInstanceByID(instanceId string) (SInstance, error) {
params := NewUcloudParams()
params.Set("UHostIds.0", instanceId)
instances := make([]SInstance, 0)
err := self.DoAction("DescribeUHostInstance", params, &instances)
if err != nil {
return SInstance{}, err
}
if len(instances) == 1 {
return instances[0], nil
} else if len(instances) == 0 {
return SInstance{}, cloudprovider.ErrNotFound
} else {
return SInstance{}, fmt.Errorf("GetInstanceByID %s %d found.", instanceId, len(instances))
}
}
func (self *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.SecurityRule) error {
return cloudprovider.ErrNotImplemented
}
+202
View File
@@ -0,0 +1,202 @@
package ucloud
import (
"fmt"
"net"
"strconv"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/pkg/util/secrules"
)
// https://docs.ucloud.cn/api/unet-api/describe_firewall
type SSecurityGroup struct {
region *SRegion
vpc *SVPC // 安全组在UCLOUD实际上与VPC是没有直接关联的。这里的vpc字段只是为了统一,仅仅是标记是哪个VPC在操作该安全组。
CreateTime int64 `json:"CreateTime"`
FWID string `json:"FWId"`
GroupID string `json:"GroupId"`
Name string `json:"Name"`
Remark string `json:"Remark"`
ResourceCount int `json:"ResourceCount"`
Rule []Rule `json:"Rule"`
Tag string `json:"Tag"`
Type string `json:"Type"`
}
func (self *SSecurityGroup) GetProjectId() string {
return self.region.client.projectId
}
type Rule struct {
DstPort string `json:"DstPort"`
Priority string `json:"Priority"`
ProtocolType string `json:"ProtocolType"`
RuleAction string `json:"RuleAction"`
SrcIP string `json:"SrcIP"`
}
func (self *SSecurityGroup) GetId() string {
return self.FWID
}
func (self *SSecurityGroup) GetName() string {
if len(self.Name) == 0 {
return self.GetId()
}
return self.Name
}
func (self *SSecurityGroup) GetGlobalId() string {
return self.GetId()
}
func (self *SSecurityGroup) GetStatus() string {
return ""
}
func (self *SSecurityGroup) Refresh() error {
if new, err := self.region.GetSecurityGroupById(self.GetId()); err != nil {
return err
} else {
return jsonutils.Update(self, new)
}
}
func (self *SSecurityGroup) IsEmulated() bool {
return false
}
func (self *SSecurityGroup) GetMetadata() *jsonutils.JSONDict {
data := jsonutils.NewDict()
return data
}
func (self *SSecurityGroup) GetDescription() string {
return self.Remark
}
func (self *SSecurityGroup) UcloudSecRuleToOnecloud(rule Rule) secrules.SecurityRule {
secrule := secrules.SecurityRule{}
switch rule.Priority {
case "HIGH":
secrule.Priority = 90
case "MEDIUM":
secrule.Priority = 60
case "LOW":
secrule.Priority = 30
default:
secrule.Priority = 1
}
switch rule.RuleAction {
case "ACCEPT":
secrule.Action = secrules.SecurityRuleAllow
case "DROP":
secrule.Action = secrules.SecurityRuleDeny
default:
secrule.Action = secrules.SecurityRuleDeny
}
_, ipNet, err := net.ParseCIDR(rule.SrcIP)
if err != nil {
log.Errorf(err.Error())
}
secrule.IPNet = ipNet
secrule.Protocol = strings.ToLower(rule.ProtocolType)
secrule.Direction = secrules.SecurityRuleIngress
if rule.DstPort == "" {
secrule.PortStart = -1
secrule.PortEnd = -1
} else if strings.Contains(rule.DstPort, "-") {
segs := strings.Split(rule.DstPort, "-")
s, err := strconv.Atoi(segs[0])
if err != nil {
log.Errorf(err.Error())
}
e, err := strconv.Atoi(segs[1])
if err != nil {
log.Errorf(err.Error())
}
secrule.PortStart = s
secrule.PortEnd = e
} else {
port, err := strconv.Atoi(rule.DstPort)
if err != nil {
log.Errorf(err.Error())
}
secrule.PortStart = port
secrule.PortEnd = port
}
return secrule
}
// https://docs.ucloud.cn/network/firewall/firewall
// 貌似没有出方向规则
// todo: fix me
func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
rules := make([]secrules.SecurityRule, 0)
for _, r := range self.Rule {
rule := self.UcloudSecRuleToOnecloud(r)
rules = append(rules, rule)
}
return rules, nil
}
func (self *SSecurityGroup) GetVpcId() string {
// 无vpc关联的安全组统一返回normal
return "normal"
}
func (self *SRegion) GetSecurityGroupById(secGroupId string) (*SSecurityGroup, error) {
secgroups, err := self.GetSecurityGroups(secGroupId, "")
if err != nil {
return nil, err
}
if len(secgroups) == 1 {
return &secgroups[0], nil
} else if len(secgroups) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("GetSecurityGroupById %s %d found", secGroupId, len(secgroups))
}
}
func (self *SRegion) CreateSecurityGroup(name, description string) (string, error) {
return "", cloudprovider.ErrNotImplemented
}
// https://docs.ucloud.cn/api/unet-api/describe_firewall
func (self *SRegion) GetSecurityGroups(secGroupId string, resourceId string) ([]SSecurityGroup, error) {
secgroups := make([]SSecurityGroup, 0)
params := NewUcloudParams()
if len(secGroupId) > 0 {
params.Set("FWId", secGroupId)
}
if len(resourceId) > 0 {
params.Set("ResourceId", resourceId)
params.Set("ResourceType", "uhost") // 默认只支持"uhost",云主机
}
err := self.DoListAll("DescribeFirewall", params, &secgroups)
if err != nil {
return nil, err
}
for i := range secgroups {
secgroups[i].region = self
}
return secgroups, nil
}
+139
View File
@@ -0,0 +1,139 @@
package ucloud
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
// https://docs.ucloud.cn/api/udisk-api/describe_udisk_snapshot
type SSnapshot struct {
region *SRegion
Comment string `json:"Comment"`
ChargeType string `json:"ChargeType"`
Name string `json:"Name"`
UDiskName string `json:"UDiskName"`
ExpiredTime int64 `json:"ExpiredTime"`
UDiskID string `json:"UDiskId"`
SnapshotID string `json:"SnapshotId"`
CreateTime int64 `json:"CreateTime"`
SizeGB int32 `json:"Size"`
Status string `json:"Status"`
IsUDiskAvailable bool `json:"IsUDiskAvailable"`
Version string `json:"Version"`
DiskType int `json:"DiskType"`
UHostID string `json:"UHostId"`
}
func (self *SSnapshot) GetProjectId() string {
return self.region.client.projectId
}
func (self *SSnapshot) GetId() string {
return self.SnapshotID
}
func (self *SSnapshot) GetName() string {
if len(self.Name) == 0 {
return self.GetId()
}
return self.Name
}
func (self *SSnapshot) GetGlobalId() string {
return self.GetId()
}
// 快照状态,Normal:正常,Failed:失败,Creating:制作中
func (self *SSnapshot) GetStatus() string {
switch self.Status {
case "Normal":
return models.SNAPSHOT_READY
case "Failed":
return models.SNAPSHOT_FAILED
case "Creating":
return models.SNAPSHOT_CREATING
default:
return models.SNAPSHOT_UNKNOWN
}
}
func (self *SSnapshot) Refresh() error {
snapshot, err := self.region.GetSnapshotById(self.GetId())
if err != nil {
return err
}
if err := jsonutils.Update(self, snapshot); err != nil {
return err
}
return nil
}
func (self *SSnapshot) IsEmulated() bool {
return false
}
func (self *SSnapshot) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SSnapshot) GetSize() int32 {
return self.SizeGB
}
func (self *SSnapshot) GetDiskId() string {
return self.UDiskID
}
// 磁盘类型,0:数据盘,1:系统盘
func (self *SSnapshot) GetDiskType() string {
if self.DiskType == 1 {
return models.DISK_TYPE_SYS
} else {
return models.DISK_TYPE_DATA
}
}
func (self *SSnapshot) Delete() error {
panic("implement me")
}
func (self *SRegion) GetSnapshotById(snapshotId string) (SSnapshot, error) {
snapshots, err := self.GetSnapshots("", snapshotId)
if err != nil {
return SSnapshot{}, err
}
if len(snapshots) == 1 {
return snapshots[0], nil
} else if len(snapshots) == 0 {
return SSnapshot{}, cloudprovider.ErrNotFound
} else {
return SSnapshot{}, fmt.Errorf("GetSnapshotById %s %d found", snapshotId, len(snapshots))
}
}
func (self *SRegion) GetSnapshots(diskId string, snapshotId string) ([]SSnapshot, error) {
snapshots := make([]SSnapshot, 0)
params := NewUcloudParams()
if len(diskId) == 0 {
params.Set("UDiskId", diskId)
}
if len(snapshotId) == 0 {
params.Set("SnapshotId", snapshotId)
}
err := self.DoAction("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
return snapshots, nil
}
+127
View File
@@ -0,0 +1,127 @@
package ucloud
import (
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SStorage struct {
zone *SZone
storageType string
}
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) GetStatus() string {
return models.STORAGE_ONLINE
}
func (self *SStorage) Refresh() error {
return nil
}
func (self *SStorage) IsEmulated() bool {
return true
}
func (self *SStorage) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
return self.zone.region.getStoragecache()
}
func (self *SStorage) GetIZone() cloudprovider.ICloudZone {
return self.zone
}
func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
disks, err := self.zone.region.GetDisks(self.zone.GetId(), "", nil)
if err != nil {
return nil, err
}
filtedDisks := make([]SDisk, 0)
for _, disk := range disks {
// ssd 盘
if self.storageType == models.STORAGE_UCLOUD_SSD && strings.Contains(disk.DiskType, models.STORAGE_UCLOUD_SSD) {
filtedDisks = append(filtedDisks, disk)
}
// 普通盘
if self.storageType == models.STORAGE_UCLOUD_SATA && !strings.Contains(disk.DiskType, models.STORAGE_UCLOUD_SSD) {
filtedDisks = append(filtedDisks, disk)
}
}
idisks := make([]cloudprovider.ICloudDisk, len(filtedDisks))
for i := 0; i < len(filtedDisks); i += 1 {
filtedDisks[i].storage = self
idisks[i] = &filtedDisks[i]
}
return idisks, nil
}
func (self *SStorage) GetStorageType() string {
return self.storageType
}
func (self *SStorage) GetMediumType() string {
if self.storageType == models.STORAGE_UCLOUD_SSD {
return models.DISK_TYPE_SSD
} else {
return models.DISK_TYPE_ROTATE
}
}
func (self *SStorage) GetCapacityMB() int {
return 0 // unlimited
}
func (self *SStorage) GetStorageConf() jsonutils.JSONObject {
return jsonutils.NewDict()
}
func (self *SStorage) GetEnabled() bool {
return true
}
func (self *SStorage) GetManagerId() string {
return self.zone.region.client.providerId
}
func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
panic("implement me")
}
func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) {
if disk, err := self.zone.region.GetDisk(idStr); err != nil {
return nil, err
} else {
disk.storage = self
return disk, nil
}
}
func (self *SStorage) GetMountPoint() string {
return ""
}
func (self *SStorage) IsSysDiskStore() bool {
return true
}
+94
View File
@@ -0,0 +1,94 @@
package ucloud
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SStoragecache struct {
region *SRegion
iimages []cloudprovider.ICloudImage
}
func (self *SStoragecache) fetchImages() error {
images, err := self.region.GetImages("", "")
if err != nil {
return err
}
for _, image := range images {
image.storageCache = self
self.iimages = append(self.iimages, &image)
}
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) GetGlobalId() string {
return fmt.Sprintf("%s-%s", self.region.client.providerId, self.region.GetGlobalId())
}
func (self *SStoragecache) GetStatus() string {
return "available"
}
func (self *SStoragecache) Refresh() error {
return nil
}
func (self *SStoragecache) IsEmulated() bool {
return false
}
func (self *SStoragecache) GetMetadata() *jsonutils.JSONDict {
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) GetIImageById(extId string) (cloudprovider.ICloudImage, error) {
image, err := self.region.GetImage(extId)
image.storageCache = self
return &image, err
}
func (self *SStoragecache) GetPath() string {
return ""
}
func (self *SStoragecache) GetManagerId() string {
return self.region.client.providerId
}
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
panic("implement me")
}
func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) {
panic("implement me")
}
func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist, osVersion string, extId string, isForce bool) (string, error) {
panic("implement me")
}
+247
View File
@@ -0,0 +1,247 @@
package ucloud
import (
"fmt"
"net/http"
"strings"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
/*
UCLOUD 项目:https://docs.ucloud.cn/management_monitor/uproject/projects
项目可认为是云账户下承载资源的容器,当您注册一个UCloud云账户后,系统会默认创建一个项目,您属于的资源都落在此项目下。如您有新的业务要使用云服务,可创建一个新项目,并将新业务部署在新项目下,实现业务之间的网络与逻辑隔离。
1、项目之间默认网络与逻辑隔离,即项目A的主机无法绑定项目B的EIP,默认也无法与项目B的主机内网通信。但联通项目后,uhost、udb、umem可实现内网通信。
2、资源不能在项目间迁移,即项目A内的主机无法迁移至项目B,因其不在一个基础网络内,且逻辑上也是隔离的。但诸如自主镜像等静态资源,您可以提交工单申请迁移至其他项目。
3、只有云账户本身,才能删除项目,且必须是项目被没有资源、没有任何子成员、未与其他项目联通的情况下才可删除。
UCloud DiskType貌似也是一个奇葩的存在
1.在主机创建查询接口中 DISK type 对应 CLOUD_SSD|CLOUD_SSD
2.在数据盘创建中对应 DataDisk|SSDDataDisk
3.在数据盘查询接口请求中对应 DataDisk|SystemDisk 。在结果中对应DataDisk|SSDDataDisk|SSDSystemDisk|SystemDisk
*/
const (
CLOUD_PROVIDER_UCLOUD = models.CLOUD_PROVIDER_UCLOUD
CLOUD_PROVIDER_UCLOUD_CN = "UCloud"
UCLOUD_DEFAULT_REGION = "cn-bj2"
UCLOUD_API_VERSION = "2019-02-28"
)
type SUcloudClient struct {
providerId string
providerName string
accessKeyId string
accessKeySecret string
projectId string
iregions []cloudprovider.ICloudRegion
httpClient *http.Client
Debug bool
}
func parseAccount(account string) (accessKey string, projectId string) {
segs := strings.Split(account, "::")
if len(segs) == 2 {
accessKey = segs[0]
projectId = segs[1]
} else {
accessKey = account
projectId = ""
}
return
}
// 进行资源操作时参数account 对应数据库cloudprovider表中的account字段,由accessKey和projectID两部分组成,通过"/"分割。
// 初次导入Subaccount时,参数account对应cloudaccounts表中的account字段,即accesskey。此时projectID为空,只能进行同步子账号(项目)、查询region列表等projectId无关的操作。
func NewUcloudClient(providerId string, providerName string, account string, secret string, isDebug bool) (*SUcloudClient, error) {
accessKey, projectId := parseAccount(account)
client := SUcloudClient{
providerId: providerId,
providerName: providerName,
accessKeyId: accessKey,
accessKeySecret: secret,
projectId: projectId,
Debug: isDebug,
}
err := client.fetchRegions()
if err != nil {
return nil, err
}
return &client, nil
}
func (self *SUcloudClient) UpdateAccount(accessKey, secret string) error {
if self.accessKeyId != accessKey || self.accessKeySecret != secret {
self.accessKeyId = accessKey
self.accessKeySecret = secret
return self.fetchRegions()
} else {
return nil
}
}
func (self *SUcloudClient) commonParams(params SParams, action string) (string, SParams) {
resultKey, exists := UCLOUD_API_RESULT_KEYS[action]
if !exists || len(resultKey) == 0 {
log.Debugf("Do %s no result key found or result key is empty", action)
}
if len(self.projectId) > 0 {
params.Set("ProjectId", self.projectId)
}
params.Set("PublicKey", self.accessKeyId)
return resultKey, params
}
func (self *SUcloudClient) DoListAll(action string, params SParams, result interface{}) error {
resultKey, params := self.commonParams(params, action)
return DoListAll(self, action, params, resultKey, result)
}
func (self *SUcloudClient) DoListPart(action string, limit int, offset int, params SParams, result interface{}) (int, int, error) {
resultKey, params := self.commonParams(params, action)
params.SetPagination(limit, offset)
return doListPart(self, action, params, resultKey, result)
}
func (self *SUcloudClient) DoAction(action string, params SParams, result interface{}) error {
resultKey, params := self.commonParams(params, action)
return DoAction(self, action, params, resultKey, result)
}
func (self *SUcloudClient) fetchRegions() error {
type Region struct {
RegionID int64 `json:"RegionId"`
RegionName string `json:"RegionName"`
IsDefault bool `json:"IsDefault"`
BitMaps string `json:"BitMaps"`
Region string `json:"Region"`
Zone string `json:"Zone"`
}
params := NewUcloudParams()
regions := make([]Region, 0)
err := self.DoListAll("GetRegion", params, &regions)
if err != nil {
return err
}
regionSet := make(map[string]string, 0)
for _, region := range regions {
regionSet[region.Region] = region.Region
}
sregions := make([]SRegion, len(regionSet))
self.iregions = make([]cloudprovider.ICloudRegion, len(regionSet))
i := 0
for regionId := range regionSet {
sregions[i].client = self
sregions[i].RegionID = regionId
self.iregions[i] = &sregions[i]
i += 1
}
return nil
}
func (self *SUcloudClient) GetRegions() []SRegion {
regions := make([]SRegion, len(self.iregions))
for i := 0; i < len(regions); i += 1 {
region := self.iregions[i].(*SRegion)
regions[i] = *region
}
return regions
}
func (self *SUcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
projects, err := self.FetchProjects()
if err != nil {
return nil, err
}
subAccounts := make([]cloudprovider.SSubAccount, 0)
for _, project := range projects {
subAccount := cloudprovider.SSubAccount{}
subAccount.Name = self.providerName
// ucloud账号ID中可能包含/。因此使用::作为分割符号
subAccount.Account = fmt.Sprintf("%s::%s", self.accessKeyId, project.ProjectID)
subAccount.HealthStatus = models.CLOUD_PROVIDER_HEALTH_NORMAL
subAccounts = append(subAccounts, subAccount)
}
return subAccounts, nil
}
func (self *SUcloudClient) GetIRegions() []cloudprovider.ICloudRegion {
return self.iregions
}
func (self *SUcloudClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
for i := 0; i < len(self.iregions); i += 1 {
if self.iregions[i].GetGlobalId() == id {
return self.iregions[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SUcloudClient) GetRegion(regionId string) *SRegion {
if len(regionId) == 0 {
regionId = UCLOUD_DEFAULT_REGION
}
for i := 0; i < len(self.iregions); i += 1 {
if self.iregions[i].GetId() == regionId {
return self.iregions[i].(*SRegion)
}
}
return nil
}
func (self *SUcloudClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
for i := 0; i < len(self.iregions); i += 1 {
ihost, err := self.iregions[i].GetIHostById(id)
if err == nil {
return ihost, nil
} else if err != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SUcloudClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
for i := 0; i < len(self.iregions); i += 1 {
ihost, err := self.iregions[i].GetIVpcById(id)
if err == nil {
return ihost, nil
} else if err != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SUcloudClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
for i := 0; i < len(self.iregions); i += 1 {
ihost, err := self.iregions[i].GetIStorageById(id)
if err == nil {
return ihost, nil
} else if err != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
+90
View File
@@ -0,0 +1,90 @@
package ucloud
import (
"reflect"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
)
func unmarshalResult(resp jsonutils.JSONObject, respErr error, resultKey string, result interface{}) error {
if respErr != nil {
return respErr
}
if result == nil {
return nil
}
if len(resultKey) > 0 {
respErr = resp.Unmarshal(result, resultKey)
} else {
respErr = resp.Unmarshal(result)
}
if respErr != nil {
log.Errorf("unmarshal json error %s", respErr)
}
return nil
}
func doListPart(client *SUcloudClient, action string, params SParams, resultKey string, result interface{}) (int, int, error) {
params.SetAction(action)
ret, err := jsonRequest(client, params)
if err != nil {
return 0, 0, err
}
total, err := ret.Int("TotalCount")
if err != nil {
log.Debugf("%s TotalCount", action)
// return 0, 0, err
}
var lst []jsonutils.JSONObject
lst, err = ret.GetArray(resultKey)
if err != nil {
return 0, 0, nil
}
resultValue := reflect.Indirect(reflect.ValueOf(result))
elemType := resultValue.Type().Elem()
for i := range lst {
elemPtr := reflect.New(elemType)
err = lst[i].Unmarshal(elemPtr.Interface())
if err != nil {
return 0, 0, err
}
resultValue.Set(reflect.Append(resultValue, elemPtr.Elem()))
}
return int(total), len(lst), nil
}
// 执行操作
func DoAction(client *SUcloudClient, action string, params SParams, resultKey string, result interface{}) error {
params.SetAction(action)
resp, err := jsonRequest(client, params)
return unmarshalResult(resp, err, resultKey, result)
}
// 遍历所有结果
func DoListAll(client *SUcloudClient, action string, params SParams, resultKey string, result interface{}) error {
pageLimit := 100
offset := 0
resultValue := reflect.Indirect(reflect.ValueOf(result))
params.SetPagination(pageLimit, offset)
for {
total, part, err := doListPart(client, action, params, resultKey, result)
if err != nil {
return err
}
if (total > 0 && resultValue.Len() >= total) || (total == 0 && pageLimit > part) {
break
}
params.SetPagination(pageLimit, offset+resultValue.Len())
}
return nil
}
+224
View File
@@ -0,0 +1,224 @@
package ucloud
import (
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SVPC struct {
region *SRegion
iwires []cloudprovider.ICloudWire
secgroups []cloudprovider.ICloudSecurityGroup
CreateTime int64 `json:"CreateTime"`
Name string `json:"Name"`
Network []string `json:"Network"`
NetworkInfo []NetworkInfo `json:"NetworkInfo"`
SubnetCount int `json:"SubnetCount"`
Tag string `json:"Tag"`
UpdateTime int64 `json:"UpdateTime"`
VPCID string `json:"VPCId"`
}
type NetworkInfo struct {
Network string `json:"Network"`
SubnetCount int `json:"SubnetCount"`
}
func (self *SVPC) addWire(wire *SWire) {
if self.iwires == nil {
self.iwires = make([]cloudprovider.ICloudWire, 0)
}
self.iwires = append(self.iwires, wire)
}
func (self *SVPC) GetId() string {
return self.VPCID
}
func (self *SVPC) GetName() string {
if len(self.Name) > 0 {
return self.Name
}
return self.VPCID
}
func (self *SVPC) GetGlobalId() string {
return self.GetId()
}
func (self *SVPC) GetStatus() string {
return models.VPC_STATUS_AVAILABLE
}
func (self *SVPC) Refresh() error {
new, err := self.region.getVpc(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SVPC) IsEmulated() bool {
return false
}
func (self *SVPC) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SVPC) GetRegion() cloudprovider.ICloudRegion {
return self.region
}
func (self *SVPC) GetIsDefault() bool {
return false
}
func (self *SVPC) GetCidrBlock() string {
return strings.Join(self.Network, ",")
}
func (self *SVPC) GetIWires() ([]cloudprovider.ICloudWire, error) {
if self.iwires == nil {
err := self.fetchNetworks()
if err != nil {
return nil, err
}
}
return self.iwires, nil
}
// 由于Ucloud 安全组和vpc没有直接关联,这里是返回同一个项目下的防火墙列表,会导致重复同步的问题。
// https://docs.ucloud.cn/api/unet-api/grant_firewall
func (self *SVPC) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
if self.secgroups == nil {
err := self.fetchSecurityGroups()
if err != nil {
return nil, err
}
}
return self.secgroups, nil
}
func (self *SVPC) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) {
rts := []cloudprovider.ICloudRouteTable{}
return rts, nil
}
func (self *SVPC) GetManagerId() string {
return self.region.client.providerId
}
func (self *SVPC) Delete() error {
return self.region.DeleteVpc(self.GetId())
}
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) fetchNetworks() error {
networks, err := self.region.GetNetworks(self.GetId())
if err != nil {
return err
}
for i := 0; i < len(networks); i += 1 {
wire := self.getWireByRegionId(self.region.GetId())
networks[i].wire = wire
wire.addNetwork(&networks[i])
}
return nil
}
func (self *SVPC) getWireByRegionId(regionId string) *SWire {
if len(regionId) == 0 {
return nil
}
for i := 0; i < len(self.iwires); i++ {
wire := self.iwires[i].(*SWire)
if wire.region.GetId() == regionId {
return wire
}
}
return nil
}
func (self *SRegion) getVpc(vpcId string) (*SVPC, error) {
vpcs, err := self.GetVpcs(vpcId)
if err != nil {
return nil, err
}
if len(vpcs) == 1 {
return &vpcs[0], nil
} else if len(vpcs) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("getVpc %s %d found", vpcId, len(vpcs))
}
}
func (self *SRegion) DeleteVpc(vpcId string) error {
return cloudprovider.ErrNotImplemented
}
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090625.html
func (self *SRegion) GetVpcs(vpcId string) ([]SVPC, error) {
vpcs := make([]SVPC, 0)
params := NewUcloudParams()
if len(vpcId) > 0 {
params.Set("VPCIds.0", vpcId)
}
err := self.DoListAll("DescribeVPC", params, &vpcs)
return vpcs, err
}
func (self *SRegion) GetNetworks(vpcId string) ([]SNetwork, error) {
params := NewUcloudParams()
if len(vpcId) == 0 {
params.Set("VPCId", vpcId)
}
networks := make([]SNetwork, 0)
err := self.DoAction("DescribeSubnet", params, &networks)
return networks, err
}
// UCLOUD 同一个项目共用安全组(防火墙)
func (self *SVPC) fetchSecurityGroups() error {
secgroups, err := self.region.GetSecurityGroups("", "")
if err != nil {
return err
}
self.secgroups = make([]cloudprovider.ICloudSecurityGroup, len(secgroups))
for i := 0; i < len(secgroups); i++ {
secgroups[i].vpc = self
secgroups[i].region = self.region
self.secgroups[i] = &secgroups[i]
}
return nil
}
+99
View File
@@ -0,0 +1,99 @@
package ucloud
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
// 子网在整个region可用
type SWire struct {
region *SRegion
vpc *SVPC
inetworks []cloudprovider.ICloudNetwork
}
func (self *SWire) GetId() string {
return fmt.Sprintf("%s-%s", self.vpc.GetId(), self.region.GetId())
}
func (self *SWire) GetName() string {
return self.GetId()
}
func (self *SWire) GetGlobalId() string {
return fmt.Sprintf("%s-%s", self.vpc.GetGlobalId(), self.region.GetGlobalId())
}
func (self *SWire) GetStatus() string {
return "available"
}
func (self *SWire) Refresh() error {
return nil
}
func (self *SWire) IsEmulated() bool {
return true
}
func (self *SWire) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SWire) GetIVpc() cloudprovider.ICloudVpc {
return self.vpc
}
func (self *SWire) GetIZone() cloudprovider.ICloudZone {
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) GetBandwidth() int {
return 10000
}
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) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
panic("implement me")
}
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.GetId() {
find = true
break
}
}
if !find {
self.inetworks = append(self.inetworks, network)
}
}
+154
View File
@@ -0,0 +1,154 @@
package ucloud
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
)
// https://docs.ucloud.cn/api/udisk-api/create_udisk
// UDisk 类型: DataDisk(普通数据盘),SSDDataDiskSSD数据盘),默认值(DataDisk
var StorageTypes = []string{
models.STORAGE_UCLOUD_SATA,
models.STORAGE_UCLOUD_SSD,
}
type SZone struct {
region *SRegion
host *SHost
iwires []cloudprovider.ICloudWire
istorages []cloudprovider.ICloudStorage
RegionId string
ZoneId string
/* 支持的磁盘种类集合 */
storageTypes []string
}
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) getHost() *SHost {
if self.host == nil {
self.host = &SHost{zone: self, projectId: self.region.client.projectId}
}
return self.host
}
func (self *SZone) getStorageType() {
if len(self.storageTypes) == 0 {
self.storageTypes = StorageTypes
}
}
func (self *SZone) fetchStorages() error {
self.getStorageType()
self.istorages = make([]cloudprovider.ICloudStorage, len(self.storageTypes))
for i, sc := range self.storageTypes {
storage := SStorage{zone: self, storageType: sc}
self.istorages[i] = &storage
}
return nil
}
func (self *SZone) GetId() string {
return self.ZoneId
}
func (self *SZone) GetName() string {
if name, exists := UCLOUD_ZONE_NAMES[self.GetId()]; exists {
return name
}
return self.GetId()
}
func (self *SZone) GetGlobalId() string {
return fmt.Sprintf("%s/%s", self.region.GetGlobalId(), self.GetId())
}
func (self *SZone) GetStatus() string {
return models.ZONE_ENABLE
}
func (self *SZone) Refresh() error {
return nil
}
func (self *SZone) IsEmulated() bool {
return false
}
func (self *SZone) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SZone) GetIRegion() cloudprovider.ICloudRegion {
return self.region
}
func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
return []cloudprovider.ICloudHost{self.getHost()}, nil
}
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) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
if self.istorages == nil {
self.fetchStorages()
}
return self.istorages, nil
}
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
}
// https://docs.ucloud.cn/api/uhost-api/describe_uhost_instance
func (self *SZone) GetInstances() ([]SInstance, error) {
return self.region.GetInstances(self.GetId(), "")
}
func (self *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) {
return self.iwires, nil
}
// https://docs.ucloud.cn/api/uhost-api/describe_uhost_instance
func (self *SRegion) GetInstances(zoneId string, instanceId string) ([]SInstance, error) {
instances := make([]SInstance, 0)
params := NewUcloudParams()
if len(zoneId) > 0 {
params.Set("Zone", zoneId)
}
if len(instanceId) > 0 {
params.Set("UHostIds.0", instanceId)
}
err := self.DoListAll("DescribeUHostInstance", params, &instances)
return instances, err
}