mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
fix(region): aws dedicated host sync (#25040)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -216,6 +216,7 @@ var (
|
||||
},
|
||||
CLOUD_PROVIDER_AWS: {
|
||||
HOST_TYPE_AWS,
|
||||
HOST_TYPE_DEDICATED,
|
||||
},
|
||||
CLOUD_PROVIDER_HUAWEI: {
|
||||
HOST_TYPE_HUAWEI,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+19
-1
@@ -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
|
||||
}
|
||||
Vendored
+1
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, 云主机;natgw:NAT网关;ulb:负载均衡器;upm: 物理机; hadoophost: 大数据集群;fortresshost:堡垒机;udockhost:容器;udhost:私有专区主机;vpngw:IPSec VPN;ucdr:云灾备;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
@@ -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(¶ms, "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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
// 国际: Internation,BGP: 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
@@ -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
@@ -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
@@ -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(¶ms, 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(¶ms, "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(¶ms, resourceIds)
|
||||
setLabelsParams(¶ms, "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(¶ms, resourceIds)
|
||||
setLabelsParams(¶ms, "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
|
||||
}
|
||||
-54
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
-194
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user