fix(region): aws dedicated host sync (#25040)

This commit is contained in:
屈轩
2026-06-16 20:14:27 +08:00
committed by GitHub
parent 4bce487605
commit 0956e660a4
43 changed files with 504 additions and 6843 deletions
+1 -1
View File
@@ -108,7 +108,7 @@ require (
k8s.io/cri-api v0.28.15
k8s.io/klog/v2 v2.90.1
moul.io/http2curl/v2 v2.3.0
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260615092745-9325294dbdb4
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260616113329-0b3b4fbed80a
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
+2 -2
View File
@@ -1783,8 +1783,8 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260615092745-9325294dbdb4 h1:RuUQ+U5O1Jw74Fru/gGTASgS6rSBCZvNVymypI7tb9M=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260615092745-9325294dbdb4/go.mod h1:rCz5Nds6Zi/5uaj51oRDYdj5fZ5uULmKbuBAgNLuI+w=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260616113329-0b3b4fbed80a h1:6R6t1tPpU/aR61wj5zCSC95KMglfM1ES85nbTneZSrk=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260616113329-0b3b4fbed80a/go.mod h1:rCz5Nds6Zi/5uaj51oRDYdj5fZ5uULmKbuBAgNLuI+w=
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005 h1:3sWwcjGXGjG9mLBWa7AyLq+QSi0udTAx21pfVQRFMBE=
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
+1
View File
@@ -216,6 +216,7 @@ var (
},
CLOUD_PROVIDER_AWS: {
HOST_TYPE_AWS,
HOST_TYPE_DEDICATED,
},
CLOUD_PROVIDER_HUAWEI: {
HOST_TYPE_HUAWEI,
+2
View File
@@ -30,6 +30,7 @@ const (
HOST_TYPE_ALIYUN = compute.HOST_TYPE_ALIYUN
HOST_TYPE_APSARA = compute.HOST_TYPE_APSARA
HOST_TYPE_AWS = compute.HOST_TYPE_AWS
HOST_TYPE_DEDICATED = compute.HOST_TYPE_DEDICATED
HOST_TYPE_QCLOUD = compute.HOST_TYPE_QCLOUD
HOST_TYPE_AZURE = compute.HOST_TYPE_AZURE
HOST_TYPE_HUAWEI = compute.HOST_TYPE_HUAWEI
@@ -140,6 +141,7 @@ var HOST_TYPES = []string{
HOST_TYPE_APSARA,
HOST_TYPE_AZURE,
HOST_TYPE_AWS,
HOST_TYPE_DEDICATED,
HOST_TYPE_QCLOUD,
HOST_TYPE_HUAWEI,
HOST_TYPE_HCSO,
@@ -12,4 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package provider // import "yunion.io/x/cloudmux/pkg/multicloud/rockbase/provider"
package hostdrivers
import (
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SAwsDedicatedHostDriver struct {
SAwsHostDriver
}
func init() {
driver := SAwsDedicatedHostDriver{}
models.RegisterHostDriver(&driver)
}
func (self *SAwsDedicatedHostDriver) GetHostType() string {
return api.HOST_TYPE_DEDICATED
}
+1 -3
View File
@@ -2598,7 +2598,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
# sigs.k8s.io/yaml v1.3.0
## explicit; go 1.12
sigs.k8s.io/yaml
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260615092745-9325294dbdb4
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260616113329-0b3b4fbed80a
## explicit; go 1.24
yunion.io/x/cloudmux/pkg/apis
yunion.io/x/cloudmux/pkg/apis/billing
@@ -2687,8 +2687,6 @@ yunion.io/x/cloudmux/pkg/multicloud/qingcloud
yunion.io/x/cloudmux/pkg/multicloud/qingcloud/provider
yunion.io/x/cloudmux/pkg/multicloud/remotefile
yunion.io/x/cloudmux/pkg/multicloud/remotefile/provider
yunion.io/x/cloudmux/pkg/multicloud/rockbase
yunion.io/x/cloudmux/pkg/multicloud/rockbase/provider
yunion.io/x/cloudmux/pkg/multicloud/ucloud
yunion.io/x/cloudmux/pkg/multicloud/ucloud/provider
yunion.io/x/cloudmux/pkg/multicloud/volcengine
+2 -1
View File
@@ -22,6 +22,7 @@ const (
HOST_TYPE_ALIYUN = "aliyun"
HOST_TYPE_APSARA = "apsara"
HOST_TYPE_AWS = "aws"
HOST_TYPE_DEDICATED = "dedicated"
HOST_TYPE_QCLOUD = "qcloud"
HOST_TYPE_AZURE = "azure"
HOST_TYPE_HUAWEI = "huawei"
@@ -53,7 +54,7 @@ const (
HOST_TYPE_UIS = "uis"
HOST_TYPE_CAS = "cas"
HOST_TYPE_CNWARE = "cnware"
HOST_TYPE_ROCKBASE = "rockbase"
HOST_TYPE_ROCKBASE = "rockbase"
// # possible status
HOST_ONLINE = "online"
+428
View File
@@ -0,0 +1,428 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aws
import (
"fmt"
"time"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
const (
DedicatedHostStateAvailable = "available"
DedicatedHostStatePending = "pending"
DedicatedHostStateUnderAssessment = "under-assessment"
DedicatedHostStatePermanentFailure = "permanent-failure"
DedicatedHostStateReleased = "released"
)
type SDedicatedHost struct {
multicloud.SHostBase
AwsTags
zone *SZone
AllocationTime time.Time `xml:"allocationTime"`
AvailabilityZone string `xml:"availabilityZone"`
AvailableCapacity struct {
AvailableInstanceCapacity []struct {
AvailableCapacity int `xml:"availableCapacity"`
InstanceType string `xml:"instanceType"`
TotalCapacity int `xml:"totalCapacity"`
} `xml:"availableInstanceCapacity>item"`
AvailableVCpus int `xml:"availableVCpus"`
} `xml:"availableCapacity"`
ClientToken string `xml:"clientToken"`
HostId string `xml:"hostId"`
HostProperties struct {
Cores int `xml:"cores"`
InstanceFamily string `xml:"instanceFamily"`
InstanceType string `xml:"instanceType"`
Sockets int `xml:"sockets"`
TotalVCpus int64 `xml:"totalVCpus"`
} `xml:"hostProperties"`
Instances []struct {
InstanceId string `xml:"instanceId"`
InstanceType string `xml:"instanceType"`
} `xml:"instances>item"`
ReleaseTime time.Time `xml:"releaseTime"`
State string `xml:"state"`
}
func (self *SRegion) GetDedicatedHosts(zoneName string, hostIds ...string) ([]SDedicatedHost, error) {
params := map[string]string{}
if len(hostIds) > 0 {
for i, id := range hostIds {
params[fmt.Sprintf("HostId.%d", i+1)] = id
}
} else {
idx := 1
if len(zoneName) > 0 {
params[fmt.Sprintf("Filter.%d.Name", idx)] = "availability-zone"
params[fmt.Sprintf("Filter.%d.Value.1", idx)] = zoneName
idx++
}
// skip released hosts
params[fmt.Sprintf("Filter.%d.Name", idx)] = "state"
for i, state := range []string{
DedicatedHostStateAvailable,
DedicatedHostStatePending,
DedicatedHostStateUnderAssessment,
DedicatedHostStatePermanentFailure,
} {
params[fmt.Sprintf("Filter.%d.Value.%d", idx, i+1)] = state
}
}
ret := []SDedicatedHost{}
for {
part := struct {
HostSet []SDedicatedHost `xml:"hostSet>item"`
NextToken string `xml:"nextToken"`
}{}
err := self.ec2Request("DescribeHosts", params, &part)
if err != nil {
return nil, errors.Wrap(err, "DescribeHosts")
}
ret = append(ret, part.HostSet...)
if len(part.NextToken) == 0 {
break
}
params["NextToken"] = part.NextToken
}
return ret, nil
}
func (self *SRegion) GetDedicatedHost(hostId string) (*SDedicatedHost, error) {
hosts, err := self.GetDedicatedHosts("", hostId)
if err != nil {
return nil, err
}
if len(hosts) == 0 {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", hostId)
}
return &hosts[0], nil
}
func (self *SRegion) AllocateDedicatedHost(zoneName, instanceType, name string, quantity int, autoPlacement bool) ([]SDedicatedHost, error) {
if len(zoneName) == 0 {
return nil, fmt.Errorf("missing availability zone")
}
if len(instanceType) == 0 {
return nil, fmt.Errorf("missing instance type")
}
if quantity <= 0 {
quantity = 1
}
params := map[string]string{
"AvailabilityZone": zoneName,
"InstanceType": instanceType,
"Quantity": fmt.Sprintf("%d", quantity),
}
if autoPlacement {
params["AutoPlacement"] = "on"
} else {
params["AutoPlacement"] = "off"
}
if len(name) > 0 {
params["TagSpecification.1.ResourceType"] = "dedicated-host"
params["TagSpecification.1.Tag.1.Key"] = "Name"
params["TagSpecification.1.Tag.1.Value"] = name
}
ret := struct {
HostSet []SDedicatedHost `xml:"hostSet>item"`
}{}
err := self.ec2Request("AllocateHosts", params, &ret)
if err != nil {
return nil, errors.Wrap(err, "AllocateHosts")
}
return ret.HostSet, nil
}
func (self *SRegion) ReleaseDedicatedHost(hostId string) error {
params := map[string]string{
"HostId.1": hostId,
}
return self.ec2Request("ReleaseHosts", params, nil)
}
func (self *SDedicatedHost) GetId() string {
return self.HostId
}
func (self *SDedicatedHost) GetName() string {
name := self.AwsTags.GetName()
if len(name) > 0 {
return name
}
return self.HostId
}
func (self *SDedicatedHost) GetGlobalId() string {
return self.HostId
}
func (self *SDedicatedHost) GetStatus() string {
switch self.State {
case DedicatedHostStateAvailable:
return api.HOST_STATUS_RUNNING
case DedicatedHostStatePending:
return api.HOST_STATUS_READY
case DedicatedHostStateReleased:
return api.HOST_STATUS_UNKNOWN
default:
return api.HOST_STATUS_UNKNOWN
}
}
func (self *SDedicatedHost) Refresh() error {
hosts, err := self.zone.region.GetDedicatedHosts(self.zone.ZoneName)
if err != nil {
return errors.Wrap(err, "GetDedicatedHosts")
}
for i := range hosts {
if hosts[i].HostId == self.HostId {
return jsonutils.Update(self, &hosts[i])
}
}
return errors.Wrapf(cloudprovider.ErrNotFound, "%s", self.HostId)
}
func (self *SDedicatedHost) IsEmulated() bool {
return false
}
func (self *SDedicatedHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
vms, err := self.zone.region.GetInstances(self.zone.ZoneName, "", nil)
if err != nil {
return nil, errors.Wrap(err, "GetInstances")
}
ivms := make([]cloudprovider.ICloudVM, 0, len(vms))
for i := range vms {
if vms[i].Placement.HostId != self.HostId {
continue
}
bindInstanceHost(&vms[i], self.zone, self)
ivms = append(ivms, &vms[i])
}
return ivms, nil
}
func (self *SDedicatedHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
vms, err := self.zone.region.GetInstances(self.zone.ZoneName, "", []string{id})
if err != nil {
return nil, errors.Wrap(err, "GetInstances")
}
for i := range vms {
if vms[i].InstanceId == id && vms[i].Placement.HostId == self.HostId {
bindInstanceHost(&vms[i], self.zone, self)
return &vms[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", id)
}
func (self *SDedicatedHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
return self.zone.GetIStorages()
}
func (self *SDedicatedHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
return self.zone.GetIStorageById(id)
}
func (self *SDedicatedHost) GetEnabled() bool {
return self.State == DedicatedHostStateAvailable
}
func (self *SDedicatedHost) GetHostStatus() string {
if self.GetEnabled() {
return api.HOST_ONLINE
}
return api.HOST_OFFLINE
}
func (self *SDedicatedHost) GetAccessIp() string {
return ""
}
func (self *SDedicatedHost) GetAccessMac() string {
return ""
}
func (self *SDedicatedHost) GetSysInfo() jsonutils.JSONObject {
info := jsonutils.NewDict()
info.Add(jsonutils.NewString(CLOUD_PROVIDER_AWS), "manufacture")
info.Add(jsonutils.NewString(self.HostProperties.InstanceFamily), "instance_family")
info.Add(jsonutils.NewString(self.HostProperties.InstanceType), "instance_type")
info.Add(jsonutils.NewInt(int64(self.AvailableCapacity.AvailableVCpus)), "available_vcpus")
return info
}
func (self *SDedicatedHost) GetSN() string {
return self.HostId
}
func (self *SDedicatedHost) GetCpuCount() int {
return int(self.HostProperties.TotalVCpus)
}
func (self *SDedicatedHost) GetNodeCount() int8 {
return int8(self.HostProperties.Sockets)
}
func (self *SDedicatedHost) GetCpuDesc() string {
return self.HostProperties.InstanceFamily
}
func (self *SDedicatedHost) GetCpuMhz() int {
return 0
}
func (self *SDedicatedHost) GetCpuCmtbound() float32 {
return 1.0
}
func (self *SDedicatedHost) GetMemCmtbound() float32 {
return 1.0
}
func (self *SDedicatedHost) GetMemSizeMB() int {
itype := self.HostProperties.InstanceType
if len(itype) == 0 {
return 0
}
it, err := self.zone.region.GetInstanceType(itype)
if err != nil || it == nil {
return 0
}
vcpuPerInst := it.VCpuInfo.DefaultVCpus
if vcpuPerInst <= 0 {
vcpuPerInst = 1
}
totalVCpus := int(self.HostProperties.TotalVCpus)
if totalVCpus <= 0 {
return it.MemoryInfo.SizeInMiB
}
instances := totalVCpus / vcpuPerInst
if instances <= 0 {
instances = 1
}
return it.MemoryInfo.SizeInMiB * instances
}
func (self *SDedicatedHost) GetStorageSizeMB() int64 {
return 0
}
func (self *SDedicatedHost) GetStorageType() string {
return api.DISK_TYPE_HYBRID
}
func (self *SDedicatedHost) GetHostType() string {
return api.HOST_TYPE_DEDICATED
}
func (self *SDedicatedHost) GetInstanceById(instanceId string) (*SInstance, error) {
inst, err := self.zone.region.GetInstance(instanceId)
if err != nil {
return nil, errors.Wrap(err, "GetInstance")
}
if inst.Placement.HostId != self.HostId {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", instanceId)
}
bindInstanceHost(inst, self.zone, self)
return inst, nil
}
func (self *SDedicatedHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
vm, err := self._createVM(desc.Name, desc.ExternalImageId, desc.SysDisk, desc.InstanceType,
desc.ExternalNetworkId, desc.IpAddr, desc.Description, desc.Password, desc.DataDisks,
desc.PublicKey, desc.ExternalSecgroupIds, desc.UserData, desc.Tags, desc.EnableMonitorAgent)
if err != nil {
return nil, errors.Wrap(err, "_createVM")
}
bindInstanceHost(vm, self.zone, self)
return vm, err
}
func (self *SDedicatedHost) _createVM(name, imgId string, sysDisk cloudprovider.SDiskInfo, instanceType string,
networkId, ipAddr, desc, passwd string,
dataDisks []cloudprovider.SDiskInfo, publicKey string, secgroupIds []string, userData string,
tags map[string]string, enableMonitorAgent bool,
) (*SInstance, error) {
if len(instanceType) == 0 {
return nil, fmt.Errorf("missing instance type params")
}
var err error
keypair := ""
if len(publicKey) > 0 {
keypair, err = self.zone.region.SyncKeypair(publicKey)
if err != nil {
return nil, err
}
}
img, err := self.zone.region.GetImage(imgId)
if err != nil {
return nil, errors.Wrapf(err, "GetImage(%s)", imgId)
}
if img.Status != ImageStatusAvailable {
return nil, fmt.Errorf("image not ready status: %s", img.Status)
}
if len(dataDisks) == 0 {
dataDisks = []cloudprovider.SDiskInfo{}
}
if sysDisk.SizeGB < img.GetMinOsDiskSizeGb() {
sysDisk.SizeGB = img.GetMinOsDiskSizeGb()
}
disks := append([]cloudprovider.SDiskInfo{sysDisk}, dataDisks...)
instance, err := self.zone.region.CreateInstance(name, img, instanceType, networkId, secgroupIds, self.zone.ZoneName, desc, disks, ipAddr, keypair, userData, tags, enableMonitorAgent, self.HostId)
if err != nil {
return nil, errors.Wrapf(err, "CreateInstance")
}
return instance, nil
}
func (host *SDedicatedHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
wires, err := host.zone.GetIWires()
if err != nil {
return nil, errors.Wrap(err, "GetIWires")
}
return cloudprovider.GetHostNetifs(host, wires), nil
}
func (self *SDedicatedHost) GetIsMaintenance() bool {
return self.State == DedicatedHostStateUnderAssessment || self.State == DedicatedHostStatePermanentFailure
}
func (self *SDedicatedHost) GetVersion() string {
return AWS_API_VERSION
}
func bindInstanceHost(inst *SInstance, zone *SZone, ihost cloudprovider.ICloudHost) {
inst.host = &SHost{zone: zone}
if dh, ok := ihost.(*SDedicatedHost); ok {
inst.dedicatedHost = dh
} else if h, ok := ihost.(*SHost); ok {
inst.host = h
inst.dedicatedHost = nil
}
}
+13 -7
View File
@@ -61,10 +61,13 @@ func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
return nil, errors.Wrap(err, "GetInstances")
}
ivms := make([]cloudprovider.ICloudVM, len(vms))
ivms := make([]cloudprovider.ICloudVM, 0, len(vms))
for i := 0; i < len(vms); i += 1 {
vms[i].host = self
ivms[i] = &vms[i]
if len(vms[i].Placement.HostId) > 0 {
continue
}
bindInstanceHost(&vms[i], self.zone, self)
ivms = append(ivms, &vms[i])
}
return ivms, nil
}
@@ -76,7 +79,10 @@ func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
}
for i := range vms {
if vms[i].InstanceId == id {
vms[i].host = self
if len(vms[i].Placement.HostId) > 0 {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", id)
}
bindInstanceHost(&vms[i], self.zone, self)
return &vms[i], nil
}
}
@@ -154,7 +160,7 @@ func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) {
if err != nil {
return nil, errors.Wrap(err, "GetInstance")
}
inst.host = self
bindInstanceHost(inst, self.zone, self)
return inst, nil
}
@@ -165,7 +171,7 @@ func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudpr
if err != nil {
return nil, errors.Wrap(err, "_createVM")
}
vm.host = self
bindInstanceHost(vm, self.zone, self)
return vm, err
}
@@ -203,7 +209,7 @@ func (self *SHost) _createVM(name, imgId string, sysDisk cloudprovider.SDiskInfo
}
disks := append([]cloudprovider.SDiskInfo{sysDisk}, dataDisks...)
instance, err := self.zone.region.CreateInstance(name, img, instanceType, networkId, secgroupIds, self.zone.ZoneName, desc, disks, ipAddr, keypair, userData, tags, enableMonitorAgent)
instance, err := self.zone.region.CreateInstance(name, img, instanceType, networkId, secgroupIds, self.zone.ZoneName, desc, disks, ipAddr, keypair, userData, tags, enableMonitorAgent, "")
if err != nil {
return nil, errors.Wrapf(err, "CreateInstance")
}
+22 -2
View File
@@ -77,8 +77,9 @@ type SInstance struct {
multicloud.SInstanceBase
AwsTags
host *SHost
img *SImage
host *SHost
dedicatedHost *SDedicatedHost
img *SImage
AmiLaunchIndex int64 `xml:"amiLaunchIndex"`
Architecture string `xml:"architecture"`
@@ -281,9 +282,22 @@ func (self *SInstance) GetExpiredAt() time.Time {
}
func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
if self.dedicatedHost != nil {
return self.dedicatedHost
}
return self.host
}
func (self *SInstance) GetIHostId() string {
if self.dedicatedHost != nil {
return self.dedicatedHost.GetGlobalId()
}
if self.host != nil {
return self.host.GetGlobalId()
}
return ""
}
func (self *SInstance) GetThroughput() int {
return 0
}
@@ -767,6 +781,7 @@ func (self *SRegion) GetInstanceIdByImageId(imageId string) (string, error) {
func (self *SRegion) CreateInstance(name string, image *SImage, instanceType string, subnetId string, secgroupIds []string,
zoneId string, desc string, disks []cloudprovider.SDiskInfo, ipAddr string,
keypair string, userData string, tags map[string]string, enableMonitorAgent bool,
dedicatedHostId string,
) (*SInstance, error) {
params, devNames := map[string]string{}, image.GetBlockDeviceNames()
for i, disk := range disks {
@@ -832,6 +847,10 @@ func (self *SRegion) CreateInstance(name string, image *SImage, instanceType str
params["MaxCount"] = "1"
params["MinCount"] = "1"
params["Placement.AvailabilityZone"] = zoneId
if len(dedicatedHostId) > 0 {
params["Placement.HostId"] = dedicatedHostId
params["Placement.Tenancy"] = "host"
}
params["Monitoring.Enabled"] = fmt.Sprintf("%v", enableMonitorAgent)
// keypair
if len(keypair) > 0 {
@@ -946,6 +965,7 @@ func (self *SRegion) ReplaceSystemDisk(ctx context.Context, instanceId string, i
userdata,
nil,
false,
"",
)
if err == nil {
defer self.DeleteVM(vm.InstanceId)
+3
View File
@@ -115,6 +115,9 @@ type InstanceType struct {
MemoryInfo struct {
SizeInMiB int `xml:"sizeInMiB"`
} `xml:"memoryInfo"`
VCpuInfo struct {
DefaultVCpus int `xml:"defaultVCpus"`
} `xml:"vCpuInfo"`
}
func (self *SRegion) GetInstanceType(name string) (*InstanceType, error) {
+10 -2
View File
@@ -93,8 +93,16 @@ func (self *SZone) GetIRegion() cloudprovider.ICloudRegion {
}
func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
host := &SHost{zone: self}
return []cloudprovider.ICloudHost{host}, nil
hosts := []cloudprovider.ICloudHost{&SHost{zone: self}}
dedicatedHosts, err := self.region.GetDedicatedHosts(self.ZoneName)
if err != nil {
return nil, errors.Wrap(err, "GetDedicatedHosts")
}
for i := range dedicatedHosts {
dedicatedHosts[i].zone = self
hosts = append(hosts, &dedicatedHosts[i])
}
return hosts, nil
}
func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
-1
View File
@@ -44,7 +44,6 @@ import (
_ "yunion.io/x/cloudmux/pkg/multicloud/qcloud/provider"
_ "yunion.io/x/cloudmux/pkg/multicloud/qingcloud/provider"
_ "yunion.io/x/cloudmux/pkg/multicloud/remotefile/provider" // private clouds
_ "yunion.io/x/cloudmux/pkg/multicloud/rockbase/provider"
_ "yunion.io/x/cloudmux/pkg/multicloud/ucloud/provider" // object storages
_ "yunion.io/x/cloudmux/pkg/multicloud/volcengine/provider"
_ "yunion.io/x/cloudmux/pkg/multicloud/zstack/provider" // private clouds
-117
View File
@@ -1,117 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
// SBalance 账户余额信息,对应 UBill GetBalance 接口。
type SBalance struct {
Amount float64
AmountAvailable float64
AmountFreeze float64
AmountFree float64
AmountCredit string
}
func parseBalanceAmount(obj jsonutils.JSONObject, key string) float64 {
if obj == nil {
return 0
}
if val, err := obj.Float(key); err == nil {
return val
}
if str, err := obj.GetString(key); err == nil && len(str) > 0 {
if f, err := strconv.ParseFloat(str, 64); err == nil {
return f
}
}
return 0
}
func parseBalanceFromResp(resp jsonutils.JSONObject) *SBalance {
ret := &SBalance{}
if resp == nil {
return ret
}
info, _ := resp.Get("AccountInfo")
for _, obj := range []jsonutils.JSONObject{info, resp} {
if obj == nil {
continue
}
if ret.AmountAvailable == 0 {
ret.AmountAvailable = parseBalanceAmount(obj, "AmountAvailable")
}
if ret.Amount == 0 {
ret.Amount = parseBalanceAmount(obj, "Amount")
}
if ret.AmountFreeze == 0 {
ret.AmountFreeze = parseBalanceAmount(obj, "AmountFreeze")
}
if ret.AmountFree == 0 {
ret.AmountFree = parseBalanceAmount(obj, "AmountFree")
}
if len(ret.AmountCredit) == 0 {
ret.AmountCredit, _ = obj.GetString("AmountCredit")
}
}
return ret
}
func (balance *SBalance) GetAvailableAmount() float64 {
if balance.AmountAvailable > 0 {
return balance.AmountAvailable
}
return balance.Amount
}
// QueryBalance 查询账户余额。
func (self *SRockbaseClient) QueryBalance() (*SBalance, error) {
params := NewRockbaseParams()
params = self.commonParams(params)
params.SetAction("GetBalance")
resp, err := jsonRequest(self, params)
if err != nil {
return nil, err
}
return parseBalanceFromResp(resp), nil
}
// GetBalance 查询账户余额并转换为 cloudprovider 通用结构。
func (self *SRockbaseClient) GetBalance() (*cloudprovider.SBalanceInfo, error) {
balance, err := self.QueryBalance()
if err != nil {
return nil, errors.Wrap(err, "QueryBalance")
}
ret := &cloudprovider.SBalanceInfo{
Currency: "CNY",
Amount: balance.GetAvailableAmount(),
Status: api.CLOUD_PROVIDER_HEALTH_NORMAL,
}
if ret.Amount < 0 {
ret.Status = api.CLOUD_PROVIDER_HEALTH_ARREARS
}
if ret.Amount == 0 {
ret.Status = api.CLOUD_PROVIDER_HEALTH_INSUFFICIENT
}
return ret, nil
}
-180
View File
@@ -1,180 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"context"
"crypto/sha1"
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/httputils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
const ROCKBASE_API_HOST = "https://api.rockbasecloud.com"
type SParams struct {
data jsonutils.JSONDict
}
type SRockbaseError struct {
Action string `json:"Action"`
Message string `json:"Message"`
RetCode int64 `json:"RetCode"`
}
func (self *SRockbaseError) Error() string {
return fmt.Sprintf("Do %s failed, code: %d, %s", self.Action, self.RetCode, self.Message)
}
func NewRockbaseParams() 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.NewFloat64(v))
case float32:
self.data.Set(key, jsonutils.NewFloat32(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(params SParams, resp jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := &SRockbaseError{}
e := resp.Unmarshal(err)
if e != nil {
return nil, e
}
err.Action, _ = params.data.GetString("Action")
if err.RetCode > 0 {
if err.RetCode == 171 {
return nil, errors.Wrapf(cloudprovider.ErrInvalidAccessKey, "%s", err.Error())
}
return nil, err
}
return resp, nil
}
func jsonRequest(client *SRockbaseClient, params SParams) (jsonutils.JSONObject, error) {
ctx := context.Background()
MAX_RETRY := 3
retry := 0
var err error
var resp jsonutils.JSONObject
for retry < MAX_RETRY {
_, resp, err = httputils.JSONRequest(
client.httpClient,
ctx,
httputils.POST,
ROCKBASE_API_HOST,
nil,
BuildParams(params, client.accessKeySecret),
client.debug)
if err == nil {
return parseUcloudResponse(params, resp)
}
switch e := err.(type) {
case *httputils.JSONClientError:
if e.Code >= 499 && !strings.Contains(err.Error(), cloudprovider.ErrAccountReadOnly.Error()) {
time.Sleep(3 * time.Second)
retry += 1
continue
} else {
return nil, err
}
default:
return nil, err
}
}
return resp, err
}
-458
View File
@@ -1,458 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"context"
"fmt"
"strings"
"time"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://docs.ucloud.cn/api/udisk-api/describe_udisk
type SDisk struct {
storage *SStorage
multicloud.SDisk
RockbaseTags
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 api.DISK_READY
case "Attaching":
return api.DISK_ATTACHING
case "InUse":
return api.DISK_READY
case "Detaching":
return api.DISK_DETACHING
case "Initializating":
return api.DISK_ALLOCATING
case "Failed":
return api.DISK_ALLOC_FAILED
case "Cloning":
return api.DISK_CLONING
case "Restoring":
return api.DISK_RESET
case "RestoreFailed":
return api.DISK_RESET_FAILED
default:
return api.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) GetSysTags() map[string]string {
data := map[string]string{}
data["hypervisor"] = api.HYPERVISOR_ROCKBASE
return data
}
func (self *SDisk) GetTags() (map[string]string, error) {
return self.storage.zone.region.GetResourceTags(self.GetId())
}
func (self *SDisk) SetTags(tags map[string]string, replace bool) error {
return self.storage.zone.region.SetResourceTags(self.GetId(), tags, replace)
}
// Year,Month,Dynamic,Trial
func (self *SDisk) GetBillingType() string {
switch self.ChargeType {
case "Year", "Month":
return billing_api.BILLING_TYPE_PREPAID
default:
return billing_api.BILLING_TYPE_POSTPAID
}
}
func (self *SDisk) GetCreatedAt() time.Time {
return time.Unix(self.CreateTime, 0)
}
func (self *SDisk) GetExpiredAt() time.Time {
if strings.EqualFold(self.ChargeType, "Year") || strings.EqualFold(self.ChargeType, "Month") {
return time.Unix(self.ExpiredTime, 0)
}
return time.Time{}
}
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.GetInstance(self.UHostId)
if err != nil {
return ""
}
return ins.ImageId
}
return ""
}
func (self *SDisk) GetDiskType() string {
if strings.Contains(self.DiskType, "SystemDisk") {
return api.DISK_TYPE_SYS
}
return api.DISK_TYPE_DATA
}
func (self *SDisk) GetStorageType() string {
if self.storage == nil {
if strings.Contains(self.DiskType, "SSD") {
return api.STORAGE_ROCKBASE_CLOUD_SSD
} else {
return api.STORAGE_ROCKBASE_CLOUD_NORMAL
}
}
return self.storage.storageType
}
func (self *SDisk) GetFsFormat() string {
return ""
}
func (self *SDisk) GetIsNonPersistent() bool {
return false
}
func (self *SDisk) GetDriver() string {
return "scsi"
}
func (self *SDisk) GetCacheMode() string {
return "none"
}
func (self *SDisk) GetMountpoint() string {
return ""
}
func (self *SDisk) GetAccessPath() string {
return ""
}
func (self *SDisk) Delete(ctx context.Context) error {
return self.storage.zone.region.DeleteDisk(self.Zone, self.GetId())
}
func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) {
snapshot, err := self.storage.zone.region.CreateSnapshot(self.Zone, self.GetId(), name, desc)
if err != nil {
return nil, err
}
isnapshot, err := self.GetISnapshot(snapshot)
if err != nil {
return nil, err
}
err = cloudprovider.WaitStatus(isnapshot, api.SNAPSHOT_READY, time.Second*10, time.Second*300)
if err != nil {
return nil, errors.Wrap(err, "CreateISnapshot")
}
return isnapshot, nil
}
func (self *SDisk) getSnapshot(snapshotId string) (*SSnapshot, error) {
snapshot, err := self.storage.zone.region.GetSnapshotById(self.Zone, 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 {
var sizeGB int64
// 向上取整
if (newSizeMB % 1024) > 0 {
sizeGB = newSizeMB/1024 + 1
} else {
sizeGB = newSizeMB / 1024
}
if self.Status == "InUse" {
err := self.storage.zone.region.DetachDisk(self.Zone, self.UHostId, self.UDiskId)
if err != nil {
return err
}
defer self.storage.zone.region.AttachDisk(self.Zone, self.UHostId, self.UDiskId)
err = cloudprovider.WaitStatusWithDelay(self, api.DISK_READY, 10*time.Second, 5*time.Second, 60*time.Second)
if err != nil {
return errors.Wrap(err, "DiskResize")
}
}
return self.storage.zone.region.resizeDisk(self.Zone, self.GetId(), sizeGB)
}
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
err := self.storage.zone.region.resetDisk(self.Zone, self.GetId(), snapshotId)
if err != nil {
return "", err
}
return self.GetId(), nil
}
func (self *SDisk) Rebuild(ctx context.Context) error {
return self.storage.zone.region.resetDisk(self.Zone, self.GetId(), "")
}
func (self *SRegion) GetDisk(diskId string) (*SDisk, error) {
disks, err := self.GetDisks("", "", "", diskId)
if err != nil {
return nil, err
}
for i := range disks {
if disks[i].UDiskId == diskId {
return &disks[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetDisk %s", diskId)
}
// https://docs.ucloud.cn/api/udisk-api/describe_udisk
// diskType DataDisk|SystemDisk (DataDisk表示数据盘,SystemDisk表示系统盘)
func (self *SRegion) GetDisks(zoneId string, diskType string, isBoot string, diskId string) ([]SDisk, error) {
disks := make([]SDisk, 0)
params := NewRockbaseParams()
if len(zoneId) > 0 {
params.Set("Zone", zoneId)
}
if len(diskType) > 0 {
params.Set("ProtocolVersion", "1")
params.Set("DiskType", diskType)
}
if len(diskId) > 0 {
params.Set("UDiskId", diskId)
}
if len(isBoot) > 0 {
params.Set("IsBoot", isBoot)
}
err := self.DoListAll("DescribeUDisk", params, &disks)
if err != nil {
return nil, err
}
return disks, nil
}
// https://docs.ucloud.cn/api/udisk-api/delete_udisk
func (self *SRegion) DeleteDisk(zoneId string, diskId string) error {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("UDiskId", diskId)
return self.DoAction("DeleteUDisk", params, nil)
}
// https://docs.ucloud.cn/api/udisk-api/create_udisk
func (self *SRegion) CreateDisk(zoneId string, category string, opts *cloudprovider.DiskCreateConfig) (string, error) {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("Size", opts.SizeGb)
params.Set("Name", opts.Name)
params.Set("DiskType", category)
diskIds := make([]string, 0)
err := self.DoAction("CreateUDisk", params, &diskIds)
if err != nil {
return "", err
}
if len(diskIds) == 0 {
return "", fmt.Errorf("CreateDisk with empty response")
}
return diskIds[0], nil
}
// https://docs.ucloud.cn/api/udisk-api/create_udisk_snapshot
func (self *SRegion) CreateSnapshot(zoneId, diskId, name, desc string) (string, error) {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("UDiskId", diskId)
params.Set("Name", name)
params.Set("Comment", desc)
snapshotIds := make([]string, 0)
err := self.DoAction("CreateUDiskSnapshot", params, &snapshotIds)
if err != nil {
return "", err
}
if len(snapshotIds) == 0 {
return "", fmt.Errorf("CreateSnapshot with empty response")
}
return snapshotIds[0], nil
}
// https://docs.ucloud.cn/api/udisk-api/resize_udisk
func (self *SRegion) resizeDisk(zoneId string, diskId string, sizeGB int64) error {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("Size", sizeGB)
params.Set("UDiskId", diskId)
return self.DoAction("ResizeUDisk", params, nil)
}
// https://docs.ucloud.cn/api/udisk-api/restore_u_disk
func (self *SRegion) resetDisk(zoneId, diskId, snapshotId string) error {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("UDiskId", diskId)
if len(snapshotId) > 0 {
params.Set("SnapshotId", snapshotId)
}
return self.DoAction("RestoreUDisk", params, nil)
}
// https://docs.ucloud.cn/api/udisk-api/attach_udisk
func (self *SRegion) AttachDisk(zoneId string, instanceId string, diskId string) error {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("UHostId", instanceId)
params.Set("UDiskId", diskId)
return self.DoAction("AttachUDisk", params, nil)
}
// https://docs.ucloud.cn/api/udisk-api/detach_udisk
func (self *SRegion) DetachDisk(zoneId string, instanceId string, diskId string) error {
idisks, err := self.GetDisk(diskId)
if err != nil {
return err
}
if idisks.Status == "Available" {
return nil
}
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("UHostId", instanceId)
params.Set("UDiskId", diskId)
return self.DoAction("DetachUDisk", params, nil)
}
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase // import "yunion.io/x/cloudmux/pkg/multicloud/rockbase"
-292
View File
@@ -1,292 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
const (
EIP_CHARGE_TYPE_BY_TRAFFIC = "traffic"
EIP_CHARGE_TYPE_BY_BANDWIDTH = "bandwidth"
)
// https://docs.ucloud.cn/api/unet-api/describe_eip
type SEip struct {
region *SRegion
multicloud.SEipBase
RockbaseTags
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 api.EIP_STATUS_READY
case "free":
return api.EIP_STATUS_READY
case "freeze":
return api.EIP_STATUS_UNKNOWN
default:
return api.EIP_STATUS_UNKNOWN
}
}
func (self *SEip) Refresh() error {
if self.IsEmulated() {
return nil
}
new, err := self.region.GetEip(self.GetId())
if err != nil {
return errors.Wrapf(err, "Refresh")
}
return jsonutils.Update(self, new)
}
func (self *SEip) IsEmulated() bool {
return false
}
// 付费方式, 枚举值为: Year, 按年付费; Month, 按月付费; Dynamic, 按小时付费; Trial, 试用. 按小时付费和试用这两种付费模式需要开通权限.
func (self *SEip) GetBillingType() string {
switch self.ChargeType {
case "Year", "Month":
return billing_api.BILLING_TYPE_PREPAID
default:
return billing_api.BILLING_TYPE_POSTPAID
}
}
func (self *SEip) GetCreatedAt() time.Time {
return time.Unix(self.CreateTime, 0)
}
func (self *SEip) GetExpiredAt() time.Time {
if strings.EqualFold(self.ChargeType, "Year") || strings.EqualFold(self.ChargeType, "Month") {
return time.Unix(self.ExpireTime, 0)
}
return time.Time{}
}
func (self *SEip) GetIpAddr() string {
if len(self.EIPAddr) > 1 {
log.Warningf("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 api.EIP_MODE_STANDALONE_EIP
}
func (self *SEip) GetAssociationType() string {
switch self.Resource.ResourceType {
case "uhost":
return api.EIP_ASSOCIATE_TYPE_SERVER
case "natgw":
return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
case "ulb":
return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
default:
return self.Resource.ResourceType
}
}
// 已绑定的资源类型, 枚举值为: uhost, 云主机;natgwNAT网关;ulb:负载均衡器;upm: 物理机; hadoophost: 大数据集群;fortresshost:堡垒机;udockhost:容器;udhost:私有专区主机;vpngwIPSec VPNucdr:云灾备;dbaudit:数据库审计。
func (self *SEip) GetAssociationExternalId() string {
return self.Resource.ResourceId
}
func (self *SEip) GetBandwidth() int {
return self.BandwidthMb
}
func (self *SEip) GetINetworkId() string {
return ""
}
// 弹性IP的计费模式, 枚举值为: "Bandwidth", 带宽计费; "Traffic", 流量计费; "ShareBandwidth",共享带宽模式. 默认为 "Bandwidth".
func (self *SEip) GetInternetChargeType() string {
switch self.PayMode {
case "Bandwidth":
return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
case "Traffic":
return api.EIP_CHARGE_TYPE_BY_TRAFFIC
default:
return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
}
}
func (self *SEip) Delete() error {
return self.region.DeallocateEIP(self.GetId())
}
func (self *SEip) Associate(conf *cloudprovider.AssociateConfig) error {
return self.region.AssociateEip(self.GetId(), conf.InstanceId)
}
func (self *SEip) Dissociate() error {
return self.region.DissociateEip(self.GetId(), self.Resource.ResourceId)
}
func (self *SEip) ChangeBandwidth(bw int) error {
return self.region.UpdateEipBandwidth(self.GetId(), bw)
}
// https://docs.ucloud.cn/api/unet-api/allocate_eip
// 增加共享带宽模式ShareBandwidth
func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
if len(eip.BGPType) == 0 {
if strings.HasPrefix(self.GetId(), "cn-") {
eip.BGPType = "Bgp"
} else {
eip.BGPType = "International"
}
}
params := NewRockbaseParams()
params.Set("OperatorName", eip.BGPType)
params.Set("Bandwidth", eip.BandwidthMbps)
params.Set("Name", eip.Name)
params.Set("Region", self.GetId())
var payMode string
switch eip.ChargeType {
case api.EIP_CHARGE_TYPE_BY_TRAFFIC:
payMode = "Traffic"
case api.EIP_CHARGE_TYPE_BY_BANDWIDTH:
payMode = "Bandwidth"
default:
payMode = "Bandwidth"
}
params.Set("PayMode", payMode)
params.Set("ChargeType", "Dynamic") // 按需付费
eips := make([]SEip, 0)
err := self.DoAction("AllocateEIP", params, &eips)
if err != nil {
return nil, err
}
for i := range eips {
eips[i].region = self
return &eips[i], nil
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "CreateEIP %d eip created", len(eips))
}
// https://docs.ucloud.cn/api/unet-api/release_eip
func (self *SRegion) DeallocateEIP(eipId string) error {
params := NewRockbaseParams()
params.Set("EIPId", eipId)
return self.DoAction("ReleaseEIP", params, nil)
}
// https://docs.ucloud.cn/api/unet-api/bind_eip
func (self *SRegion) AssociateEip(eipId string, instanceId string) error {
params := NewRockbaseParams()
params.Set("EIPId", eipId)
params.Set("ResourceType", "uhost")
params.Set("ResourceId", instanceId)
return self.DoAction("BindEIP", params, nil)
}
// https://docs.ucloud.cn/api/unet-api/unbind_eip
func (self *SRegion) DissociateEip(eipId string, instanceId string) error {
params := NewRockbaseParams()
params.Set("EIPId", eipId)
params.Set("ResourceType", "uhost")
params.Set("ResourceId", instanceId)
return self.DoAction("UnBindEIP", params, nil)
}
// https://docs.ucloud.cn/api/unet-api/modify_eip_bandwidth
func (self *SRegion) UpdateEipBandwidth(eipId string, bw int) error {
params := NewRockbaseParams()
params.Set("EIPId", eipId)
params.Set("Bandwidth", bw)
return self.DoAction("ModifyEIPBandwidth", params, nil)
}
-362
View File
@@ -1,362 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"encoding/base64"
"fmt"
"regexp"
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SHost struct {
multicloud.SHostBase
zone *SZone
projectId string
}
func (self *SHost) GetId() string {
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId())
}
func (self *SHost) GetName() string {
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Name, self.zone.GetId())
}
func (self *SHost) GetGlobalId() string {
return self.GetId()
}
func (self *SHost) GetStatus() string {
return api.HOST_STATUS_RUNNING
}
func (self *SHost) Refresh() error {
return nil
}
func (self *SHost) IsEmulated() bool {
return true
}
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.GetInstance(id)
if err != nil {
return nil, err
}
vm.host = self
return vm, nil
}
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 api.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_ROCKBASE), "manufacture")
return info
}
func (self *SHost) GetSN() string {
return ""
}
func (self *SHost) GetCpuCount() int {
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() int64 {
return 0
}
func (self *SHost) GetStorageType() string {
return api.DISK_TYPE_HYBRID
}
func (self *SHost) GetHostType() string {
return api.HOST_TYPE_ROCKBASE
}
func (self *SHost) GetIsMaintenance() bool {
return false
}
func (self *SHost) GetVersion() string {
return ROCKBASE_API_VERSION
}
// 不支持user data
// 不支持指定keypair
func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
vmId, err := self._createVM(opts)
if err != nil {
return nil, err
}
vm, err := self.zone.region.GetInstance(vmId)
if err != nil {
return nil, err
}
vm.host = self
return vm, err
}
func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
wires, err := host.zone.GetIWires()
if err != nil {
return nil, errors.Wrap(err, "getIWires")
}
return cloudprovider.GetHostNetifs(host, wires), nil
}
type SInstanceType struct {
UHostType string
GpuType string
CPU int
MemoryMB int
GPU int
}
// 格式: {机型}.c{cpu}.m{memGB}[.g{gpu}],机型支持 UCloud GpuType 如 T4S、2080Ti-4C、T4/4
var instanceTypeRe = regexp.MustCompile(`^([A-Za-z0-9][A-Za-z0-9/_-]*)\.c(\d+)\.m(\d+)(?:\.g(\d+))?$`)
func ParseInstanceType(instanceType string) (SInstanceType, error) {
i := SInstanceType{}
matches := instanceTypeRe.FindStringSubmatch(instanceType)
if matches == nil {
return i, fmt.Errorf("invalid instance type %s", instanceType)
}
hostType := matches[1]
cpu, err := strconv.Atoi(matches[2])
if err != nil {
return i, err
}
memGB, err := strconv.Atoi(matches[3])
if err != nil {
return i, err
}
i.CPU = cpu
i.MemoryMB = memGB * 1024
if len(matches[4]) > 0 {
gpu, err := strconv.Atoi(matches[4])
if err != nil {
return i, err
}
i.GPU = gpu
i.GpuType = hostType
i.UHostType = "G"
} else {
i.UHostType = hostType
}
return i, nil
}
func (self *SHost) _createVM(opts *cloudprovider.SManagedVMCreateConfig) (string, error) {
net, err := self.zone.region.getNetwork(opts.ExternalNetworkId)
if err != nil {
return "", errors.Wrapf(err, "getNetwork %s", opts.ExternalNetworkId)
}
if len(opts.Password) == 0 {
return "", fmt.Errorf("CreateVM password should not be emtpty")
}
img, err := self.zone.region.GetImage(opts.ExternalImageId)
if err != nil {
return "", errors.Wrapf(err, "GetImage %s", opts.ExternalImageId)
}
if img.GetStatus() != api.CACHED_IMAGE_STATUS_ACTIVE {
return "", errors.Wrapf(cloudprovider.ErrInvalidStatus, "image %s status %s, expect %s", opts.ExternalImageId, img.GetStatus(), api.CACHED_IMAGE_STATUS_ACTIVE)
}
if int(img.ImageSizeGB) > opts.SysDisk.SizeGB {
opts.SysDisk.SizeGB = int(img.ImageSizeGB)
}
if len(opts.ExternalVpcId) == 0 {
opts.ExternalVpcId = net.wire.vpc.GetId()
}
vmId, err := self.zone.region.CreateInstance(self.zone.GetId(), opts)
if err != nil {
return "", fmt.Errorf("Failed to create: %v", err)
}
return vmId, nil
}
// https://docs.ucloud.cn/api/uhost-api/create_uhost_instance
// https://docs.ucloud.cn/api/uhost-api/specification
// 支持8-30位字符, 不能包含[A-Z],[a-z],[0-9]和[()`~!@#$%^&*-+=_|{}[]:;'<>,.?/]之外的非法字符
func (self *SRegion) CreateInstance(zoneId string, opts *cloudprovider.SManagedVMCreateConfig) (string, error) {
if opts == nil {
return "", errors.Wrap(cloudprovider.ErrMissingParameter, "opts")
}
if len(opts.SysDisk.StorageType) == 0 {
return "", errors.Wrap(cloudprovider.ErrMissingParameter, "SysDisk.StorageType")
}
i, err := ParseInstanceType(opts.InstanceType)
if err != nil {
if opts.Cpu <= 0 || opts.MemoryMB <= 0 {
return "", err
}
i.UHostType = "O"
i.CPU = opts.Cpu
i.MemoryMB = opts.MemoryMB
}
if len(zoneId) == 0 && len(opts.ExternalNetworkId) > 0 {
net, err := self.getNetwork(opts.ExternalNetworkId)
if err == nil && len(net.Zone) > 0 {
zoneId = net.Zone
}
}
if len(zoneId) == 0 {
return "", errors.Wrap(cloudprovider.ErrMissingParameter, "zoneId")
}
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("ImageId", opts.ExternalImageId)
params.Set("Password", base64.StdEncoding.EncodeToString([]byte(opts.Password)))
params.Set("LoginMode", "Password")
params.Set("Name", opts.Name)
params.Set("MachineType", i.UHostType)
params.Set("CPU", i.CPU)
params.Set("Memory", i.MemoryMB)
params.Set("VPCId", opts.ExternalVpcId)
params.Set("SubnetId", opts.ExternalNetworkId)
for _, id := range opts.ExternalSecgroupIds {
params.Set("SecurityGroupId", id)
}
if i.GPU > 0 {
params.Set("GPU", i.GPU)
if len(i.GpuType) > 0 {
params.Set("GpuType", i.GpuType)
}
}
bc := opts.BillingCycle
if bc != nil && bc.GetMonths() >= 1 && bc.GetMonths() < 10 {
params.Set("ChargeType", "Month")
params.Set("Quantity", bc.GetMonths())
} else if bc != nil && bc.GetMonths() >= 10 && bc.GetMonths() < 12 {
params.Set("ChargeType", "Year")
params.Set("Quantity", 1)
} else if bc != nil && bc.GetYears() >= 1 {
params.Set("ChargeType", "Year")
params.Set("Quantity", bc.GetYears())
} else {
params.Set("ChargeType", "Dynamic")
}
// boot disk
params.Set("Disks.0.IsBoot", "True")
params.Set("Disks.0.Type", opts.SysDisk.StorageType)
params.Set("Disks.0.Size", opts.SysDisk.SizeGB)
// data disk
for idx, disk := range opts.DataDisks {
N := idx + 1
params.Set(fmt.Sprintf("Disks.%d.IsBoot", N), "False")
params.Set(fmt.Sprintf("Disks.%d.Type", N), disk.StorageType)
params.Set(fmt.Sprintf("Disks.%d.Size", N), disk.SizeGB)
}
if len(opts.Tags) > 0 {
setLabelsParams(&params, "Labels", opts.Tags)
}
type Ret struct {
UHostIds []string
}
ret := Ret{}
err = self.DoAction("CreateUHostInstance", params, &ret)
if err != nil {
return "", errors.Wrapf(err, "CreateUHostInstance")
}
if len(ret.UHostIds) == 1 {
return ret.UHostIds[0], nil
}
return "", fmt.Errorf("CreateInstance %d instance created. %s", len(ret.UHostIds), ret.UHostIds)
}
-262
View File
@@ -1,262 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/imagetools"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SImage struct {
multicloud.SImageBase
RockbaseTags
storageCache *SStoragecache
// normalized image info
imgInfo *imagetools.ImageInfo
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 api.CACHED_IMAGE_STATUS_ACTIVE
case "Making":
return api.CACHED_IMAGE_STATUS_CACHING
case "Unavailable":
return api.CACHED_IMAGE_STATUS_CACHE_FAILED
default:
return api.CACHED_IMAGE_STATUS_CACHE_FAILED
}
}
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) Delete(ctx context.Context) error {
return self.storageCache.region.DeleteImage(self.GetId())
}
func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
return self.storageCache
}
func (self *SImage) GetSizeByte() int64 {
return self.ImageSizeGB * 1024 * 1024 * 1024
}
// 镜像类型。标准镜像:Base,镜像市场:Business, 自定义镜像:Custom,默认返回所有类型
func (self *SImage) GetImageType() cloudprovider.TImageType {
switch self.ImageType {
case "Base":
return cloudprovider.ImageTypeSystem
case "Custom":
return cloudprovider.ImageTypeCustomized
case "Business":
return cloudprovider.ImageTypeShared
default:
return cloudprovider.ImageTypeCustomized
}
}
func (self *SImage) GetImageStatus() 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) getNormalizedImageInfo() *imagetools.ImageInfo {
if self.imgInfo == nil {
imgInfo := imagetools.NormalizeImageInfo(self.ImageName, "", "", "", "")
self.imgInfo = &imgInfo
}
return self.imgInfo
}
func (self *SImage) GetOsType() cloudprovider.TOsType {
return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
}
func (self *SImage) GetOsDist() string {
return self.getNormalizedImageInfo().OsDistro
}
func (self *SImage) GetOsVersion() string {
return self.getNormalizedImageInfo().OsVersion
}
func (self *SImage) GetOsArch() string {
return self.getNormalizedImageInfo().OsArch
}
func (img *SImage) GetOsLang() string {
return img.getNormalizedImageInfo().OsLang
}
func (img *SImage) GetBios() cloudprovider.TBiosType {
return cloudprovider.ToBiosType(img.getNormalizedImageInfo().OsBios)
}
func (img *SImage) GetFullOsName() string {
return img.ImageName
}
func (self *SImage) GetMinOsDiskSizeGb() int {
return int(self.ImageSizeGB)
}
func (self *SImage) GetImageFormat() string {
return ""
}
func (self *SImage) GetCreatedAt() 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 := NewRockbaseParams()
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 := NewRockbaseParams()
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
}
// https://docs.ucloud.cn/api/uhost-api/terminate_custom_image
func (self *SRegion) DeleteImage(imageId string) error {
params := NewRockbaseParams()
params.Set("ImageId", imageId)
return self.DoAction("TerminateCustomImage", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/create_custom_image
func (self *SRegion) SaveImage(instanceId string, opts *cloudprovider.SaveImageOptions) (*SImage, error) {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
params.Set("ImageName", opts.Name)
if len(opts.Notes) > 0 {
params.Set("ImageDescription", opts.Notes)
}
ret := struct {
ImageId string
}{}
err := self.DoAction("CreateCustomImage", params, &ret)
if err != nil {
return nil, errors.Wrapf(err, "CreateCustomImage")
}
if len(ret.ImageId) == 0 {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "CreateCustomImage empty ImageId")
}
image, err := self.GetImage(ret.ImageId)
if err != nil {
return nil, errors.Wrapf(err, "GetImage(%s)", ret.ImageId)
}
image.storageCache = self.getStoragecache()
img := &image
err = cloudprovider.WaitStatus(img, api.CACHED_IMAGE_STATUS_ACTIVE, 10*time.Second, 30*time.Minute)
if err != nil {
return nil, errors.Wrapf(err, "wait image ready")
}
return img, nil
}
-677
View File
@@ -1,677 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"context"
"encoding/base64"
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/billing"
"yunion.io/x/pkg/util/imagetools"
"yunion.io/x/pkg/utils"
billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SInstance struct {
multicloud.SInstanceBase
RockbaseTags
host *SHost
osInfo *imagetools.ImageInfo
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"`
MachineType string `json:"MachineType"`
GpuType string `json:"GpuType"`
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) GetSecurityGroupIds() ([]string, error) {
secgroups, err := self.GetSecurityGroups()
if err != nil {
log.Errorln(err)
}
secgroupIds := make([]string, 0)
for _, secgroup := range secgroups {
secgroupIds = append(secgroupIds, secgroup.GetId())
}
return secgroupIds, nil
}
func (self *SInstance) GetProjectId() string {
return self.host.zone.region.client.projectId
}
func (self *SInstance) GetError() error {
return nil
}
type DiskSet struct {
DiskId string `json:"DiskId"`
DiskType string `json:"DiskType"`
Drive string `json:"Drive"`
IsBoot bool `json:"IsBoot"`
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 int64 `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) GetHostname() string {
return ""
}
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 api.VM_RUNNING
case "Stopped":
return api.VM_READY
case "Rebooting":
return api.VM_STOPPING
case "Initializing":
return api.VM_INIT
case "Starting":
return api.VM_STARTING
case "Stopping":
return api.VM_STOPPING
case "Install Fail":
return api.VM_CREATE_FAILED
default:
return api.VM_UNKNOWN
}
}
func (self *SInstance) Refresh() error {
vm, err := self.host.zone.region.GetInstance(self.GetId())
if err != nil {
return err
}
self.DiskSet = nil
self.IPSet = nil
self.osInfo = nil
return jsonutils.Update(self, vm)
}
// 计费模式,枚举值为: Year,按年付费; Month,按月付费; Dynamic,按需付费(需开启权限);
func (self *SInstance) GetBillingType() string {
switch self.ChargeType {
case "Year", "Month":
return billing_api.BILLING_TYPE_PREPAID
default:
return billing_api.BILLING_TYPE_POSTPAID
}
}
func (self *SInstance) GetCreatedAt() time.Time {
return time.Unix(self.CreateTime, 0)
}
func (self *SInstance) GetExpiredAt() time.Time {
if strings.EqualFold(self.ChargeType, "Year") || strings.EqualFold(self.ChargeType, "Month") {
return time.Unix(self.ExpireTime, 0)
}
return time.Time{}
}
func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
return self.host
}
func (self *SInstance) GetLocalDisk(diskId, storageType string, sizeGB int, isBoot bool) SDisk {
diskType := ""
if isBoot {
diskType = "SystemDisk"
}
disk := SDisk{
SDisk: multicloud.SDisk{},
Status: "Available",
UHostId: self.GetId(),
Name: diskId,
Zone: self.host.zone.GetId(),
DiskType: diskType,
UDiskId: diskId,
UHostName: self.GetName(),
CreateTime: self.CreateTime,
SizeGB: sizeGB,
}
disk.storage = &SStorage{zone: self.host.zone, storageType: storageType}
return disk
}
func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
localDisks := make([]SDisk, 0)
disks := []SDisk{}
for _, disk := range self.DiskSet {
if utils.IsInStringArray(disk.DiskType, []string{api.STORAGE_ROCKBASE_LOCAL_NORMAL, api.STORAGE_ROCKBASE_LOCAL_SSD}) {
localDisks = append(localDisks, self.GetLocalDisk(disk.DiskId, disk.DiskType, disk.Size, disk.IsBoot))
continue
}
disk, err := self.host.zone.region.GetDisk(disk.DiskId)
if err != nil {
return nil, err
}
disks = append(disks, *disk)
}
disks = append(disks, localDisks...)
idisks := make([]cloudprovider.ICloudDisk, len(disks))
for i := 0; i < len(disks); i += 1 {
if disks[i].storage == nil {
var category string
if strings.Contains(disks[i].DiskType, "SSD") {
category = api.STORAGE_ROCKBASE_CLOUD_SSD
} else {
category = api.STORAGE_ROCKBASE_CLOUD_NORMAL
}
storage, err := self.host.zone.getStorageByCategory(category)
if err != nil {
return nil, err
}
disks[i].storage = storage
}
idisks[i] = &disks[i]
// 将系统盘放到第0个位置
if disks[i].GetDiskType() == api.DISK_TYPE_SYS {
idisks[0], idisks[i] = idisks[i], idisks[0]
}
}
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,
macAddr: ip.MAC,
}
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.GetEip(ip.IPId)
if err != nil {
return nil, errors.Wrapf(err, "GetEip %s", ip.IPId)
}
return eip, nil
}
}
return nil, nil
}
func (self *SInstance) GetVcpuCount() int {
return 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 (ins *SInstance) getNormalizedOsInfo() *imagetools.ImageInfo {
if ins.osInfo == nil {
osInfo := imagetools.NormalizeImageInfo(ins.OSName, "", ins.OSType, "", "")
ins.osInfo = &osInfo
}
return ins.osInfo
}
func (ins *SInstance) GetOsType() cloudprovider.TOsType {
return cloudprovider.TOsType(ins.getNormalizedOsInfo().OsType)
}
func (ins *SInstance) GetFullOsName() string {
return ins.OSName
}
func (ins *SInstance) GetBios() cloudprovider.TBiosType {
return cloudprovider.ToBiosType(ins.getNormalizedOsInfo().OsBios)
}
func (ins *SInstance) GetOsArch() string {
return ins.getNormalizedOsInfo().OsArch
}
func (ins *SInstance) GetOsDist() string {
return ins.getNormalizedOsInfo().OsDistro
}
func (ins *SInstance) GetOsVersion() string {
return ins.getNormalizedOsInfo().OsVersion
}
func (ins *SInstance) GetOsLang() string {
return ins.getNormalizedOsInfo().OsLang
}
func (self *SInstance) GetMachine() string {
return "pc"
}
func (self *SInstance) instanceTypeHostPrefix() string {
if self.GPU > 0 && len(self.GpuType) > 0 {
return self.GpuType
}
if len(self.UHostType) > 0 {
return self.UHostType
}
if len(self.MachineType) > 0 {
return self.MachineType
}
return self.HostType
}
func (self *SInstance) GetInstanceType() string {
memGB := self.MemoryMB / 1024
return formatInstanceSpec(self.instanceTypeHostPrefix(), self.CPU, memGB, self.GPU)
}
// https://docs.ucloud.cn/api/unet-api/grant_firewall
func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
if len(secgroupIds) == 0 {
return fmt.Errorf("SetSecurityGroups secgroup id should not be empty")
} else if len(secgroupIds) > 1 {
return fmt.Errorf("SetSecurityGroups only allowed to assign one secgroup id. %d given", len(secgroupIds))
}
return self.host.zone.region.assignSecurityGroups(self.GetId(), secgroupIds[0])
}
func (self *SInstance) GetHypervisor() string {
return api.HYPERVISOR_ROCKBASE
}
func (self *SInstance) StartVM(ctx context.Context) error {
err := self.host.zone.region.StartVM(self.GetId())
if err != nil {
return err
}
err = cloudprovider.WaitStatusWithDelay(self, api.VM_RUNNING, 10*time.Second, 10*time.Second, 600*time.Second)
if err != nil {
return errors.Wrap(err, "StartVM")
}
return nil
}
func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
err := self.host.zone.region.StopVM(self.GetId())
if err != nil {
return err
}
err = cloudprovider.WaitStatusWithDelay(self, api.VM_READY, 10*time.Second, 10*time.Second, 600*time.Second)
if err != nil {
return errors.Wrap(err, "StopVM")
}
return nil
}
func (self *SInstance) DeleteVM(ctx context.Context) error {
return self.host.zone.region.DeleteVM(self.GetId())
}
func (self *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
return self.host.zone.region.UpdateVM(self.GetId(), input.NAME)
}
func (self *SInstance) UpdateUserData(userData string) error {
return cloudprovider.ErrNotSupported
}
// https://docs.ucloud.cn/api/uhost-api/reinstall_uhost_instance
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
err := self.host.zone.region.RebuildRoot(self.GetId(), desc.ImageId, desc.Password)
if err != nil {
return "", err
}
err = cloudprovider.WaitStatusWithDelay(self, api.VM_RUNNING, 10*time.Second, 15*time.Second, 300*time.Second)
if err != nil {
return "", errors.Wrap(err, "RebuildRoot")
}
for _, disk := range self.DiskSet {
if strings.EqualFold(disk.Type, "SystemDisk") || strings.EqualFold(disk.Type, "Boot") {
return disk.DiskId, nil
}
}
return "", errors.Wrapf(cloudprovider.ErrNotFound, "SystemDisk not found")
}
func (self *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
if len(opts.PublicKey) > 0 {
return fmt.Errorf("DeployVM not support assign ssh keypair")
}
if opts.DeleteKeypair {
return fmt.Errorf("DeployVM not support delete ssh keypair")
}
if self.GetStatus() != api.VM_READY {
return fmt.Errorf("DeployVM instance status %s , expected %s.", self.GetStatus(), api.VM_READY)
}
if len(opts.Password) > 0 {
err := self.host.zone.region.ResetVMPasswd(self.GetId(), opts.Password)
if err != nil {
return err
}
}
err := cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second)
if err != nil {
return errors.Wrap(err, "DeployVM")
}
return nil
}
func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
if len(config.InstanceType) > 0 {
return self.ChangeConfig2(ctx, config.InstanceType)
}
return self.host.zone.region.ResizeVM(self.GetId(), config.Cpu, config.MemoryMB)
}
func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
i, err := ParseInstanceType(instanceType)
if err != nil {
return err
}
return self.host.zone.region.ResizeVM(self.GetId(), i.CPU, i.MemoryMB)
}
func (self *SInstance) GetTags() (map[string]string, error) {
return self.host.zone.region.GetResourceTags(self.GetId())
}
func (self *SInstance) SetTags(tags map[string]string, replace bool) error {
return self.host.zone.region.SetResourceTags(self.GetId(), tags, replace)
}
func (self *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
image, err := self.host.zone.region.SaveImage(self.GetId(), opts)
if err != nil {
return nil, errors.Wrapf(err, "SaveImage")
}
return image, nil
}
func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
return self.host.zone.region.GetInstanceVNCUrl(self.GetId())
}
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
return self.host.zone.region.AttachDisk(self.host.zone.GetId(), self.GetId(), diskId)
}
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
err := self.host.zone.region.DetachDisk(self.host.zone.GetId(), self.GetId(), diskId)
if err != nil {
return err
}
disk, err := self.host.zone.region.GetDisk(diskId)
if err != nil {
return err
}
disk.storage = &SStorage{zone: self.host.zone, storageType: disk.GetStorageType()}
err = cloudprovider.WaitStatusWithDelay(disk, api.DISK_READY, 10*time.Second, 10*time.Second, 60*time.Second)
if err != nil {
return errors.Wrap(err, "DetachDisk")
}
return nil
}
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
// return self.host.zone.region
return self.host.zone.region.RenewInstance(self.GetId(), bc)
}
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
func (self *SRegion) GetInstanceVNCUrl(instanceId string) (*cloudprovider.ServerVncOutput, error) {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
vnc := SVncInfo{}
err := self.DoAction("GetUHostInstanceVncInfo", params, &vnc)
if err != nil {
return nil, err
}
ret := &cloudprovider.ServerVncOutput{
Host: vnc.VNCIP,
Port: vnc.VNCPort,
Password: vnc.VNCPassword,
Hypervisor: api.HYPERVISOR_ROCKBASE,
Protocol: "vnc",
InstanceId: instanceId,
}
return ret, nil
}
// https://docs.ucloud.cn/api/unet-api/grant_firewall
func (self *SRegion) assignSecurityGroups(instanceId string, secgroupId string) error {
params := NewRockbaseParams()
params.Set("FWId", secgroupId)
params.Set("ResourceType", "uhost")
params.Set("ResourceId", instanceId)
return self.DoAction("GrantFirewall", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/start_uhost_instance
func (self *SRegion) StartVM(instanceId string) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
return self.DoAction("StartUHostInstance", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/stop_uhost_instance
func (self *SRegion) StopVM(instanceId string) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
return self.DoAction("StopUHostInstance", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/terminate_uhost_instance
func (self *SRegion) DeleteVM(instanceId string) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
params.Set("Destroy", 1) // 跳过回收站,直接删除
params.Set("ReleaseUDisk", true)
return self.DoAction("TerminateUHostInstance", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/modify_uhost_instance_name
func (self *SRegion) UpdateVM(instanceId, name string) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
params.Set("Name", name)
return self.DoAction("ModifyUHostInstanceName", params, nil)
}
// ChargeType : Dynamic(按需)/Month(按月)/Year(按年)
func (self *SRegion) RenewInstance(instanceId string, bc billing.SBillingCycle) error {
params := NewRockbaseParams()
params.Set("ResourceId", instanceId)
params.Set("ResourceType", "Host")
if bc.GetMonths() >= 10 && bc.GetMonths() < 12 {
params.Set("ChargeType", "Year")
params.Set("Quantity", 1)
} else if bc.GetYears() >= 1 {
params.Set("ChargeType", "Year")
params.Set("Quantity", bc.GetYears())
} else {
params.Set("ChargeType", "Month")
params.Set("Quantity", bc.GetMonths())
}
return self.DoAction("CreateRenew", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/reset_uhost_instance_password
// 该操作需要UHost实例处于关闭状态。
func (self *SRegion) ResetVMPasswd(instanceId, password string) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
return self.DoAction("ResetUHostInstancePassword", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/reinstall_uhost_instance
// (密码格式使用BASE64编码;LoginMode不可变更)
func (self *SRegion) RebuildRoot(instanceId, imageId, password string) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
params.Set("ImageId", imageId)
return self.DoAction("ReinstallUHostInstance", params, nil)
}
// https://docs.ucloud.cn/api/uhost-api/resize_uhost_instance
func (self *SRegion) ResizeVM(instanceId string, cpu, memoryMB int) error {
params := NewRockbaseParams()
params.Set("UHostId", instanceId)
params.Set("CPU", cpu)
params.Set("Memory", memoryMB)
return self.DoAction("ResizeUHostInstance", params, nil)
}
-56
View File
@@ -1,56 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
type SInstanceNic struct {
instance *SInstance
ipAddr string
macAddr string
cloudprovider.DummyICloudNic
}
func (self *SInstanceNic) GetId() string {
return ""
}
func (self *SInstanceNic) GetIP() string {
return self.ipAddr
}
func (self *SInstanceNic) GetMAC() string {
return self.macAddr
}
func (self *SInstanceNic) GetDriver() string {
return "virtio"
}
func (self *SInstanceNic) InClassicNetwork() bool {
return false
}
func (self *SInstanceNic) GetINetworkId() string {
for _, ip := range self.instance.IPSet {
if ip.IP == self.ipAddr {
return ip.SubnetId
}
}
return ""
}
-355
View File
@@ -1,355 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"sort"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/jsonutils"
)
// SAvailableInstanceType 对应 DescribeAvailableInstanceTypes 返回的机型信息。
type SAvailableInstanceType struct {
Zone string
InstanceType string
Name string
Status string
ParentType string
Description string
MachineClass string
MachineSizes []SMachineSize
UHostFamilies []SUHostFamily
Disks []SDiskCategory
}
type SDiskCategory struct {
Name string
BootDisk []SBootDiskInfo
DataDisk []SDataDiskInfo
}
type SBootDiskInfo struct {
Name string
InstantResize bool
MaximalSize int
Features []string
}
type SDataDiskInfo struct {
Name string
MinimalSize int
MaximalSize int
Features []string
}
type SUHostFamily struct {
Name string
CpuFrequency string
}
type SMachineSize struct {
Gpu int
Collection []SInstanceCollection
}
type SInstanceCollection struct {
Cpu int
MemoryGB []int
MinimalCpuPlatform []string
}
// SInstancePackage 展平后的云主机套餐规格,Spec 格式与 instance-create 的 INSTANCETYPE 一致。
type SInstancePackage struct {
Zone string
Name string
UHostType string
Status string
CPU int
MemoryMB int
GPU int
Spec string
Description string
}
func formatInstanceSpec(hostType string, cpu, memoryGB, gpu int) string {
spec := fmt.Sprintf("%s.c%d.m%d", hostType, cpu, memoryGB)
if gpu > 0 {
spec = fmt.Sprintf("%s.g%d", spec, gpu)
}
return spec
}
func parseMemoryGB(obj jsonutils.JSONObject) []int {
val, err := obj.Get("Memory")
if err != nil || val == nil {
return nil
}
switch v := val.(type) {
case *jsonutils.JSONArray:
items, _ := v.GetArray()
ret := make([]int, 0, len(items))
for _, item := range items {
if n, err := item.Int(); err == nil {
ret = append(ret, int(n))
}
}
return ret
default:
if n, err := val.Int(); err == nil {
// 兼容 Memory 为单个整数的情况:小于 512 视为 GB,否则视为 MB。
if n >= 512 {
return []int{int(n / 1024)}
}
return []int{int(n)}
}
}
return nil
}
func parseStringArray(obj jsonutils.JSONObject, key string) []string {
arr, err := obj.GetArray(key)
if err != nil {
return nil
}
ret := make([]string, 0, len(arr))
for _, item := range arr {
if s, err := item.GetString(); err == nil {
ret = append(ret, s)
}
}
return ret
}
func parseInstanceCollection(obj jsonutils.JSONObject) SInstanceCollection {
cpu, _ := obj.Int("Cpu")
return SInstanceCollection{
Cpu: int(cpu),
MemoryGB: parseMemoryGB(obj),
MinimalCpuPlatform: parseStringArray(obj, "MinimalCpuPlatform"),
}
}
func parseMachineSize(obj jsonutils.JSONObject) SMachineSize {
gpu, _ := obj.Int("Gpu")
size := SMachineSize{Gpu: int(gpu)}
cols, err := obj.GetArray("Collection")
if err != nil {
return size
}
for _, col := range cols {
size.Collection = append(size.Collection, parseInstanceCollection(col))
}
return size
}
func parseBootDiskInfo(obj jsonutils.JSONObject) SBootDiskInfo {
ret := SBootDiskInfo{}
ret.Name, _ = obj.GetString("Name")
ret.InstantResize, _ = obj.Bool("InstantResize")
maxSize, _ := obj.Int("MaximalSize")
ret.MaximalSize = int(maxSize)
ret.Features = parseStringArray(obj, "Features")
return ret
}
func parseDataDiskInfo(obj jsonutils.JSONObject) SDataDiskInfo {
ret := SDataDiskInfo{}
ret.Name, _ = obj.GetString("Name")
minSize, _ := obj.Int("MinimalSize")
ret.MinimalSize = int(minSize)
maxSize, _ := obj.Int("MaximalSize")
ret.MaximalSize = int(maxSize)
ret.Features = parseStringArray(obj, "Features")
return ret
}
func parseDiskCategory(obj jsonutils.JSONObject) SDiskCategory {
ret := SDiskCategory{}
ret.Name, _ = obj.GetString("Name")
bootDisks, err := obj.GetArray("BootDisk")
if err == nil {
for _, boot := range bootDisks {
ret.BootDisk = append(ret.BootDisk, parseBootDiskInfo(boot))
}
}
dataDisks, err := obj.GetArray("DataDisk")
if err == nil {
for _, data := range dataDisks {
ret.DataDisk = append(ret.DataDisk, parseDataDiskInfo(data))
}
}
return ret
}
func parseAvailableInstanceType(obj jsonutils.JSONObject) SAvailableInstanceType {
ret := SAvailableInstanceType{}
ret.Zone, _ = obj.GetString("Zone")
ret.InstanceType, _ = obj.GetString("InstanceType")
ret.Name, _ = obj.GetString("Name")
ret.Status, _ = obj.GetString("Status")
ret.ParentType, _ = obj.GetString("ParentType")
ret.Description, _ = obj.GetString("Description")
ret.MachineClass, _ = obj.GetString("MachineClass")
sizes, err := obj.GetArray("MachineSizes")
if err == nil {
for _, size := range sizes {
ret.MachineSizes = append(ret.MachineSizes, parseMachineSize(size))
}
}
families, err := obj.GetArray("UHostFamilies")
if err == nil {
for _, family := range families {
f := SUHostFamily{}
f.Name, _ = family.GetString("Name")
f.CpuFrequency, _ = family.GetString("CpuFrequency")
ret.UHostFamilies = append(ret.UHostFamilies, f)
}
}
disks, err := obj.GetArray("Disks")
if err == nil {
for _, disk := range disks {
ret.Disks = append(ret.Disks, parseDiskCategory(disk))
}
}
return ret
}
func parseAvailableInstanceTypes(resp jsonutils.JSONObject) ([]SAvailableInstanceType, error) {
arr, err := resp.GetArray("AvailableInstanceTypes")
if err != nil {
return nil, err
}
ret := make([]SAvailableInstanceType, 0, len(arr))
for _, item := range arr {
ret = append(ret, parseAvailableInstanceType(item))
}
return ret, nil
}
func flattenInstancePackages(types []SAvailableInstanceType) []SInstancePackage {
ret := make([]SInstancePackage, 0)
for i := range types {
hostType := types[i].ParentType
if len(hostType) == 0 {
hostType = types[i].Name
}
for _, size := range types[i].MachineSizes {
for _, col := range size.Collection {
if col.Cpu <= 0 || len(col.MemoryGB) == 0 {
continue
}
for _, memGB := range col.MemoryGB {
if memGB <= 0 {
continue
}
ret = append(ret, SInstancePackage{
Zone: types[i].Zone,
Name: types[i].Name,
UHostType: hostType,
Status: types[i].Status,
CPU: col.Cpu,
MemoryMB: memGB * 1024,
GPU: size.Gpu,
Spec: formatInstanceSpec(hostType, col.Cpu, memGB, size.Gpu),
Description: types[i].Description,
})
}
}
}
}
return ret
}
var rockbaseStorageTypeOrder = []string{
api.STORAGE_ROCKBASE_CLOUD_NORMAL,
api.STORAGE_ROCKBASE_CLOUD_SSD,
api.STORAGE_ROCKBASE_CLOUD_ESSD,
api.STORAGE_ROCKBASE_CLOUD_RSSD,
api.STORAGE_ROCKBASE_LOCAL_NORMAL,
api.STORAGE_ROCKBASE_LOCAL_SSD,
api.STORAGE_ROCKBASE_EXCLUSIVE_LOCAL_DISK,
}
func collectStorageTypesFromInstanceTypes(types []SAvailableInstanceType, zoneId string) []string {
typeSet := map[string]struct{}{}
for i := range types {
if len(zoneId) > 0 && len(types[i].Zone) > 0 && types[i].Zone != zoneId {
continue
}
for _, disk := range types[i].Disks {
for _, boot := range disk.BootDisk {
if len(boot.Name) > 0 {
typeSet[boot.Name] = struct{}{}
}
}
for _, data := range disk.DataDisk {
if len(data.Name) > 0 {
typeSet[data.Name] = struct{}{}
}
}
}
}
ret := make([]string, 0, len(typeSet))
for _, storageType := range rockbaseStorageTypeOrder {
if _, ok := typeSet[storageType]; ok {
ret = append(ret, storageType)
delete(typeSet, storageType)
}
}
others := make([]string, 0, len(typeSet))
for storageType := range typeSet {
others = append(others, storageType)
}
sort.Strings(others)
ret = append(ret, others...)
return ret
}
// GetZoneStorageTypes 从 DescribeAvailableInstanceTypes 的 Disks 信息获取可用区存储类型。
func (self *SRegion) GetZoneStorageTypes(zoneId string) ([]string, error) {
types, err := self.GetAvailableInstanceTypes(zoneId)
if err != nil {
return nil, err
}
return collectStorageTypesFromInstanceTypes(types, zoneId), nil
}
// GetAvailableInstanceTypes 获取地域/可用区下可售机型信息。
func (self *SRegion) GetAvailableInstanceTypes(zoneId string) ([]SAvailableInstanceType, error) {
params := NewRockbaseParams()
if len(zoneId) > 0 {
params.Set("Zone", zoneId)
}
params.Set("Region", self.GetId())
params = self.client.commonParams(params)
params.SetAction("DescribeAvailableInstanceTypes")
resp, err := jsonRequest(self.client, params)
if err != nil {
return nil, err
}
return parseAvailableInstanceTypes(resp)
}
// GetInstancePackages 获取展平后的云主机套餐列表。
func (self *SRegion) GetInstancePackages(zoneId string) ([]SInstancePackage, error) {
types, err := self.GetAvailableInstanceTypes(zoneId)
if err != nil {
return nil, err
}
return flattenInstancePackages(types), nil
}
-209
View File
@@ -1,209 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"strings"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
const (
ROCKBASE_LABEL_CATEGORY_CUSTOM = "custom"
ROCKBASE_LABEL_CATEGORY_SYSTEM = "system"
)
type SResourceLabel struct {
ResourceId string `json:"ResourceId"`
Key string `json:"Key"`
Value string `json:"Value"`
Category string `json:"Category"`
}
func setLabelsParams(params *SParams, prefix string, labels map[string]string) {
idx := 0
for k, v := range labels {
params.Set(fmt.Sprintf("%s.%d.Key", prefix, idx), k)
params.Set(fmt.Sprintf("%s.%d.Value", prefix, idx), v)
idx++
}
}
func setResourceIdsParams(params *SParams, resourceIds []string) {
for i, id := range resourceIds {
params.Set(fmt.Sprintf("ResourceIds.%d", i), id)
}
}
// https://docs.ucloud.cn/api/label-api/list_labels_by_resource_ids
func (client *SRockbaseClient) fetchAllLabelsByResourceIds(resourceIds []string) ([]SResourceLabel, error) {
if len(resourceIds) == 0 {
return nil, nil
}
ret := []SResourceLabel{}
offset := 0
limit := 100
for {
params := client.commonParams(NewRockbaseParams())
setResourceIdsParams(&params, resourceIds)
params.Set("Offset", offset)
params.Set("Limit", limit)
params.SetAction("ListLabelsByResourceIds")
resp, err := jsonRequest(client, params)
if err != nil {
return nil, errors.Wrap(err, "ListLabelsByResourceIds")
}
part := []SResourceLabel{}
err = resp.Unmarshal(&part, "Labels")
if err != nil {
return nil, errors.Wrap(err, "unmarshal Labels")
}
ret = append(ret, part...)
total, _ := resp.Int("TotalCount")
if int(total) > 0 && len(ret) >= int(total) {
break
}
if len(part) < limit {
break
}
offset += limit
}
return ret, nil
}
func filterCustomLabels(labels []SResourceLabel, resourceId string) map[string]string {
ret := map[string]string{}
for i := range labels {
if labels[i].ResourceId != resourceId {
continue
}
if labels[i].Category == ROCKBASE_LABEL_CATEGORY_SYSTEM {
continue
}
ret[labels[i].Key] = labels[i].Value
}
return ret
}
// https://docs.ucloud.cn/api/label-api/create_labels
func (client *SRockbaseClient) createLabels(labels map[string]string) error {
if len(labels) == 0 {
return nil
}
params := client.commonParams(NewRockbaseParams())
setLabelsParams(&params, "Labels", labels)
params.SetAction("CreateLabels")
_, err := jsonRequest(client, params)
if err != nil {
if uerr, ok := err.(*SRockbaseError); ok && strings.Contains(strings.ToLower(uerr.Message), "duplicate") {
return nil
}
return errors.Wrap(err, "CreateLabels")
}
return nil
}
// https://docs.ucloud.cn/api/label-api/bind_labels
func (client *SRockbaseClient) bindLabels(resourceIds []string, labels map[string]string) error {
if len(resourceIds) == 0 || len(labels) == 0 {
return nil
}
params := client.commonParams(NewRockbaseParams())
setResourceIdsParams(&params, resourceIds)
setLabelsParams(&params, "Labels", labels)
params.SetAction("BindLabels")
_, err := jsonRequest(client, params)
if err != nil {
return errors.Wrap(err, "BindLabels")
}
return nil
}
// https://docs.ucloud.cn/api/label-api/unbind_labels
func (client *SRockbaseClient) unbindLabels(resourceIds []string, labels map[string]string) error {
if len(resourceIds) == 0 || len(labels) == 0 {
return nil
}
params := client.commonParams(NewRockbaseParams())
setResourceIdsParams(&params, resourceIds)
setLabelsParams(&params, "Labels", labels)
params.SetAction("UnbindLabels")
_, err := jsonRequest(client, params)
if err != nil {
return errors.Wrap(err, "UnbindLabels")
}
return nil
}
func (self *SRegion) GetResourceTags(resourceId string) (map[string]string, error) {
if len(resourceId) == 0 {
return nil, errors.Wrap(cloudprovider.ErrMissingParameter, "resourceId")
}
labels, err := self.client.fetchAllLabelsByResourceIds([]string{resourceId})
if err != nil {
return nil, errors.Wrap(err, "fetchAllLabelsByResourceIds")
}
return filterCustomLabels(labels, resourceId), nil
}
func (self *SRegion) SetResourceTags(resourceId string, tags map[string]string, replace bool) error {
if len(resourceId) == 0 {
return errors.Wrap(cloudprovider.ErrMissingParameter, "resourceId")
}
if len(tags) == 0 && !replace {
return nil
}
resourceIds := []string{resourceId}
existing, err := self.GetResourceTags(resourceId)
if err != nil {
return errors.Wrap(err, "GetResourceTags")
}
toUnbind := map[string]string{}
for k, v := range existing {
nv, ok := tags[k]
if replace {
if !ok || nv != v {
toUnbind[k] = v
}
} else if ok && nv != v {
toUnbind[k] = v
}
}
if err := self.client.unbindLabels(resourceIds, toUnbind); err != nil {
return errors.Wrap(err, "unbindLabels")
}
if len(tags) == 0 {
return nil
}
if err := self.client.createLabels(tags); err != nil {
return errors.Wrap(err, "createLabels")
}
return self.client.bindLabels(resourceIds, tags)
}
func (self *SRegion) FetchResourceTags(resourceIds []string) (map[string]map[string]string, error) {
labels, err := self.client.fetchAllLabelsByResourceIds(resourceIds)
if err != nil {
return nil, err
}
ret := map[string]map[string]string{}
for _, id := range resourceIds {
ret[id] = filterCustomLabels(labels, id)
}
return ret, nil
}
@@ -1,54 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
// 中国
"cn-bj2": api.RegionBeijing,
"cn-wlcb": api.RegionWulanchabu,
"cn-sh2": api.RegionShanghai,
"cn-gd": api.RegionGuangzhou,
"cn-guiyang1": api.RegionGuiyang,
"hk": api.RegionHongkong,
"tw-tp": api.RegionTaipei,
// 亚太
"sg": api.RegionSingapore,
"jpn-tky": api.RegionTokyo,
"kr-seoul": api.RegionSeoul,
"th-bkk": api.RegionBangkok,
"idn-jakarta": api.RegionJakarta,
"vn-sng": api.RegionHoChiMinh,
"ph-mnl": api.RegionManila,
"ind-mumbai": api.RegionMumbai,
"pk-khi": api.RegionKarachi,
"uz-tas": api.RegionTashkent,
"kz-ala": api.RegionAlmaty,
// 美洲
"us-den": api.RegionDenver,
"us-ca": api.RegionLosAngeles,
"us-ws": api.RegionWashingtonDC,
"bra-saopaulo": api.RegionSaoPaulo,
"mx-mex": api.RegionMexico,
// 欧洲、中东及非洲
"ge-fra": api.RegionFrankfurt,
"uk-london": api.RegionLondon,
"uae-dubai": api.RegionDubai,
"afr-nigeria": api.RegionLagos,
}
-193
View File
@@ -1,193 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
const (
// https://docs.ucloud.cn/cloudwatch/metric/intro
ROCKBASE_PRODUCT_KEY_UHOST = "uhost"
)
type SMetricSample struct {
Timestamp int64
Value float64
}
type SMetricResult struct {
ResourceId string
ResourceName string
TagMap map[string]string
Values []SMetricSample
}
type SQueryMetricDataItem struct {
Metric string
Results []SMetricResult
}
type SQueryMetricDataResp struct {
InvalidResourceIds []string
List []SQueryMetricDataItem
}
var uhostMetricNames = map[cloudprovider.TMetricType]string{
cloudprovider.VM_METRIC_TYPE_CPU_USAGE: "uhost_cpu_used",
cloudprovider.VM_METRIC_TYPE_MEM_USAGE: "cloudwatch_memory_usage",
cloudprovider.VM_METRIC_TYPE_DISK_USAGE: "cloudwatch_sys_disk_used_per",
cloudprovider.VM_METRIC_TYPE_NET_BPS_RX: "uhost_net_in_flow",
cloudprovider.VM_METRIC_TYPE_NET_BPS_TX: "uhost_net_out_flow",
cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_BPS: "uhost_disk_read",
cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_BPS: "uhost_disk_write",
cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_IOPS: "uhost_disk_read_times",
cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_IOPS: "uhost_disk_write_times",
}
var uhostMetricNameTypes map[string]cloudprovider.TMetricType
func init() {
uhostMetricNameTypes = make(map[string]cloudprovider.TMetricType, len(uhostMetricNames))
for metricType, name := range uhostMetricNames {
uhostMetricNameTypes[name] = metricType
}
}
func pickMetricPeriod(start, end time.Time, interval int) int {
if interval > 0 {
return interval
}
d := end.Sub(start)
switch {
case d <= time.Hour:
return 60
case d <= 12*time.Hour:
return 60
case d <= 24*time.Hour:
return 300
default:
return 3600
}
}
// https://docs.ucloud.cn/api/cloudwatch-api/query_metric_data_set
func (self *SRegion) QueryMetricDataSet(productKey string, resourceIds []string, metricNames []string, start, end time.Time, period int) (*SQueryMetricDataResp, error) {
params := NewRockbaseParams()
params.Set("Region", self.GetId())
params.Set("ProductKey", productKey)
params.Set("StartTime", int(start.Unix()))
params.Set("EndTime", int(end.Unix()))
params.Set("CalcMethod", "avg")
params.Set("Period", period)
idx := 0
for _, resourceId := range resourceIds {
for _, metricName := range metricNames {
params.Set(fmt.Sprintf("MetricInfos.%d.Metric", idx), metricName)
params.Set(fmt.Sprintf("MetricInfos.%d.ResourceId", idx), resourceId)
idx++
}
}
if idx == 0 {
return nil, errors.Wrap(cloudprovider.ErrMissingParameter, "metric infos")
}
params = self.client.commonParams(params)
params.SetAction("QueryMetricDataSet")
resp, err := jsonRequest(self.client, params)
if err != nil {
return nil, errors.Wrap(err, "QueryMetricDataSet")
}
ret := SQueryMetricDataResp{}
err = resp.Unmarshal(&ret, "Data")
if err != nil {
return nil, errors.Wrap(err, "unmarshal Data")
}
return &ret, nil
}
func (self *SRegion) GetUhostMetrics(resourceIds []string, start, end time.Time, interval int) ([]cloudprovider.MetricValues, error) {
metricNames := make([]string, 0, len(uhostMetricNames))
for _, name := range uhostMetricNames {
metricNames = append(metricNames, name)
}
period := pickMetricPeriod(start, end, interval)
data, err := self.QueryMetricDataSet(ROCKBASE_PRODUCT_KEY_UHOST, resourceIds, metricNames, start, end, period)
if err != nil {
return nil, errors.Wrap(err, "QueryMetricDataSet")
}
ret := []cloudprovider.MetricValues{}
for _, item := range data.List {
metricType, ok := uhostMetricNameTypes[item.Metric]
if !ok {
log.Warningf("unknown uhost metric %s", item.Metric)
continue
}
for _, result := range item.Results {
metric := cloudprovider.MetricValues{
Id: result.ResourceId,
MetricType: metricType,
Values: []cloudprovider.MetricValue{},
}
for _, sample := range result.Values {
value := sample.Value
switch metricType {
case cloudprovider.VM_METRIC_TYPE_NET_BPS_RX, cloudprovider.VM_METRIC_TYPE_NET_BPS_TX:
// uhost network metrics unit is bps (bits per second)
value = value / 8.0
}
metric.Values = append(metric.Values, cloudprovider.MetricValue{
Timestamp: time.Unix(sample.Timestamp, 0),
Value: value,
})
}
if len(metric.Values) > 0 {
ret = append(ret, metric)
}
}
}
return ret, nil
}
func (self *SRockbaseClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
switch opts.ResourceType {
case cloudprovider.METRIC_RESOURCE_TYPE_SERVER:
return self.GetEcsMetrics(opts)
default:
return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.ResourceType)
}
}
func (self *SRockbaseClient) GetEcsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
region := self.GetRegion(opts.RegionExtId)
if region == nil {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "region %s", opts.RegionExtId)
}
resourceIds := opts.ResourceIds
if len(resourceIds) == 0 && len(opts.ResourceId) > 0 {
resourceIds = []string{opts.ResourceId}
}
if len(resourceIds) == 0 {
return nil, errors.Wrap(cloudprovider.ErrMissingParameter, "resource_id")
}
return region.GetUhostMetrics(resourceIds, opts.StartTime, opts.EndTime, opts.Interval)
}
-201
View File
@@ -1,201 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/rbacscope"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://docs.ucloud.cn/api/vpc2.0-api/describe_subnet
type SNetwork struct {
multicloud.SNetworkBase
RockbaseTags
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.vpc.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 api.NETWORK_STATUS_AVAILABLE
}
func (self *SNetwork) Refresh() error {
log.Debugf("network refresh %s", self.GetId())
new, err := self.wire.vpc.region.getNetwork(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SNetwork) IsEmulated() bool {
return false
}
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 gateway
startIp = startIp.StepUp() // 2
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
return endIp.String()
}
func (self *SNetwork) GetIpMask() int8 {
pref, _ := netutils.NewIPV4Prefix(self.Subnet + "/" + self.Netmask)
return pref.MaskLen
}
func (self *SNetwork) GetGateway() string {
return self.Gateway
}
func (self *SNetwork) GetServerType() string {
return api.NETWORK_TYPE_GUEST
}
func (self *SNetwork) GetIsPublic() bool {
return true
}
func (self *SNetwork) GetPublicScope() rbacscope.TRbacScope {
return rbacscope.ScopeDomain
}
func (self *SNetwork) Delete() error {
return self.wire.vpc.region.DeleteNetwork(self.GetId())
}
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) {
return self.getNetwork(networkId)
}
func (self *SRegion) getNetwork(networkId string) (*SNetwork, error) {
networks := make([]SNetwork, 0)
params := NewRockbaseParams()
params.Set("SubnetId", networkId)
err := self.DoListAll("DescribeSubnet", params, &networks)
if err != nil {
return nil, err
}
for i := range networks {
if networks[i].SubnetId == networkId {
vpc, err := self.GetVpc(networks[i].VpcId)
if err != nil {
return nil, err
}
networks[i].wire = &SWire{vpc: vpc}
return &networks[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "getNetwork %s", networkId)
}
// https://docs.ucloud.cn/api/vpc2.0-api/delete_subnet
func (self *SRegion) DeleteNetwork(networkId string) error {
params := NewRockbaseParams()
params.Set("SubnetId", networkId)
return self.DoAction("DeleteSubnet", params, nil)
}
// https://docs.ucloud.cn/api/vpc2.0-api/create_subnet
func (self *SRegion) CreateNetwork(vpcId string, name string, cidr string, desc string) (*SNetwork, error) {
ip, mask, err := netutils.ParsePrefix(cidr)
if err != nil {
return nil, fmt.Errorf("CreateINetwork invalid cidr %s", cidr)
}
params := NewRockbaseParams()
params.Set("VPCId", vpcId)
params.Set("Subnet", ip.String())
params.Set("Netmask", int(mask))
params.Set("SubnetName", name)
params.Set("Remark", desc)
type SNet struct {
SubnetId string
}
net := SNet{}
err = self.DoAction("CreateSubnet", params, &net)
if err != nil {
return nil, err
}
return self.getNetwork(net.SubnetId)
}
-64
View File
@@ -1,64 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://docs.ucloud.cn/api/summary/get_project_list
type SProject struct {
multicloud.SProjectBase
RockbaseTags
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 *SRockbaseClient) FetchProjects() ([]SProject, error) {
params := NewRockbaseParams()
projects := make([]SProject, 0)
err := self.DoListAll("GetProjectList", params, &projects)
return projects, err
}
@@ -1,194 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package provider
import (
"context"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/rockbase"
)
// tag:finished
type SRockbaseProviderFactory struct {
cloudprovider.SPublicCloudBaseProviderFactory
}
func (self *SRockbaseProviderFactory) GetId() string {
return rockbase.CLOUD_PROVIDER_ROCKBASE
}
func (self *SRockbaseProviderFactory) GetName() string {
return rockbase.CLOUD_PROVIDER_ROCKBASE_CN
}
func (self *SRockbaseProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) {
output := cloudprovider.SCloudaccount{}
if len(input.AccessKeyId) == 0 {
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "access_key_id")
}
if len(input.AccessKeySecret) == 0 {
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "access_key_secret")
}
output.Account = input.AccessKeyId
output.Secret = input.AccessKeySecret
return output, nil
}
func (self *SRockbaseProviderFactory) ValidateUpdateCloudaccountCredential(ctx context.Context, input cloudprovider.SCloudaccountCredential, cloudaccount string) (cloudprovider.SCloudaccount, error) {
output := cloudprovider.SCloudaccount{}
if len(input.AccessKeyId) == 0 {
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "access_key_id")
}
if len(input.AccessKeySecret) == 0 {
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "access_key_secret")
}
output = cloudprovider.SCloudaccount{
Account: input.AccessKeyId,
Secret: input.AccessKeySecret,
}
return output, nil
}
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
}
func (self *SRockbaseProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) {
accessKey, projectId := parseAccount(cfg.Account)
client, err := rockbase.NewRockbaseClient(
rockbase.NewRockbaseClientConfig(
accessKey, cfg.Secret,
).ProjectId(projectId).CloudproviderConfig(cfg),
)
if err != nil {
return nil, err
}
return &SRockbaseProvider{
SBaseProvider: cloudprovider.NewBaseProvider(self),
client: client,
}, nil
}
func (self *SRockbaseProviderFactory) GetClientRC(info cloudprovider.SProviderInfo) (map[string]string, error) {
accessKey, projectId := parseAccount(info.Account)
return map[string]string{
"ROCKBASE_ACCESS_KEY_ID": accessKey,
"ROCKBASE_ACCESS_KEY_SECRET": info.Secret,
"ROCKBASE_REGION_ID": rockbase.ROCKBASE_DEFAULT_REGION,
"ROCKBASE_PROJECT_ID": projectId,
}, nil
}
func init() {
factory := SRockbaseProviderFactory{}
cloudprovider.RegisterFactory(&factory)
}
type SRockbaseProvider struct {
cloudprovider.SBaseProvider
client *rockbase.SRockbaseClient
}
func (self *SRockbaseProvider) 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 *SRockbaseProvider) GetSysInfo() (jsonutils.JSONObject, error) {
regions, _ := self.client.GetIRegions()
info := jsonutils.NewDict()
info.Add(jsonutils.NewInt(int64(len(regions))), "region_count")
info.Add(jsonutils.NewString(rockbase.ROCKBASE_API_VERSION), "api_version")
return info, nil
}
func (self *SRockbaseProvider) GetVersion() string {
return rockbase.ROCKBASE_API_VERSION
}
func (self *SRockbaseProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
return self.client.GetSubAccounts()
}
func (self *SRockbaseProvider) GetAccountId() string {
return self.client.GetAccountId()
}
func (self *SRockbaseProvider) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
return self.client.GetIRegions()
}
func (self *SRockbaseProvider) GetIRegionById(extId string) (cloudprovider.ICloudRegion, error) {
return self.client.GetIRegionById(extId)
}
func (self *SRockbaseProvider) GetBalance() (*cloudprovider.SBalanceInfo, error) {
return self.client.GetBalance()
}
func (self *SRockbaseProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRockbaseProvider) GetStorageClasses(regionId string) []string {
return []string{
"STANDARD", "IA", "ARCHIVE",
}
}
func (self *SRockbaseProvider) GetBucketCannedAcls(regionId string) []string {
return []string{
string(cloudprovider.ACLPrivate),
string(cloudprovider.ACLPublicRead),
}
}
func (self *SRockbaseProvider) GetObjectCannedAcls(regionId string) []string {
return []string{
string(cloudprovider.ACLPrivate),
string(cloudprovider.ACLPublicRead),
}
}
func (self *SRockbaseProvider) GetCapabilities() []string {
return self.client.GetCapabilities()
}
func (self *SRockbaseProvider) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
return self.client.GetMetrics(opts)
}
-505
View File
@@ -1,505 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"strings"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SRegion struct {
multicloud.SRegion
multicloud.SNoLbRegion
client *SRockbaseClient
RegionId string `json:"Region"`
LocalName string `json:"LocalName"`
CountryCode string `json:"CountryCode"`
Category string `json:"Category"`
storageCache *SStoragecache
}
func (self *SRegion) GetId() string {
return self.RegionId
}
func (self *SRegion) GetName() string {
return self.LocalName
}
func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable {
table := cloudprovider.SModelI18nTable{}
return table
}
func (self *SRegion) GetGlobalId() string {
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_ROCKBASE, self.GetId())
}
func (self *SRegion) GetStatus() string {
return api.CLOUD_REGION_STATUS_INSERVER
}
func (self *SRegion) Refresh() error {
return nil
}
func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
if info, ok := LatitudeAndLongitude[self.GetId()]; ok {
return info
}
return cloudprovider.SGeographicInfo{}
}
func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
instance, err := self.GetInstance(id)
if err != nil {
return nil, err
}
return instance, nil
}
func (self *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) {
return self.GetDisk(id)
}
func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) {
zones, err := self.GetZones()
if err != nil {
return nil, err
}
ret := []cloudprovider.ICloudZone{}
for i := range zones {
zones[i].region = self
ret = append(ret, &zones[i])
}
return ret, nil
}
func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
vpcs, err := self.GetVpcs("")
if err != nil {
return nil, errors.Wrapf(err, "GetVpcs")
}
ret := []cloudprovider.ICloudVpc{}
for i := range vpcs {
vpcs[i].region = self
ret = append(ret, &vpcs[i])
}
return ret, nil
}
// https://docs.ucloud.cn/api/unet-api/describe_eip
func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
params := NewRockbaseParams()
eips := make([]SEip, 0)
err := self.DoListAll("DescribeEIP", params, &eips)
if err != nil {
return nil, err
}
ieips := []cloudprovider.ICloudEIP{}
for i := range eips {
eip := eips[i]
eip.region = self
ieips = append(ieips, &eip)
}
return ieips, nil
}
func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
return self.GetVpc(id)
}
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) GetEip(eipId string) (*SEip, error) {
params := NewRockbaseParams()
params.Set("EIPIds.0", eipId)
eips := make([]SEip, 0)
err := self.DoListAll("DescribeEIP", params, &eips)
if err != nil {
return nil, err
}
for i := range eips {
if eips[i].EIPId == eipId {
eips[i].region = self
return &eips[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetEip %s", eipId)
}
func (self *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) {
eip, err := self.GetEip(id)
if err != nil {
return nil, err
}
return eip, nil
}
// https://docs.ucloud.cn/api/unet-api/delete_firewall
func (self *SRegion) DeleteSecurityGroup(secgroupId string) error {
params := NewRockbaseParams()
params.Set("FWId", secgroupId)
return self.DoAction("DeleteFirewall", params, nil)
}
func (self *SRegion) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
secgroups, err := self.GetSecurityGroups("", "", "")
if err != nil {
return nil, err
}
ret := []cloudprovider.ICloudSecurityGroup{}
for i := 0; i < len(secgroups); i++ {
secgroups[i].region = self
ret = append(ret, &secgroups[i])
}
return ret, nil
}
func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) {
return self.GetSecurityGroup(secgroupId)
}
func (self *SRegion) CreateISecurityGroup(opts *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
externalId, err := self.CreateSecurityGroup(opts.Name, opts.Desc)
if err != nil {
return nil, err
}
return self.GetISecurityGroupById(externalId)
}
// https://docs.ucloud.cn/api/unet-api/describe_firewall
// 绑定防火墙组的资源类型,默认为全部资源类型。枚举值为:"unatgw"NAT网关; "uhost",云主机; "upm",物理云主机; "hadoophost"hadoop节点; "fortresshost",堡垒机; "udhost",私有专区主机;"udockhost",容器;"dbaudit",数据库审计.
// todo: 是否需要过滤出仅绑定云主机的安全组?
func (self *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) {
params := NewRockbaseParams()
params.Set("Name", opts.NAME)
params.Set("Remark", opts.Desc)
for i, cidr := range strings.Split(opts.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/udisk-api/describe_udisk_snapshot
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
params := NewRockbaseParams()
snapshots := make([]SSnapshot, 0)
err := self.DoListAll("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
isnapshots := make([]cloudprovider.ICloudSnapshot, 0)
for i := range snapshots {
snapshots[i].region = self
isnapshots = append(isnapshots, &snapshots[i])
}
return isnapshots, nil
}
func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
if len(snapshotId) == 0 {
return nil, cloudprovider.ErrNotFound
}
params := NewRockbaseParams()
snapshots := make([]SSnapshot, 0)
err := self.DoListAll("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
for i := range snapshots {
if snapshots[i].SnapshotId == snapshotId {
snapshot := snapshots[i]
snapshot.region = self
return &snapshot, nil
}
}
return nil, cloudprovider.ErrNotFound
}
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 errors.Cause(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 errors.Cause(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) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SRegion) GetProvider() string {
return CLOUD_PROVIDER_ROCKBASE
}
func (self *SRegion) GetCloudEnv() string {
return ""
}
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) GetZones() ([]SZone, error) {
params := NewRockbaseParams()
params.Set("Region", self.GetId())
zones := make([]SZone, 0)
err := self.client.DoListAll("ListZones", params, &zones)
return zones, err
}
// https://docs.ucloud.cn/api/uhost-api/describe_uhost_instance
func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) {
params := NewRockbaseParams()
params.Set("UHostIds.0", instanceId)
instances := make([]SInstance, 0)
err := self.DoAction("DescribeUHostInstance", params, &instances)
if err != nil {
return nil, errors.Wrapf(err, "DescribeUHostInstance")
}
for i := range instances {
if instances[i].UHostId == instanceId {
return &instances[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetInstance %s", instanceId)
}
func (self *SRegion) GetClient() *SRockbaseClient {
return self.client
}
// https://docs.ucloud.cn/api/ufile-api/describe_bucket
func (client *SRockbaseClient) listBuckets(name string, offset int, limit int) ([]SBucket, error) {
params := NewRockbaseParams()
if len(name) > 0 {
params.Set("BucketName", name)
} else {
params.Set("Limit", limit)
params.Set("Offset", offset)
}
buckets := make([]SBucket, 0)
// request without RegionId
err := client.DoAction("DescribeBucket", params, &buckets)
if err != nil {
return nil, errors.Wrap(err, "DoAction DescribeBucket")
}
return buckets, nil
}
// https://docs.ucloud.cn/api/ufile-api/update_bucket
func (region *SRegion) updateBucket(name string, aclType string) error {
params := NewRockbaseParams()
params.Set("BucketName", name)
params.Set("ProjectId", region.client.projectId)
params.Set("Type", aclType)
return region.client.DoAction("UpdateBucket", params, nil)
}
func (region *SRegion) GetIBuckets() ([]cloudprovider.ICloudBucket, error) {
iBuckets, err := region.client.getIBuckets()
if err != nil {
return nil, errors.Wrap(err, "getIBuckets")
}
ret := make([]cloudprovider.ICloudBucket, 0)
for i := range iBuckets {
if iBuckets[i].GetLocation() != region.GetId() {
continue
}
ret = append(ret, iBuckets[i])
}
return ret, nil
}
func (region *SRegion) CreateIBucket(name string, storageClassStr string, aclStr string) error {
if aclStr != "private" && aclStr != "public" {
return errors.Error("invalid acl")
}
return region.CreateBucket(name, aclStr)
}
func (region *SRegion) DeleteIBucket(name string) error {
err := region.DeleteBucket(name)
if err != nil {
if strings.Index(err.Error(), "bucket not found") >= 0 {
return nil
}
return errors.Wrap(err, "region.DeleteBucket")
}
return nil
}
func (region *SRegion) IBucketExist(name string) (bool, error) {
parts, err := region.client.listBuckets(name, 0, 1)
if err != nil {
return false, errors.Wrap(err, "region.listBuckets")
}
if len(parts) == 0 {
return false, cloudprovider.ErrNotFound
}
return true, nil
}
func (region *SRegion) GetIBucketById(bucketId string) (cloudprovider.ICloudBucket, error) {
return cloudprovider.GetIBucketById(region, bucketId)
}
func (region *SRegion) GetIBucketByName(name string) (cloudprovider.ICloudBucket, error) {
return region.GetIBucketByName(name)
}
func (region *SRegion) GetCapabilities() []string {
return region.client.GetCapabilities()
}
func (region *SRegion) GetIVMs() ([]cloudprovider.ICloudVM, error) {
vms, err := region.GetInstances("", "")
if err != nil {
return nil, err
}
ret := []cloudprovider.ICloudVM{}
for i := range vms {
ret = append(ret, &vms[i])
}
return ret, nil
}
-389
View File
@@ -1,389 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
/*
UCLOUD 项目:https://docs.ucloud.cn/management_monitor/uproject/projects
项目可认为是云账户下承载资源的容器,当您注册一个UCloud云账户后,系统会默认创建一个项目,您属于的资源都落在此项目下。如您有新的业务要使用云服务,可创建一个新项目,并将新业务部署在新项目下,实现业务之间的网络与逻辑隔离。
1、项目之间默认网络与逻辑隔离,即项目A的主机无法绑定项目B的EIP,默认也无法与项目B的主机内网通信。但联通项目后,uhost、udb、umem可实现内网通信。
2、资源不能在项目间迁移,即项目A内的主机无法迁移至项目B,因其不在一个基础网络内,且逻辑上也是隔离的。但诸如自主镜像等静态资源,您可以提交工单申请迁移至其他项目。
3、只有云账户本身,才能删除项目,且必须是项目被没有资源、没有任何子成员、未与其他项目联通的情况下才可删除。
UCloud DiskType貌似也是一个奇葩的存在
// https://docs.ucloud.cn/api/uhost-api/disk_type
1.在主机创建查询接口中 DISK type 对应 CLOUD_SSD|CLOUD_NORMAL|...
2.在数据盘创建中对应 DataDisk|SSDDataDisk
3.在数据盘查询接口请求中对应 DataDisk|SystemDisk 。在结果中对应DataDisk|SSDDataDisk|SSDSystemDisk|SystemDisk
目前存在的问题:
1.很多国外区域都需要单独申请开通权限才能使用。onecloud有可能调度到未开通权限区域导致失败。
*/
const (
CLOUD_PROVIDER_ROCKBASE = api.CLOUD_PROVIDER_ROCKBASE
CLOUD_PROVIDER_ROCKBASE_CN = "RockBase"
ROCKBASE_DEFAULT_REGION = "hk"
ROCKBASE_API_VERSION = "2019-02-28"
)
type RockbaseClientConfig struct {
cpcfg cloudprovider.ProviderConfig
accessKeyId string
accessKeySecret string
projectId string
debug bool
}
func NewRockbaseClientConfig(accessKeyId, accessKeySecret string) *RockbaseClientConfig {
cfg := &RockbaseClientConfig{
accessKeyId: accessKeyId,
accessKeySecret: accessKeySecret,
}
return cfg
}
func (cfg *RockbaseClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *RockbaseClientConfig {
cfg.cpcfg = cpcfg
return cfg
}
func (cfg *RockbaseClientConfig) ProjectId(projectId string) *RockbaseClientConfig {
cfg.projectId = projectId
return cfg
}
func (cfg *RockbaseClientConfig) Debug(debug bool) *RockbaseClientConfig {
cfg.debug = debug
return cfg
}
type SRockbaseClient struct {
*RockbaseClientConfig
iregions []cloudprovider.ICloudRegion
iBuckets []cloudprovider.ICloudBucket
httpClient *http.Client
}
// 进行资源操作时参数account 对应数据库cloudprovider表中的account字段,由accessKey和projectID两部分组成,通过"/"分割。
// 初次导入Subaccount时,参数account对应cloudaccounts表中的account字段,即accesskey。此时projectID为空,只能进行同步子账号(项目)、查询region列表等projectId无关的操作。
func NewRockbaseClient(cfg *RockbaseClientConfig) (*SRockbaseClient, error) {
httpClient := cfg.cpcfg.AdaptiveTimeoutHttpClient()
ts, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response) error, error) {
if cfg.cpcfg.ReadOnly {
if req.ContentLength > 0 {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, errors.Wrapf(err, "ioutil.ReadAll")
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
obj, err := jsonutils.Parse(body)
if err != nil {
return nil, errors.Wrapf(err, "Parse request body")
}
action, err := obj.GetString("Action")
if err != nil {
return nil, errors.Wrapf(err, "Get request action")
}
for _, prefix := range []string{"Get", "Describe", "List"} {
if strings.HasPrefix(action, prefix) {
return nil, nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
}
return nil, nil
})
client := SRockbaseClient{
RockbaseClientConfig: cfg,
httpClient: httpClient,
}
err := client.fetchRegions()
if err != nil {
return nil, err
}
err = client.fetchBuckets()
if err != nil {
return nil, err
}
return &client, nil
}
func (self *SRockbaseClient) 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 *SRockbaseClient) commonParams(params SParams) SParams {
if len(self.projectId) > 0 {
params.Set("ProjectId", self.projectId)
}
params.Set("PublicKey", self.accessKeyId)
return params
}
func (self *SRockbaseClient) DoListAll(action string, params SParams, result interface{}) error {
params = self.commonParams(params)
return DoListAll(self, action, params, result)
}
func (self *SRockbaseClient) DoListPart(action string, limit int, offset int, params SParams, result interface{}) (int, int, error) {
params = self.commonParams(params)
params.SetPagination(limit, offset)
return doListPart(self, action, params, result)
}
func (self *SRockbaseClient) DoAction(action string, params SParams, result interface{}) error {
params = self.commonParams(params)
err := DoAction(self, action, params, result)
if err != nil {
return err
}
return nil
}
func (self *SRockbaseClient) fetchRegions() error {
params := NewRockbaseParams()
sregions := make([]SRegion, 0)
err := self.DoListAll("ListRegions", params, &sregions)
if err != nil {
return err
}
self.iregions = make([]cloudprovider.ICloudRegion, len(sregions))
for i := range sregions {
sregions[i].client = self
self.iregions[i] = &sregions[i]
}
return nil
}
func (client *SRockbaseClient) invalidateIBuckets() {
client.iBuckets = nil
}
func (client *SRockbaseClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) {
if client.iBuckets == nil {
err := client.fetchBuckets()
if err != nil {
return nil, errors.Wrap(err, "fetchBuckets")
}
}
return client.iBuckets, nil
}
func (client *SRockbaseClient) fetchBuckets() error {
buckets := make([]SBucket, 0)
offset := 0
limit := 50
for {
parts, err := client.listBuckets("", offset, limit)
if err != nil {
return errors.Wrap(err, "client.listBuckets")
}
if len(parts) > 0 {
buckets = append(buckets, parts...)
}
if len(parts) < limit {
break
} else {
offset += limit
}
}
ret := make([]cloudprovider.ICloudBucket, 0)
for i := range buckets {
region, err := client.getIRegionByRegionId(buckets[i].Region)
if err != nil {
log.Errorf("fail to find iregion %s", buckets[i].Region)
continue
}
buckets[i].region = region.(*SRegion)
ret = append(ret, &buckets[i])
}
client.iBuckets = ret
return nil
}
func (self *SRockbaseClient) 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 *SRockbaseClient) 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.Id = project.ProjectId
subAccount.Name = fmt.Sprintf("%s-%s", self.cpcfg.Name, project.ProjectName)
// ucloud账号ID中可能包含/。因此使用::作为分割符号
subAccount.Account = fmt.Sprintf("%s::%s", self.accessKeyId, project.ProjectId)
subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
subAccounts = append(subAccounts, subAccount)
}
return subAccounts, nil
}
func (self *SRockbaseClient) GetAccountId() string {
return "" // no account ID found for ucloud
}
func (self *SRockbaseClient) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
return self.iregions, nil
}
func removeDigit(idstr string) string {
for len(idstr) > 0 && idstr[len(idstr)-1] >= '0' && idstr[len(idstr)-1] <= '9' {
idstr = idstr[:len(idstr)-1]
}
return idstr
}
func (self *SRockbaseClient) getIRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) {
for i := 0; i < len(self.iregions); i += 1 {
if self.iregions[i].GetId() == id {
return self.iregions[i], nil
}
}
// retry
for i := 0; i < len(self.iregions); i += 1 {
rid := removeDigit(self.iregions[i].GetId())
rid2 := removeDigit(id)
if rid == rid2 {
return self.iregions[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRockbaseClient) 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 *SRockbaseClient) GetRegion(regionId string) *SRegion {
if len(regionId) == 0 {
regionId = ROCKBASE_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 *SRockbaseClient) 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 errors.Cause(err) != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRockbaseClient) 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 errors.Cause(err) != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRockbaseClient) 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 errors.Cause(err) != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRockbaseClient) GetCapabilities() []string {
caps := []string{
// cloudprovider.CLOUD_CAPABILITY_PROJECT,
cloudprovider.CLOUD_CAPABILITY_COMPUTE,
cloudprovider.CLOUD_CAPABILITY_NETWORK,
cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP,
cloudprovider.CLOUD_CAPABILITY_EIP,
// cloudprovider.CLOUD_CAPABILITY_LOADBALANCER,
// cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE,
// cloudprovider.CLOUD_CAPABILITY_RDS,
// cloudprovider.CLOUD_CAPABILITY_CACHE,
// cloudprovider.CLOUD_CAPABILITY_EVENT,
}
return caps
}
-99
View File
@@ -1,99 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"strings"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/pkg/util/secrules"
)
type SecurityGroupRule struct {
secgroup *SSecurityGroup
DstPort string `json:"DstPort"`
Priority string `json:"Priority"`
ProtocolType string `json:"ProtocolType"`
RuleAction string `json:"RuleAction"`
SrcIP string `json:"SrcIP"`
Remark string
}
func (self SecurityGroupRule) String() string {
return fmt.Sprintf("%s|%s|%s|%s|%s|%s", self.ProtocolType, self.DstPort, self.SrcIP, self.RuleAction, self.Priority, self.Remark)
}
func (self *SecurityGroupRule) GetGlobalId() string {
return fmt.Sprintf("%s|%s|%s|%s|%s", self.Priority, self.RuleAction, self.SrcIP, self.DstPort, self.ProtocolType)
}
func (self *SecurityGroupRule) GetAction() secrules.TSecurityRuleAction {
if self.RuleAction == "ACCEPT" {
return secrules.SecurityRuleAllow
}
return secrules.SecurityRuleDeny
}
func (self *SecurityGroupRule) GetDescription() string {
return self.Remark
}
func (self *SecurityGroupRule) GetDirection() secrules.TSecurityRuleDirection {
return secrules.DIR_IN
}
func (self *SecurityGroupRule) GetCIDRs() []string {
return []string{self.SrcIP}
}
func (self *SecurityGroupRule) GetProtocol() string {
return strings.ToLower(self.ProtocolType)
}
func (self *SecurityGroupRule) GetPorts() string {
return self.DstPort
}
func (self *SecurityGroupRule) GetPriority() int {
switch self.Priority {
case "HIGH":
return 1
case "MEDIUM":
return 2
case "LOW":
return 3
}
return 1
}
func (self *SecurityGroupRule) Delete() error {
params := NewRockbaseParams()
params.Set("FWId", self.secgroup.FWId)
idx := 0
for _, rule := range self.secgroup.Rule {
if rule.GetGlobalId() == self.GetGlobalId() {
continue
}
params.Set(fmt.Sprintf("Rule.%d", idx), rule.String())
idx++
}
return self.secgroup.region.DoAction("UpdateFirewall", params, nil)
}
func (self *SecurityGroupRule) Update(opts *cloudprovider.SecurityGroupRuleUpdateOptions) error {
return cloudprovider.ErrNotImplemented
}
-214
View File
@@ -1,214 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/secrules"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://docs.ucloud.cn/api/unet-api/describe_firewall
type SSecurityGroup struct {
multicloud.SSecurityGroup
RockbaseTags
region *SRegion
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 []SecurityGroupRule `json:"Rule"`
Tag string `json:"Tag"`
Type string `json:"Type"`
}
func (self *SSecurityGroup) GetProjectId() string {
return self.region.client.projectId
}
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 api.SECGROUP_STATUS_READY
}
func (self *SSecurityGroup) Refresh() error {
group, err := self.region.GetSecurityGroup(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, group)
}
func (self *SSecurityGroup) GetDescription() string {
return self.Remark
}
func (self *SSecurityGroup) GetRules() ([]cloudprovider.ISecurityGroupRule, error) {
ret := []cloudprovider.ISecurityGroupRule{}
for i := range self.Rule {
self.Rule[i].secgroup = self
ret = append(ret, &self.Rule[i])
}
return ret, nil
}
func (self *SSecurityGroup) GetVpcId() string {
return ""
}
func (self *SRegion) GetSecurityGroup(secGroupId string) (*SSecurityGroup, error) {
secgroups, err := self.GetSecurityGroups(secGroupId, "", "")
if err != nil {
return nil, err
}
for i := range secgroups {
secgroups[i].region = self
if secgroups[i].GetGlobalId() == secGroupId {
return &secgroups[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", secGroupId)
}
// https://docs.ucloud.cn/api/unet-api/create_firewall
func (self *SRegion) CreateSecurityGroup(name, description string) (string, error) {
params := NewRockbaseParams()
params.Set("Name", name)
params.Set("Remark", description)
rules := []string{"TCP|22|0.0.0.0/0|ACCEPT|LOW", "TCP|3389|0.0.0.0/0|ACCEPT|LOW", "ICMP||0.0.0.0/0|ACCEPT|LOW"}
for i, rule := range rules {
params.Set(fmt.Sprintf("Rule.%d", i), rule)
}
type Firewall struct {
FWId string
}
firewall := Firewall{}
err := self.DoAction("CreateFirewall", params, &firewall)
if err != nil {
return "", err
}
return firewall.FWId, nil
}
func (self *SSecurityGroup) CreateRule(opts *cloudprovider.SecurityGroupRuleCreateOptions) (cloudprovider.ISecurityGroupRule, error) {
params := NewRockbaseParams()
params.Set("FWId", self.FWId)
idx := 0
for _, rule := range self.Rule {
params.Set(fmt.Sprintf("Rule.%d", idx), rule.String())
idx++
}
if len(opts.Ports) == 0 {
opts.Ports = "1-65535"
}
if opts.Protocol == secrules.PROTO_ICMP || opts.Protocol == "gre" {
opts.Ports = ""
}
if opts.Protocol == secrules.PROTO_ANY {
return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "protocol any")
}
action := "DROP"
if opts.Action == secrules.SecurityRuleAllow {
action = "ACCEPT"
}
priority := "LOW"
if opts.Priority == 1 {
priority = "HIGH"
} else if opts.Priority == 2 {
priority = "MEDIUM"
}
rule := fmt.Sprintf("%s|%s|%s|%s|%s|%s", strings.ToUpper(opts.Protocol), opts.Ports, opts.CIDR, action, priority, opts.Desc)
params.Set(fmt.Sprintf("Rule.%d", idx), rule)
err := self.region.DoAction("UpdateFirewall", params, nil)
if err != nil {
return nil, err
}
err = self.Refresh()
if err != nil {
return nil, err
}
for i := range self.Rule {
if self.Rule[i].String() == rule {
self.Rule[i].secgroup = self
return &self.Rule[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created")
}
func (self *SRegion) GetSecurityGroups(secGroupId string, resourceId string, name string) ([]SSecurityGroup, error) {
secgroups := make([]SSecurityGroup, 0)
params := NewRockbaseParams()
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 {
if strings.Contains(err.Error(), "not exist") {
return nil, cloudprovider.ErrNotFound
}
return nil, err
}
result := []SSecurityGroup{}
for i := range secgroups {
if len(name) == 0 || secgroups[i].Name == name {
secgroups[i].region = self
result = append(result, secgroups[i])
}
}
return result, nil
}
func (self *SSecurityGroup) Delete() error {
return self.region.DeleteSecurityGroup(self.FWId)
}
-207
View File
@@ -1,207 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://docs.ucloud.cn/api/udisk-api/describe_udisk_snapshot
type SSnapshot struct {
multicloud.SResourceBase
RockbaseTags
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 api.SNAPSHOT_READY
case "Failed":
return api.SNAPSHOT_FAILED
case "Creating":
return api.SNAPSHOT_CREATING
default:
return api.SNAPSHOT_UNKNOWN
}
}
func (self *SSnapshot) Refresh() error {
disk, err := self.region.GetDisk(self.UDiskId)
if err != nil {
return err
}
snapshot, err := self.region.GetSnapshotById(disk.Zone, 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) GetSizeMb() int32 {
return self.SizeGB * 1024
}
func (self *SSnapshot) GetDiskId() string {
return self.UDiskId
}
// 磁盘类型,0:数据盘,1:系统盘
func (self *SSnapshot) GetDiskType() string {
if self.DiskType == 1 {
return api.DISK_TYPE_SYS
} else {
return api.DISK_TYPE_DATA
}
}
// https://docs.ucloud.cn/api/udisk-api/delete_udisk_snapshot
func (self *SSnapshot) Delete() error {
zoneId := ""
idisk, err := self.region.GetDisk(self.UDiskId)
if err == nil {
zoneId = idisk.Zone
} else if errors.Cause(err) == cloudprovider.ErrNotFound {
zones, err := self.region.GetIZones()
if err != nil {
return errors.Wrap(err, "snapshot.Delete GetIZones")
}
for _, zone := range zones {
if _, err := self.region.GetSnapshotById(zone.GetId(), self.GetId()); err == nil {
zoneId = zone.GetId()
break
}
}
} else {
return errors.Wrap(err, "snapshot.Delete")
}
if len(zoneId) == 0 {
return fmt.Errorf("snapshot.Delete can not found snapshot %s zone id", self.GetId())
}
return self.region.DeleteSnapshot(self.GetId(), zoneId)
}
func (self *SRegion) GetSnapshotById(zoneId string, snapshotId string) (SSnapshot, error) {
snapshots, err := self.GetSnapshots(zoneId, "", 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(zoneId string, diskId string, snapshotId string) ([]SSnapshot, error) {
params := NewRockbaseParams()
if len(diskId) > 0 {
disk, err := self.GetDisk(diskId)
if err != nil {
return nil, err
}
params.Set("UDiskId", diskId)
params.Set("Zone", disk.Zone)
}
if len(zoneId) > 0 {
params.Set("Zone", zoneId)
}
if len(snapshotId) > 0 {
params.Set("SnapshotId", snapshotId)
}
snapshots := make([]SSnapshot, 0)
err := self.DoAction("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
for i := range snapshots {
snapshots[i].region = self
}
return snapshots, nil
}
// https://docs.ucloud.cn/api/udisk-api/delete_udisk_snapshot
func (self *SRegion) DeleteSnapshot(snapshotId string, zoneId string) error {
params := NewRockbaseParams()
params.Set("SnapshotId", snapshotId)
params.Set("Zone", zoneId)
return self.DoAction("DeleteUDiskSnapshot", params, nil)
}
-157
View File
@@ -1,157 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"strings"
"yunion.io/x/jsonutils"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
"yunion.io/x/pkg/utils"
)
type SStorage struct {
multicloud.SStorageBase
RockbaseTags
zone *SZone
storageType string
}
func (self *SStorage) GetId() string {
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId(), self.storageType)
}
func (self *SStorage) GetName() string {
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.cpcfg.Name, self.zone.GetId(), self.storageType)
}
func (self *SStorage) GetGlobalId() string {
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetGlobalId(), self.storageType)
}
func (self *SStorage) GetStatus() string {
return api.STORAGE_ONLINE
}
func (self *SStorage) Refresh() error {
return nil
}
func (self *SStorage) IsEmulated() bool {
return true
}
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) {
ret := []cloudprovider.ICloudDisk{}
for _, isBoot := range []string{"True", "False"} {
disks, err := self.zone.region.GetDisks(self.zone.GetId(), self.storageType, isBoot, "")
if err != nil {
return nil, err
}
for _, disk := range disks {
disk.storage = self
ret = append(ret, &disk)
}
}
return ret, nil
}
func (self *SStorage) GetStorageType() string {
return self.storageType
}
func (self *SStorage) GetMediumType() string {
if strings.Contains(self.storageType, "SSD") {
return api.DISK_TYPE_SSD
} else {
return api.DISK_TYPE_ROTATE
}
}
func (self *SStorage) GetCapacityMB() int64 {
return 0 // unlimited
}
func (self *SStorage) GetCapacityUsedMB() int64 {
return 0
}
func (self *SStorage) GetStorageConf() jsonutils.JSONObject {
return jsonutils.NewDict()
}
func (self *SStorage) GetEnabled() bool {
return true
}
func (self *SStorage) CreateIDisk(opts *cloudprovider.DiskCreateConfig) (cloudprovider.ICloudDisk, error) {
diskType := "DataDisk"
switch self.storageType {
case api.STORAGE_ROCKBASE_CLOUD_SSD:
diskType = "SSDDataDisk"
case api.STORAGE_ROCKBASE_CLOUD_ESSD:
diskType = "ESSDDataDisk"
case api.STORAGE_ROCKBASE_CLOUD_RSSD:
diskType = "RSSDDataDisk"
}
diskId, err := self.zone.region.CreateDisk(self.zone.GetId(), diskType, opts)
if err != nil {
return nil, err
}
disk, err := self.zone.region.GetDisk(diskId)
if err != nil {
return nil, err
}
disk.storage = self
return disk, nil
}
func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) {
disk, err := self.zone.region.GetDisk(idStr)
if err != nil {
return nil, err
}
disk.storage = self
return disk, nil
}
func (self *SStorage) GetMountPoint() string {
return ""
}
func (self *SStorage) IsSysDiskStore() bool {
return true
}
func (self *SStorage) DisableSync() bool {
return utils.IsInStringArray(self.storageType, []string{
api.STORAGE_ROCKBASE_LOCAL_SSD,
api.STORAGE_ROCKBASE_LOCAL_NORMAL,
})
}
-374
View File
@@ -1,374 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"context"
"fmt"
"math"
"strings"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/qemuimgfmt"
"yunion.io/x/pkg/utils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SStoragecache struct {
multicloud.SResourceBase
RockbaseTags
region *SRegion
}
func GetBucketName(regionId string, imageId string) string {
return fmt.Sprintf("imgcache-%s-%s", strings.ToLower(regionId), strings.ToLower(imageId))
}
func (self *SStoragecache) GetId() string {
return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetId())
}
func (self *SStoragecache) GetName() string {
return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Name, self.region.GetId())
}
func (self *SStoragecache) GetGlobalId() string {
return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, 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) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) {
images, err := self.region.GetImages("Custom", "")
if err != nil {
return nil, errors.Wrapf(err, "GetImages")
}
ret := []cloudprovider.ICloudImage{}
for i := range images {
images[i].storageCache = self
ret = append(ret, &images[i])
}
return ret, nil
}
func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) {
image, err := self.region.GetImage(extId)
if err != nil {
return nil, err
}
image.storageCache = self
return &image, nil
}
func (self *SStoragecache) GetPath() string {
return ""
}
// https://docs.ucloud.cn/api/uhost-api/import_custom_image
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
return self.uploadImage(ctx, image, callback)
}
func (self *SStoragecache) uploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
if len(image.OsVersion) == 0 {
return "", fmt.Errorf("uploadImage os version is empty")
}
bucketName := GetBucketName(self.region.GetId(), image.ImageId)
// create bucket
exist, err := self.region.IBucketExist(bucketName)
if err != nil {
return "", errors.Wrap(err, "self.region.IBucketExist")
}
if !exist {
err = self.region.CreateBucket(bucketName, "private")
if err != nil {
return "", errors.Wrap(err, "CreateBucket")
}
}
defer func() {
e := self.region.DeleteBucket(bucketName)
if e != nil {
log.Errorf("uploadImage delete bucket %s", e.Error())
}
}()
reader, size, err := image.GetReader(image.ImageId, string(qemuimgfmt.VMDK))
if err != nil {
return "", errors.Wrapf(err, "GetReader")
}
minDiskGB := int64(math.Ceil(float64(image.MinDiskMb) / 1024))
if minDiskGB < 40 {
minDiskGB = 40
} else if minDiskGB > 1024 {
minDiskGB = 1024
}
md5 := image.Checksum
diskFormat := string(qemuimgfmt.QCOW2)
// upload to ucloud
bucket, err := self.region.GetIBucketById(bucketName)
if err != nil {
return "", errors.Wrap(err, "GetIBucketByName")
}
file := SFile{
bucket: bucket.(*SBucket),
file: multicloud.NewProgress(size, 98, reader, callback),
Size: size,
FileName: image.ImageId,
Hash: md5,
}
err = file.Upload()
if err != nil {
return "", err
}
defer func() {
e := file.Delete()
if e != nil {
log.Errorf("uploadImage delete object %s", e.Error())
}
}() // remove object
// check image name, avoid name conflict
imageBaseName := image.ImageId
if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' {
imageBaseName = fmt.Sprintf("img%s", image.ImageId)
}
imageName := imageBaseName
nameIdx := 1
for {
_, err = self.region.GetImageByName(imageName)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
break
} else {
return "", err
}
}
imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx)
nameIdx += 1
log.Debugf("uploadImage Match remote name %s", imageName)
}
imgId, err := self.region.ImportImage(imageName, file.FetchFileUrl(), image.OsDistribution, image.OsVersion, diskFormat)
if err != nil {
log.Errorf("ImportImage error %s %s", file.FetchFileUrl(), err)
return "", err
}
// timeout: 1hour = 3600 seconds
err = cloudprovider.WaitCreated(60*time.Second, 3600*time.Second, func() bool {
image, err := self.region.GetImage(imgId)
if err == nil && image.State == "Available" {
return true
}
return false
})
if err != nil {
return "", errors.Wrap(err, "UploadImage")
}
if callback != nil {
callback(100)
}
return imgId, err
}
/*func (self *SRegion) GetBucketDomain(name string) (string, error) {
params := NewRockbaseParams()
params.Set("BucketName", name)
res := make([]SBucket, 0)
err := self.client.DoListAll("DescribeBucket", params, &res)
if err != nil {
return "", err
}
if len(res) == 1 && len(res[0].Domain.Src) >= 1 {
return res[0].Domain.Src[0], nil
} else {
return "", fmt.Errorf("GetBucketDomain failed. %v", res)
}
}*/
// https://docs.ucloud.cn/api/ufile-api/create_bucket
func (self *SRegion) CreateBucket(name, bucketType string) error {
params := NewRockbaseParams()
params.Set("BucketName", name)
params.Set("Type", bucketType)
err := self.DoAction("CreateBucket", params, nil)
if err != nil {
return err
}
self.client.invalidateIBuckets()
return nil
}
// https://docs.ucloud.cn/api/ufile-api/delete_bucket
func (self *SRegion) DeleteBucket(name string) error {
params := NewRockbaseParams()
params.Set("BucketName", name)
err := self.DoAction("DeleteBucket", params, nil)
if err != nil {
return err
}
self.client.invalidateIBuckets()
return nil
}
// https://docs.ucloud.cn/api/ufile-api/put_file
func (self *SRegion) uploadObj(name string) error {
params := NewRockbaseParams()
params.Set("BucketName", name)
return self.DoAction("PutFile", params, nil)
}
func (self *SRegion) GetImageByName(name string) (*SImage, error) {
images, err := self.GetImages("Custom", "")
if err != nil {
return nil, err
}
for i := range images {
if images[i].GetName() == name {
return &images[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func normalizeOsType(osType string) string {
switch strings.ToLower(osType) {
case "centos":
return "CentOS"
case "ubuntu":
return "Ubuntu"
case "windows":
return "Windows"
case "redHat":
return "RedHat"
case "debian":
return "Debian"
default:
return "Other"
}
}
func normalizeOsName(normalizeOsType string, fullVersion string) string {
if utils.IsInStringArray(normalizeOsType, []string{"CentOS", "RedHat", "Debian"}) {
if len(fullVersion) >= 3 {
return fmt.Sprintf("%s %s 64位", normalizeOsType, fullVersion[0:3])
} else {
return fmt.Sprintf("%s %s.0 64位", normalizeOsType, fullVersion[0:1])
}
}
if normalizeOsType == "Ubuntu" {
if len(fullVersion) >= 5 {
return fmt.Sprintf("Ubuntu %s 64位", fullVersion[0:5])
} else if len(fullVersion) >= 2 {
return fmt.Sprintf("Ubuntu %s.04 64位", fullVersion[0:2])
} else {
// 默认猜一个?
return "Ubuntu 14.04 64位"
}
}
if normalizeOsType == "Windows" {
if len(fullVersion) >= 4 {
return fmt.Sprintf("Windows %s 64位", fullVersion[0:4])
} else {
// 默认猜一个?
return "Windows 2016 64位"
}
}
return "Other"
}
func normalizeDiskFormat(diskFormat string) (string, error) {
switch strings.ToLower(diskFormat) {
case "raw":
return "RAW", nil
case "vhd":
return "VHD", nil
case "vmdk":
return "VMDK", nil
case "qcow2":
return "qcow2", nil
default:
return "", fmt.Errorf("unsupported image format %s", diskFormat)
}
}
// https://docs.ucloud.cn/api/uhost-api/import_custom_image
func (self *SRegion) ImportImage(name string, ufileUrl string, osType string, osVersion string, diskFormat string) (string, error) {
format, err := normalizeDiskFormat(diskFormat)
if err != nil {
return "", err
}
nOsType := normalizeOsType(osType)
params := NewRockbaseParams()
params.Set("ImageName", name)
params.Set("UFileUrl", ufileUrl)
// 操作系统平台,比如CentOS、Ubuntu、Windows、RedHat等,请参考控制台的镜像版本;若导入控制台上没有的操作系统,参数为Other
params.Set("OsType", nOsType)
// 操作系统详细版本,请参考控制台的镜像版本;OsType为Other时,输入参数为Other
params.Set("OsName", normalizeOsName(nOsType, osVersion))
params.Set("Format", format)
params.Set("Auth", "True")
params.Set("ImageDescription", osVersion)
type SImageId struct {
ImageId string
}
ret := SImageId{}
log.Debugf("ImportImage with params %s", params.String())
err = self.DoAction("ImportCustomImage", params, &ret)
if err != nil {
return "", err
}
return ret.ImageId, nil
}
-36
View File
@@ -1,36 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
type RockbaseTags struct {
}
func (self *RockbaseTags) GetTags() (map[string]string, error) {
return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetTags")
}
func (self *RockbaseTags) GetSysTags() map[string]string {
return nil
}
func (self *RockbaseTags) SetTags(tags map[string]string, replace bool) error {
return errors.Wrap(cloudprovider.ErrNotImplemented, "SetTags")
}
-394
View File
@@ -1,394 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/httputils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SBucket struct {
multicloud.SBaseBucket
RockbaseTags
region *SRegion
// projectId string
Domain Domain `json:"Domain"`
BucketId string `json:"BucketId"`
Region string `json:"Region"`
CreateTime int64 `json:"CreateTime"`
Biz string `json:"Biz"`
BucketName string `json:"BucketName"`
ModifyTime int64 `json:"ModifyTime"`
Type string `json:"Type"`
Tag string `json:"Tag"`
HasUserDomain int64 `json:"HasUserDomain"`
CDNDomainId []string `json:"CdnDomainId"`
}
type Domain struct {
Src []string `json:"Src"`
CDN []string `json:"Cdn"`
CustomCDN []interface{} `json:"CustomCdn"`
CustomSrc []interface{} `json:"CustomSrc"`
}
type SFile struct {
bucket *SBucket
BucketName string `json:"BucketName"`
FileName string `json:"FileName"`
Size int64 `json:"Size"`
Hash string `json:"Hash"`
MimeType string `json:"MimeType"`
CreateTime int64 `json:"CreateTime"`
ModifyTime int64 `json:"ModifyTime"`
StorageClass string `json:"StorageClass"`
file io.Reader
}
func (client *SRockbaseClient) signHeader(httpMethod string, path string, md5 string) string {
contentType := ""
if httpMethod == http.MethodPut {
contentType = "application/octet-stream"
}
data := httpMethod + "\n"
data += md5 + "\n"
data += contentType + "\n"
data += "\n"
data += path
log.Debugf("sign %s", data)
h := hmac.New(sha1.New, []byte(client.accessKeySecret))
h.Write([]byte(data))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func (self *SFile) signHeader(httpMethod string) string {
return self.bucket.region.client.signHeader(httpMethod, "/"+self.bucket.BucketName+"/"+self.FileName, self.Hash)
}
func (self *SFile) auth(httpMethod string) string {
return "UCloud" + " " + self.bucket.region.client.accessKeyId + ":" + self.signHeader(httpMethod)
}
func (self *SFile) GetHost() string {
return self.bucket.Domain.Src[0]
}
func (self *SFile) GetUrl() string {
return fmt.Sprintf("http://%s/%s", self.GetHost(), self.FileName)
}
// https://github.com/ufilesdk-dev/ufile-gosdk/blob/master/auth.go
func (self *SFile) FetchFileUrl() string {
expired := strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)
// sign
data := "GET\n\n\n" + expired + "\n"
data += "/" + self.bucket.BucketName + "/" + self.FileName
h := hmac.New(sha1.New, []byte(self.bucket.region.client.accessKeySecret))
h.Write([]byte(data))
sign := base64.StdEncoding.EncodeToString(h.Sum(nil))
urlEncoder := url.Values{}
urlEncoder.Add("UCloudPublicKey", self.bucket.region.client.accessKeyId)
urlEncoder.Add("Signature", sign)
urlEncoder.Add("Expires", expired)
querys := urlEncoder.Encode()
return fmt.Sprintf("%s?%s", self.GetUrl(), querys)
}
func (self *SFile) Upload() error {
req, _ := http.NewRequest(http.MethodPut, self.GetUrl(), self.file)
req.Header.Add("Authorization", self.auth(http.MethodPut))
req.Header.Add("Content-MD5", self.Hash)
req.Header.Add("Content-Type", "application/octet-stream")
req.Header.Add("Content-Length", strconv.FormatInt(self.Size, 10))
_, err := doRequest(req)
return err
}
func (self *SFile) Delete() error {
req, _ := http.NewRequest(http.MethodDelete, self.GetUrl(), nil)
req.Header.Add("Authorization", self.auth(http.MethodDelete))
_, err := doRequest(req)
return err
}
func (self *SFile) GetIBucket() cloudprovider.ICloudBucket {
return self.bucket
}
func (self *SFile) GetKey() string {
return self.FileName
}
func (self *SFile) GetSizeBytes() int64 {
return self.Size
}
func (self *SFile) GetLastModified() time.Time {
return time.Unix(self.ModifyTime, 0)
}
func (self *SFile) GetStorageClass() string {
return self.StorageClass
}
func (self *SFile) GetETag() string {
return self.Hash
}
func (self *SFile) GetContentType() string {
return self.MimeType
}
func (self *SFile) GetAcl() cloudprovider.TBucketACLType {
return self.bucket.GetAcl()
}
func (self *SFile) SetAcl(cloudprovider.TBucketACLType) error {
return nil
}
func (self *SFile) GetMeta() http.Header {
return nil
}
func (self *SFile) SetMeta(ctx context.Context, meta http.Header) error {
return cloudprovider.ErrNotSupported
}
func doRequest(req *http.Request) (jsonutils.JSONObject, error) {
// ufile request use no timeout client so as to download/upload large files
res, err := httputils.GetAdaptiveTimeoutClient().Do(req)
if err != nil {
return nil, errors.Wrap(err, "httpclient Do")
}
_, body, err := httputils.ParseJSONResponse("", res, err, false)
if err != nil {
return nil, errors.Wrap(err, "ParseJSONResponse")
}
return body, nil
}
type sPrefixFileListOutput struct {
BucketName string
BucketId string
NextMarker string
DataSet []SFile
}
func (b *SBucket) doPrefixFileList(prefix string, marker string, limit int) (*sPrefixFileListOutput, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(""), "list")
if len(prefix) > 0 {
params.Add(jsonutils.NewString(prefix), "prefix")
}
if len(marker) > 0 {
params.Add(jsonutils.NewString(marker), "marker")
}
if limit > 0 {
params.Add(jsonutils.NewInt(int64(limit)), "limit")
}
host := fmt.Sprintf("https://%s.ufile.ucloud.cn", b.BucketName)
path := fmt.Sprintf("/?%s", params.QueryString())
log.Debugf("Request %s%s", host, path)
req, _ := http.NewRequest(http.MethodGet, host+path, nil)
sign := b.region.client.signHeader(http.MethodGet, path, "")
auth := "UCloud" + " " + b.region.client.accessKeyId + ":" + sign
req.Header.Add("Authorization", auth)
output := sPrefixFileListOutput{}
body, err := doRequest(req)
if err != nil {
return nil, errors.Wrap(err, "doRequest")
}
err = body.Unmarshal(&output)
if err != nil {
return nil, errors.Wrap(err, "body.Unmarshal")
}
return &output, nil
}
func (b *SBucket) GetProjectId() string {
return b.region.client.projectId
}
func (b *SBucket) GetGlobalId() string {
return b.BucketId
}
func (b *SBucket) GetName() string {
return b.BucketName
}
func (b *SBucket) GetLocation() string {
return b.region.GetId()
}
func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
return b.region
}
func (b *SBucket) GetCreatedAt() time.Time {
return time.Unix(b.CreateTime, 0)
}
func (b *SBucket) GetStorageClass() string {
return ""
}
func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
switch b.Type {
case "public":
return cloudprovider.ACLPublicRead
default:
return cloudprovider.ACLPrivate
}
}
func (b *SBucket) SetAcl(aclStr cloudprovider.TBucketACLType) error {
aclType := "private"
if aclStr == cloudprovider.ACLPublicRead || aclStr == cloudprovider.ACLPublicReadWrite {
aclType = "public"
}
return b.region.updateBucket(b.BucketName, aclType)
}
func (b *SBucket) getSrcUrl() string {
if len(b.Domain.Src) > 0 {
return b.Domain.Src[0]
}
return ""
}
func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
ret := make([]cloudprovider.SBucketAccessUrl, 0)
for i, u := range b.Domain.Src {
primary := false
if i == 0 {
primary = true
}
ret = append(ret, cloudprovider.SBucketAccessUrl{
Url: u,
Description: fmt.Sprintf("src%d", i),
Primary: primary,
})
}
for i, u := range b.Domain.CDN {
ret = append(ret, cloudprovider.SBucketAccessUrl{
Url: u,
Description: fmt.Sprintf("cdn%d", i),
})
}
return ret
}
func (b *SBucket) GetStats() cloudprovider.SBucketStats {
stats, _ := cloudprovider.GetIBucketStats(b)
return stats
}
func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
result := cloudprovider.SListObjectResult{}
output, err := b.doPrefixFileList(prefix, marker, maxCount)
if err != nil {
return result, errors.Wrap(err, "b.doPrefixFileList")
}
if len(output.NextMarker) > 0 {
result.NextMarker = output.NextMarker
result.IsTruncated = true
}
result.Objects = make([]cloudprovider.ICloudObject, len(output.DataSet))
for i := range output.DataSet {
result.Objects[i] = &output.DataSet[i]
}
return result, nil
}
func (b *SBucket) PutObject(ctx context.Context, key string, input io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
return cloudprovider.ErrNotSupported
}
func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
return "", cloudprovider.ErrNotSupported
}
func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, input io.Reader, partSize int64, offset, totalSize int64) (string, error) {
return "", cloudprovider.ErrNotSupported
}
func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
return cloudprovider.ErrNotSupported
}
func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
return cloudprovider.ErrNotSupported
}
func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
file := SFile{
bucket: b,
FileName: key,
}
return file.Delete()
}
func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
return "", cloudprovider.ErrNotSupported
}
func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
return cloudprovider.ErrNotSupported
}
func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
return nil, cloudprovider.ErrNotSupported
}
func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partIndex int, srcBucketName string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
return "", cloudprovider.ErrNotSupported
}
-182
View File
@@ -1,182 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"reflect"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
)
var responseMetaKeys = map[string]bool{
"RetCode": true,
"Message": true,
"Action": true,
"TotalCount": true,
}
func detectResultKey(resp jsonutils.JSONObject, result interface{}) string {
if result == nil {
return ""
}
rt := reflect.TypeOf(result)
if rt.Kind() != reflect.Ptr {
return ""
}
et := rt.Elem()
switch et.Kind() {
case reflect.Slice:
if et.Elem().Kind() == reflect.String {
return detectStringResultKey(resp)
}
return detectArrayResultKey(resp)
case reflect.String:
return detectStringResultKey(resp)
default:
return ""
}
}
func detectStringResultKey(resp jsonutils.JSONObject) string {
dict, ok := resp.(*jsonutils.JSONDict)
if !ok {
return ""
}
for _, key := range dict.SortedKeys() {
if responseMetaKeys[key] {
continue
}
if strings.HasSuffix(key, "Id") || strings.HasSuffix(key, "Ids") {
return key
}
}
return ""
}
func detectArrayResultKey(resp jsonutils.JSONObject) string {
dict, ok := resp.(*jsonutils.JSONDict)
if !ok {
return ""
}
candidates := []string{}
for _, key := range dict.SortedKeys() {
if responseMetaKeys[key] {
continue
}
val, _ := dict.Get(key)
if val == nil {
continue
}
if _, ok := val.(*jsonutils.JSONArray); ok {
candidates = append(candidates, key)
}
}
if len(candidates) == 0 {
return ""
}
if len(candidates) == 1 {
return candidates[0]
}
for _, key := range candidates {
if strings.HasSuffix(key, "Set") {
return key
}
}
return candidates[0]
}
func unmarshalResult(resp jsonutils.JSONObject, result interface{}) error {
if result == nil {
return nil
}
var err error
resultKey := detectResultKey(resp, result)
if len(resultKey) > 0 {
err = resp.Unmarshal(result, resultKey)
} else {
err = resp.Unmarshal(result)
}
if err != nil {
return errors.Wrapf(err, "unmarshalResult")
}
return nil
}
func doListPart(client *SRockbaseClient, action string, params SParams, result interface{}) (int, int, error) {
params.SetAction(action)
ret, err := jsonRequest(client, params)
if err != nil {
return 0, 0, err
}
total, _ := ret.Int("TotalCount")
resultKey := detectResultKey(ret, result)
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 *SRockbaseClient, action string, params SParams, result interface{}) error {
params.SetAction(action)
resp, err := jsonRequest(client, params)
if err != nil {
log.Debugf("DoAction %s %s error %s", action, params.PrettyString(), err)
return err
}
return unmarshalResult(resp, result)
}
// 遍历所有结果
func DoListAll(client *SRockbaseClient, action string, params SParams, 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, result)
if err != nil {
return err
}
// total 大于零的情况下通过total字段判断列表是否遍历完成。total不存在或者为0的情况下,通过返回列表的长度判断是否遍历完成
if (total > 0 && resultValue.Len() >= total) || (total == 0 && pageLimit > part) {
break
}
params.SetPagination(pageLimit, offset+resultValue.Len())
}
return nil
}
-119
View File
@@ -1,119 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SVipAddr struct {
VIP string
SubnetId string
}
func (ip *SVipAddr) GetIP() string {
return ip.VIP
}
func (ip *SVipAddr) GetINetworkId() string {
return ip.SubnetId
}
func (ip *SVipAddr) IsPrimary() bool {
return true
}
func (ip *SVipAddr) GetGlobalId() string {
return ip.VIP
}
type SVip struct {
multicloud.SNetworkInterfaceBase
RockbaseTags
region *SRegion
CreateTime int64
Name string
RealIp string
Remark string
SubnetId string
Tag string
VIP string
VIPId string
VPCId string
Zone string
}
func (vip *SVip) GetName() string {
if len(vip.Name) > 0 {
return vip.Name
}
return vip.VIPId
}
func (vip *SVip) GetId() string {
return vip.VIPId
}
func (vip *SVip) GetGlobalId() string {
return vip.VIPId
}
func (vip *SVip) GetMacAddress() string {
ip, _ := netutils.NewIPV4Addr(vip.VIP)
return ip.ToMac("00:16:")
}
func (vip *SVip) GetAssociateType() string {
return ""
}
func (vip *SVip) GetAssociateId() string {
return ""
}
func (vip *SVip) GetStatus() string {
return api.NETWORK_INTERFACE_STATUS_AVAILABLE
}
func (vip *SVip) GetICloudInterfaceAddresses() ([]cloudprovider.ICloudInterfaceAddress, error) {
ip := &SVipAddr{VIP: vip.VIP, SubnetId: vip.SubnetId}
return []cloudprovider.ICloudInterfaceAddress{ip}, nil
}
func (region *SRegion) GetINetworkInterfaces() ([]cloudprovider.ICloudNetworkInterface, error) {
vips, err := region.GetVips()
if err != nil {
return nil, errors.Wrap(err, "region.GetVips")
}
ret := []cloudprovider.ICloudNetworkInterface{}
for i := 0; i < len(vips); i++ {
vips[i].region = region
ret = append(ret, &vips[i])
}
return ret, nil
}
func (self *SRegion) GetVips() ([]SVip, error) {
vips := []SVip{}
params := NewRockbaseParams()
err := self.DoListAll("DescribeVIP", params, &vips)
return vips, err
}
-173
View File
@@ -1,173 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SVPC struct {
multicloud.SVpc
RockbaseTags
region *SRegion
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) 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 api.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) 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) {
return []cloudprovider.ICloudWire{&SWire{vpc: self}}, nil
}
// 由于Ucloud 安全组和vpc没有直接关联,这里是返回同一个项目下的防火墙列表,会导致重复同步的问题。
// https://docs.ucloud.cn/api/unet-api/grant_firewall
func (self *SVPC) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
return []cloudprovider.ICloudSecurityGroup{}, nil
}
func (self *SVPC) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) {
rts := []cloudprovider.ICloudRouteTable{}
return rts, nil
}
func (self *SVPC) GetIRouteTableById(routeTableId string) (cloudprovider.ICloudRouteTable, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SVPC) Delete() error {
return self.region.DeleteVpc(self.GetId())
}
func (self *SVPC) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) {
wires, err := self.GetIWires()
if err != nil {
return nil, err
}
for i := 0; i < len(wires); i += 1 {
if wires[i].GetGlobalId() == wireId {
return wires[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetVpc(vpcId string) (*SVPC, error) {
if len(vpcId) == 0 {
return nil, errors.Wrap(cloudprovider.ErrNotFound, "empty vpc id")
}
vpcs, err := self.GetVpcs(vpcId)
if err != nil {
return nil, errors.Wrap(err, "GetVpcs")
}
for i := range vpcs {
if vpcs[i].VpcId == vpcId {
vpcs[i].region = self
return &vpcs[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", vpcId)
}
// https://docs.ucloud.cn/api/vpc2.0-api/delete_vpc
func (self *SRegion) DeleteVpc(vpcId string) error {
params := NewRockbaseParams()
params.Set("VPCId", vpcId)
return self.DoAction("DeleteVPC", params, nil)
}
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090625.html
func (self *SRegion) GetVpcs(vpcId string) ([]SVPC, error) {
vpcs := make([]SVPC, 0)
params := NewRockbaseParams()
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 := NewRockbaseParams()
if len(vpcId) > 0 {
params.Set("VPCId", vpcId)
}
networks := make([]SNetwork, 0)
err := self.DoAction("DescribeSubnet", params, &networks)
return networks, err
}
-97
View File
@@ -1,97 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
// 子网在整个region可用
type SWire struct {
multicloud.SResourceBase
RockbaseTags
vpc *SVPC
}
func (self *SWire) GetId() string {
return fmt.Sprintf("%s-%s", self.vpc.GetId(), self.vpc.region.GetId())
}
func (self *SWire) GetName() string {
return self.GetId()
}
func (self *SWire) GetGlobalId() string {
return fmt.Sprintf("%s-%s", self.vpc.GetGlobalId(), self.vpc.region.GetGlobalId())
}
func (self *SWire) GetStatus() string {
return api.WIRE_STATUS_AVAILABLE
}
func (self *SWire) Refresh() error {
return nil
}
func (self *SWire) IsEmulated() bool {
return true
}
func (self *SWire) GetIVpc() cloudprovider.ICloudVpc {
return self.vpc
}
func (self *SWire) GetIZone() cloudprovider.ICloudZone {
return nil
}
func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) {
networks, err := self.vpc.region.GetNetworks(self.vpc.GetId())
if err != nil {
return nil, err
}
ret := []cloudprovider.ICloudNetwork{}
for i := range networks {
networks[i].wire = self
ret = append(ret, &networks[i])
}
return ret, 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
}
// https://docs.ucloud.cn/api/vpc2.0-api/create_subnet
func (self *SWire) CreateINetwork(opts *cloudprovider.SNetworkCreateOptions) (cloudprovider.ICloudNetwork, error) {
return self.vpc.region.CreateNetwork(self.vpc.GetId(), opts.Name, opts.Cidr, opts.Desc)
}
-188
View File
@@ -1,188 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rockbase
import (
"fmt"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
type SZone struct {
multicloud.SResourceBase
RockbaseTags
region *SRegion
host *SHost
istorages []cloudprovider.ICloudStorage
storageTypes []string
RegionId string `json:"Region"`
ZoneId string `json:"Zone"`
LocalName string `json:"LocalName"`
Default bool `json:"Default"`
Permission string `json:"Permission"`
}
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) fetchStorages() error {
storageTypes, err := self.region.GetZoneStorageTypes(self.GetId())
if err != nil {
return err
}
for _, storageType := range []string{api.STORAGE_ROCKBASE_LOCAL_SSD, api.STORAGE_ROCKBASE_LOCAL_NORMAL} {
if !utils.IsInStringArray(storageType, storageTypes) {
storageTypes = append(storageTypes, storageType)
}
}
self.storageTypes = storageTypes
self.istorages = make([]cloudprovider.ICloudStorage, len(self.storageTypes))
for i, sc := range self.storageTypes {
self.istorages[i] = &SStorage{zone: self, storageType: sc}
}
return nil
}
func (self *SZone) GetId() string {
return self.ZoneId
}
func (self *SZone) GetName() string {
return self.LocalName
}
func (self *SZone) GetI18n() cloudprovider.SModelI18nTable {
name := self.GetName()
table := cloudprovider.SModelI18nTable{}
table["name"] = cloudprovider.NewSModelI18nEntry(name).CN(name).EN(name)
return table
}
func (self *SZone) GetGlobalId() string {
return fmt.Sprintf("%s/%s", self.region.GetGlobalId(), self.GetId())
}
func (self *SZone) GetStatus() string {
return api.ZONE_ENABLE
}
func (self *SZone) Refresh() error {
return nil
}
func (self *SZone) IsEmulated() bool {
return false
}
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 {
err := self.fetchStorages()
if err != nil {
return nil, errors.Wrapf(err, "fetchStorages")
}
}
return self.istorages, nil
}
func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
if self.istorages == nil {
err := self.fetchStorages()
if err != nil {
return nil, errors.Wrapf(err, "fetchStorages")
}
}
for i := range self.istorages {
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) {
vpcs, err := self.region.GetVpcs("")
if err != nil {
return nil, err
}
ret := []cloudprovider.ICloudWire{}
for i := range vpcs {
vpcs[i].region = self.region
ret = append(ret, &SWire{vpc: &vpcs[i]})
}
return ret, 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 := NewRockbaseParams()
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
}
func (self *SZone) getStorageByCategory(category string) (*SStorage, error) {
storages, err := self.GetIStorages()
if err != nil {
return nil, err
}
for i := 0; i < len(storages); i += 1 {
storage := storages[i].(*SStorage)
if storage.storageType == category {
return storage, nil
}
}
return nil, fmt.Errorf("No such storage %s", category)
}