fix(region): support rockbasecloud (#24978)

This commit is contained in:
屈轩
2026-06-05 19:28:19 +08:00
committed by GitHub
parent 3ac9178c61
commit e0584f12e3
73 changed files with 7449 additions and 131 deletions

View File

@@ -41,6 +41,7 @@ func init() {
cmd.CreateWithKeyword("create-hcs", &options.SHCSAccountCreateOptions{})
cmd.CreateWithKeyword("create-hcsop", &options.SHcsOpCloudAccountCreateOptions{})
cmd.CreateWithKeyword("create-ucloud", &options.SUcloudCloudAccountCreateOptions{})
cmd.CreateWithKeyword("create-rockbase", &options.SRockbaseAccountCreateOptions{})
cmd.CreateWithKeyword("create-volcengine", &options.SVolcengineCloudAccountCreateOptions{})
cmd.CreateWithKeyword("create-zstack", &options.SZStackCloudAccountCreateOptions{})
cmd.CreateWithKeyword("create-s3", &options.SS3CloudAccountCreateOptions{})
@@ -76,6 +77,7 @@ func init() {
cmd.UpdateWithKeyword("update-hcso", &options.SHCSOAccountUpdateOptions{})
cmd.UpdateWithKeyword("update-hcs", &options.SHCSAccountUpdateOptions{})
cmd.UpdateWithKeyword("update-ucloud", &options.SUcloudCloudAccountUpdateOptions{})
cmd.UpdateWithKeyword("update-rockbase", &options.SRockbaseAccountUpdateOptions{})
cmd.UpdateWithKeyword("update-volcengine", &options.SVolcengineCloudAccountUpdateOptions{})
cmd.UpdateWithKeyword("update-zstack", &options.SZStackCloudAccountUpdateOptions{})
cmd.UpdateWithKeyword("update-s3", &options.SS3CloudAccountUpdateOptions{})
@@ -107,6 +109,7 @@ func init() {
cmd.PerformWithKeyword("update-credential-hcso", "update-credential", &options.SHCSOAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("update-credential-hcs", "update-credential", &options.SHCSOAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("update-credential-ucloud", "update-credential", &options.SUcloudCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("update-credential-rockbase", "update-credential", &options.SRockbaseAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("update-credential-volcengine", "update-credential", &options.SVolcengineCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("update-credential-zstack", "update-credential", &options.SZStackCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("update-credential-s3", "update-credential", &options.SS3CloudAccountUpdateCredentialOptions{})
@@ -132,6 +135,7 @@ func init() {
cmd.PerformWithKeyword("test-connectivity-openstack", "test-connectivity", &options.SOpenStackCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("test-connectivity-huawei", "test-connectivity", &options.SHuaweiCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("test-connectivity-ucloud", "test-connectivity", &options.SUcloudCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("test-connectivity-rockbase", "test-connectivity", &options.SRockbaseAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("test-connectivity-volcengine", "test-connectivity", &options.SVolcengineCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("test-connectivity-zstack", "test-connectivity", &options.SZStackCloudAccountUpdateCredentialOptions{})
cmd.PerformWithKeyword("test-connectivity-s3", "test-connectivity", &options.SS3CloudAccountUpdateCredentialOptions{})

2
go.mod
View File

@@ -108,7 +108,7 @@ require (
k8s.io/cri-api v0.28.15
k8s.io/klog/v2 v2.90.1
moul.io/http2curl/v2 v2.3.0
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260512084559-539f78289e9a
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260605102752-5312ff154585
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

4
go.sum
View File

@@ -1785,8 +1785,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.20260512084559-539f78289e9a h1:cFTQgCSRc8X0yPvlELifa5zZkcEO7X7RXA002TNrV4E=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260512084559-539f78289e9a/go.mod h1:TbMuTWxcTdyL2Usn+WQdIw0jdlBYO66SzPRY8LZ3Wj0=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260605102752-5312ff154585 h1:4JQZa+RLkT8qut0U4Fa15hhktj01V9q/cqyDST/xqeA=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260605102752-5312ff154585/go.mod h1:TbMuTWxcTdyL2Usn+WQdIw0jdlBYO66SzPRY8LZ3Wj0=
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=

View File

@@ -78,6 +78,7 @@ const (
CLOUD_PROVIDER_CLOUDFLARE = compute.CLOUD_PROVIDER_CLOUDFLARE
CLOUD_PROVIDER_CNWARE = compute.CLOUD_PROVIDER_CNWARE
CLOUD_PROVIDER_OCEANBASE = compute.CLOUD_PROVIDER_OCEANBASE
CLOUD_PROVIDER_ROCKBASE = compute.CLOUD_PROVIDER_ROCKBASE
CLOUD_PROVIDER_GENERICS3 = compute.CLOUD_PROVIDER_GENERICS3
CLOUD_PROVIDER_CEPH = compute.CLOUD_PROVIDER_CEPH
@@ -147,6 +148,7 @@ var (
CLOUD_PROVIDER_VOLCENGINE,
CLOUD_PROVIDER_ORACLE,
CLOUD_PROVIDER_CLOUDFLARE,
CLOUD_PROVIDER_ROCKBASE,
}
CLOUD_PROVIDERS = []string{
@@ -188,6 +190,7 @@ var (
CLOUD_PROVIDER_CLOUDFLARE,
CLOUD_PROVIDER_CNWARE,
CLOUD_PROVIDER_OCEANBASE,
CLOUD_PROVIDER_ROCKBASE,
}
CLOUD_PROVIDER_HOST_TYPE_MAP = map[string][]string{
@@ -301,6 +304,9 @@ var (
CLOUD_PROVIDER_CNWARE: {
HOST_TYPE_CNWARE,
},
CLOUD_PROVIDER_ROCKBASE: {
HOST_TYPE_ROCKBASE,
},
}
)

View File

@@ -230,6 +230,7 @@ const (
HYPERVISOR_UIS = compute.HYPERVISOR_UIS
HYPERVISOR_CAS = compute.HYPERVISOR_CAS
HYPERVISOR_CNWARE = compute.HYPERVISOR_CNWARE
HYPERVISOR_ROCKBASE = compute.HYPERVISOR_ROCKBASE
// HYPERVISOR_DEFAULT = HYPERVISOR_KVM
HYPERVISOR_DEFAULT = HYPERVISOR_KVM
@@ -319,6 +320,7 @@ var HYPERVISORS = []string{
HYPERVISOR_ZETTAKIT,
HYPERVISOR_UIS,
HYPERVISOR_CNWARE,
HYPERVISOR_ROCKBASE,
}
const (

View File

@@ -61,6 +61,7 @@ const (
HOST_TYPE_UIS = compute.HOST_TYPE_UIS
HOST_TYPE_CAS = compute.HOST_TYPE_CAS
HOST_TYPE_CNWARE = compute.HOST_TYPE_CNWARE
HOST_TYPE_ROCKBASE = compute.HOST_TYPE_ROCKBASE
HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR
@@ -167,6 +168,7 @@ var HOST_TYPES = []string{
HOST_TYPE_ZETTAKIT,
HOST_TYPE_UIS,
HOST_TYPE_CNWARE,
HOST_TYPE_ROCKBASE,
}
var ALL_NIC_TYPES = []compute.TNicType{NIC_TYPE_IPMI, NIC_TYPE_ADMIN, NIC_TYPE_NORMAL}

View File

@@ -79,6 +79,7 @@ var (
CLOUD_PROVIDER_HCS,
CLOUD_PROVIDER_CTYUN,
CLOUD_PROVIDER_UCLOUD,
CLOUD_PROVIDER_ROCKBASE,
CLOUD_PROVIDER_GOOGLE,
CLOUD_PROVIDER_OPENSTACK,
CLOUD_PROVIDER_JDCLOUD,

View File

@@ -95,6 +95,13 @@ const (
STORAGE_UCLOUD_LOCAL_SSD = compute.STORAGE_UCLOUD_LOCAL_SSD // SSD本地盘
STORAGE_UCLOUD_EXCLUSIVE_LOCAL_DISK = compute.STORAGE_UCLOUD_EXCLUSIVE_LOCAL_DISK // 独享本地盘
// RockBase storage type
STORAGE_ROCKBASE_CLOUD_NORMAL = compute.STORAGE_ROCKBASE_CLOUD_NORMAL // 普通云盘
STORAGE_ROCKBASE_CLOUD_SSD = compute.STORAGE_ROCKBASE_CLOUD_SSD // SSD云盘
STORAGE_ROCKBASE_LOCAL_NORMAL = compute.STORAGE_ROCKBASE_LOCAL_NORMAL // 普通本地盘
STORAGE_ROCKBASE_LOCAL_SSD = compute.STORAGE_ROCKBASE_LOCAL_SSD // SSD本地盘
STORAGE_ROCKBASE_EXCLUSIVE_LOCAL_DISK = compute.STORAGE_ROCKBASE_EXCLUSIVE_LOCAL_DISK // 独享本地盘
// VolcEngine storage types
STORAGE_VOLC_CLOUD_FLEXPL = compute.STORAGE_VOLCENGINE_FlexPL // 极速型SSD云盘, FlexPL规格
STORAGE_VOLC_CLOUD_PL0 = compute.STORAGE_VOLCENGINE_PL0 //极速型SSD云盘, PL0规格
@@ -184,6 +191,7 @@ var (
STORAGE_LOCAL_TYPES = []string{
STORAGE_LOCAL, STORAGE_NVME_PT, STORAGE_NVME, STORAGE_LVM,
STORAGE_BAREMETAL, STORAGE_UCLOUD_LOCAL_NORMAL, STORAGE_UCLOUD_LOCAL_SSD, STORAGE_UCLOUD_EXCLUSIVE_LOCAL_DISK,
STORAGE_ROCKBASE_LOCAL_NORMAL, STORAGE_ROCKBASE_LOCAL_SSD, STORAGE_ROCKBASE_EXCLUSIVE_LOCAL_DISK,
STORAGE_EPHEMERAL_SSD, STORAGE_LOCAL_BASIC, STORAGE_LOCAL_SSD, STORAGE_LOCAL_PRO, STORAGE_OPENSTACK_NOVA,
STORAGE_ZSTACK_LOCAL_STORAGE, STORAGE_GOOGLE_LOCAL_SSD,
}
@@ -203,6 +211,8 @@ var (
STORAGE_HUAWEI_SSD, STORAGE_HUAWEI_SAS, STORAGE_HUAWEI_SATA,
STORAGE_OPENSTACK_ISCSI, STORAGE_UCLOUD_CLOUD_NORMAL, STORAGE_UCLOUD_CLOUD_SSD,
STORAGE_UCLOUD_LOCAL_NORMAL, STORAGE_UCLOUD_LOCAL_SSD, STORAGE_UCLOUD_EXCLUSIVE_LOCAL_DISK,
STORAGE_ROCKBASE_CLOUD_NORMAL, STORAGE_ROCKBASE_CLOUD_SSD,
STORAGE_ROCKBASE_LOCAL_NORMAL, STORAGE_ROCKBASE_LOCAL_SSD, STORAGE_ROCKBASE_EXCLUSIVE_LOCAL_DISK,
STORAGE_ZSTACK_LOCAL_STORAGE, STORAGE_ZSTACK_CEPH, STORAGE_ZSTACK_LOCAL_STORAGE, STORAGE_GPFS, STORAGE_CIFS,
STORAGE_NVME_PT, STORAGE_NVME, STORAGE_LVM, STORAGE_CLVM, STORAGE_SLVM,
}

View File

@@ -0,0 +1,31 @@
// 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 providerdriver
import (
api "yunion.io/x/onecloud/pkg/apis/compute"
)
type RockbaseCollect struct {
SBaseCollectDriver
}
func (self *RockbaseCollect) GetProvider() string {
return api.CLOUD_PROVIDER_ROCKBASE
}
func init() {
Register(&RockbaseCollect{})
}

View File

@@ -0,0 +1,136 @@
// 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 guestdrivers
import (
"context"
"fmt"
"strings"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/rbacscope"
"yunion.io/x/pkg/utils"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SRockbaseGuestDriver struct {
SManagedVirtualizedGuestDriver
}
func (self *SRockbaseGuestDriver) GetHypervisor() string {
return api.HYPERVISOR_ROCKBASE
}
func (self *SRockbaseGuestDriver) GetProvider() string {
return api.CLOUD_PROVIDER_ROCKBASE
}
func (self *SRockbaseGuestDriver) GetComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
keys := models.SComputeResourceKeys{}
keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
keys.CloudEnv = api.CLOUD_ENV_PUBLIC_CLOUD
keys.Provider = api.CLOUD_PROVIDER_ROCKBASE
keys.Brand = api.CLOUD_PROVIDER_ROCKBASE
keys.Hypervisor = api.HYPERVISOR_ROCKBASE
return keys
}
func (self *SRockbaseGuestDriver) GetDefaultSysDiskBackend() string {
return api.STORAGE_ROCKBASE_CLOUD_SSD
}
func (self *SRockbaseGuestDriver) GetDeployStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SRockbaseGuestDriver) GetMinimalSysDiskSizeGb() int {
return 10
}
func (self *SRockbaseGuestDriver) GetGuestInitialStateAfterCreate() string {
return api.VM_RUNNING
}
func (self *SRockbaseGuestDriver) GetDetachDiskStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SRockbaseGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SRockbaseGuestDriver) IsChangeInstanceTypeWhileRunningSupported(guest *models.SGuest) (bool, error) {
return false, nil
}
func (self *SRockbaseGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SRockbaseGuestDriver) GetGuestInitialStateAfterRebuild() string {
return api.VM_RUNNING
}
func (self *SRockbaseGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_START_RESIZE_DISK, api.VM_RESIZE_DISK}) {
return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
}
if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_ROCKBASE_CLOUD_SSD, api.STORAGE_ROCKBASE_CLOUD_NORMAL}) {
return fmt.Errorf("Cannot resize disk with unsupported volumes type %s", storage.StorageType)
}
return nil
}
func (self *SRockbaseGuestDriver) GetInstanceCapability() cloudprovider.SInstanceCapability {
return cloudprovider.SInstanceCapability{
Hypervisor: self.GetHypervisor(),
Provider: self.GetProvider(),
DefaultAccount: cloudprovider.SDefaultAccount{
Linux: cloudprovider.SOsDefaultAccount{
DefaultAccount: api.VM_DEFAULT_LINUX_LOGIN_USER,
},
Windows: cloudprovider.SOsDefaultAccount{
DefaultAccount: api.VM_DEFAULT_WINDOWS_LOGIN_USER,
},
},
}
}
func init() {
driver := SRockbaseGuestDriver{}
models.RegisterGuestDriver(&driver)
}
func (rockbase *SRockbaseGuestDriver) ValidateGuestChangeConfigInput(ctx context.Context, guest *models.SGuest, input api.ServerChangeConfigInput) (*api.ServerChangeConfigSettings, error) {
confs, err := rockbase.SBaseGuestDriver.ValidateGuestChangeConfigInput(ctx, guest, input)
if err != nil {
return nil, errors.Wrap(err, "SBaseGuestDriver.ValidateGuestChangeConfigInput")
}
if len(input.InstanceType) > 0 {
if !strings.HasPrefix(guest.InstanceType, confs.InstanceTypeFamily) {
return nil, httperrors.NewInputParameterError("Cannot change config with different instance family")
}
}
return confs, nil
}

View File

@@ -55,7 +55,7 @@ func (self *SManagedVirtualizationHostDriver) CheckAndSetCacheImage(ctx context.
}
providerName := storageCache.GetProviderName()
if utils.IsInStringArray(providerName, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS, api.CLOUD_PROVIDER_UCLOUD}) {
if utils.IsInStringArray(providerName, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS, api.CLOUD_PROVIDER_UCLOUD, api.CLOUD_PROVIDER_ROCKBASE}) {
image.OsVersion = input.OsFullVersion
}

View File

@@ -0,0 +1,84 @@
// 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 hostdrivers
import (
"context"
"fmt"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SRockbaseHostDriver struct {
SManagedVirtualizationHostDriver
}
func init() {
driver := SRockbaseHostDriver{}
models.RegisterHostDriver(&driver)
}
func (self *SRockbaseHostDriver) GetHostType() string {
return api.HOST_TYPE_ROCKBASE
}
func (self *SRockbaseHostDriver) GetHypervisor() string {
return api.HYPERVISOR_ROCKBASE
}
func (self *SRockbaseHostDriver) GetProvider() string {
return api.CLOUD_PROVIDER_ROCKBASE
}
func (self *SRockbaseHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
if storage.StorageType == api.STORAGE_ROCKBASE_CLOUD_NORMAL {
if sizeGb < 20 || sizeGb > 8000 {
return fmt.Errorf("The %s disk size must be in the range of 20G ~ 8000GB", storage.StorageType)
}
} else if storage.StorageType == api.STORAGE_ROCKBASE_CLOUD_SSD {
if sizeGb < 20 || sizeGb > 4000 {
return fmt.Errorf("The %s disk size must be in the range of 20G ~ 4000GB", storage.StorageType)
}
} else if storage.StorageType == api.STORAGE_ROCKBASE_LOCAL_SSD {
if sizeGb < 20 || sizeGb > 1000 {
return fmt.Errorf("The %s disk size must be in the range of 20G ~ 1000GB", storage.StorageType)
}
return fmt.Errorf("Not support create/resize %s disk", storage.StorageType)
} else if storage.StorageType == api.STORAGE_ROCKBASE_LOCAL_NORMAL {
if sizeGb < 20 || sizeGb > 2000 {
return fmt.Errorf("The %s disk size must be in the range of 20G ~ 2000GB", storage.StorageType)
}
return fmt.Errorf("Not support create/resize %s disk", storage.StorageType)
} else {
return fmt.Errorf("Not support create %s disk", storage.StorageType)
}
return nil
}
func (self *SRockbaseHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, input *api.DiskResetInput) (*api.DiskResetInput, error) {
if len(guests) > 0 {
return nil, httperrors.NewInputParameterError("RockBase reset disk operation required disk not be attached")
}
if disk.DiskType != api.DISK_TYPE_DATA {
return nil, httperrors.NewInputParameterError("RockBase only support data disk reset operation")
}
return input, nil
}

View File

@@ -161,6 +161,7 @@ var HypervisorBrandMap = map[string]string{
computeapis.HYPERVISOR_HUAWEI: computeapis.CLOUD_PROVIDER_HUAWEI,
computeapis.HYPERVISOR_OPENSTACK: computeapis.CLOUD_PROVIDER_OPENSTACK,
computeapis.HYPERVISOR_UCLOUD: computeapis.CLOUD_PROVIDER_UCLOUD,
computeapis.HYPERVISOR_ROCKBASE: computeapis.CLOUD_PROVIDER_ROCKBASE,
computeapis.HYPERVISOR_ZSTACK: computeapis.CLOUD_PROVIDER_ZSTACK,
computeapis.HYPERVISOR_GOOGLE: computeapis.CLOUD_PROVIDER_GOOGLE,
computeapis.HYPERVISOR_CTYUN: computeapis.CLOUD_PROVIDER_CTYUN,
@@ -177,6 +178,7 @@ var BrandHypervisorMap = map[string]string{
computeapis.CLOUD_PROVIDER_HUAWEI: computeapis.HYPERVISOR_HUAWEI,
computeapis.CLOUD_PROVIDER_OPENSTACK: computeapis.HYPERVISOR_OPENSTACK,
computeapis.CLOUD_PROVIDER_UCLOUD: computeapis.HYPERVISOR_UCLOUD,
computeapis.CLOUD_PROVIDER_ROCKBASE: computeapis.HYPERVISOR_ROCKBASE,
computeapis.CLOUD_PROVIDER_ZSTACK: computeapis.HYPERVISOR_ZSTACK,
computeapis.CLOUD_PROVIDER_GOOGLE: computeapis.HYPERVISOR_GOOGLE,
computeapis.CLOUD_PROVIDER_CTYUN: computeapis.HYPERVISOR_CTYUN,

View File

@@ -0,0 +1,96 @@
// 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 regiondrivers
import (
"context"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/secrules"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SRockbaseRegionDriver struct {
SManagedVirtualizationRegionDriver
}
func init() {
driver := SRockbaseRegionDriver{}
models.RegisterRegionDriver(&driver)
}
func (self *SRockbaseRegionDriver) GetProvider() string {
return api.CLOUD_PROVIDER_ROCKBASE
}
func (self *SRockbaseRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, input api.VpcCreateInput) (api.VpcCreateInput, error) {
var cidrV = validators.NewIPv4PrefixValidator("cidr_block")
if err := cidrV.Validate(ctx, jsonutils.Marshal(input).(*jsonutils.JSONDict)); err != nil {
return input, err
}
err := IsInPrivateIpRange(cidrV.Value.ToIPRange())
if err != nil {
return input, err
}
if cidrV.Value.MaskLen > 29 {
return input, httperrors.NewInputParameterError("%s request the mask range should be less than or equal to 29", self.GetProvider())
}
return input, nil
}
func (self *SRockbaseRegionDriver) ValidateCreateSecurityGroupInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.SSecgroupCreateInput) (*api.SSecgroupCreateInput, error) {
for i := range input.Rules {
if input.Rules[i].Protocol == secrules.PROTO_ANY {
return nil, httperrors.NewNotSupportedError("protocol %s", input.Rules[i].Protocol)
}
if input.Rules[i].Priority == nil {
return nil, httperrors.NewMissingParameterError("priority")
}
if *input.Rules[i].Priority < 1 || *input.Rules[i].Priority > 3 {
return nil, httperrors.NewInputParameterError("invalid priority %d, range 1-3", *input.Rules[i].Priority)
}
}
return self.SManagedVirtualizationRegionDriver.ValidateCreateSecurityGroupInput(ctx, userCred, input)
}
func (self *SRockbaseRegionDriver) ValidateUpdateSecurityGroupRuleInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.SSecgroupRuleUpdateInput) (*api.SSecgroupRuleUpdateInput, error) {
if input.Priority != nil && (*input.Priority < 1 || *input.Priority > 3) {
return nil, httperrors.NewInputParameterError("invalid priority %d, range 1-3", *input.Priority)
}
if input.Protocol != nil && *input.Protocol == secrules.PROTO_ANY {
return nil, httperrors.NewNotSupportedError("protocol %s", *input.Protocol)
}
if input.Ports != nil && strings.Contains(*input.Ports, ",") {
return nil, httperrors.NewInputParameterError("invalid ports %s", *input.Ports)
}
return self.SManagedVirtualizationRegionDriver.ValidateUpdateSecurityGroupRuleInput(ctx, userCred, input)
}
func (self *SRockbaseRegionDriver) GetSecurityGroupFilter(vpc *models.SVpc) (func(q *sqlchemy.SQuery) *sqlchemy.SQuery, error) {
return func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("cloudregion_id", vpc.CloudregionId).Equals("manager_id", vpc.ManagerId)
}, nil
}

View File

@@ -245,7 +245,7 @@ type BaseListOptions struct {
Manager []string `help:"List objects belonging to the cloud provider" json:"manager,omitempty"`
Account string `help:"List objects belonging to the cloud account" json:"account,omitempty"`
Provider []string `help:"List objects from the provider" choices:"OneCloud|VMware|Aliyun|Apsara|Qcloud|Azure|Aws|Huawei|OpenStack|Ucloud|VolcEngine|ZStack|Google|Ctyun|Cloudpods|Nutanix|BingoCloud|IncloudSphere|JDcloud|Proxmox|Ceph|CephFS|Ecloud|HCSO|HCS|HCSOP|H3C|S3|RemoteFile|Ksyun|Baidu|QingCloud|OracleCloud|SangFor|ZettaKit|UIS|CNWare" json:"provider,omitempty"`
Provider []string `help:"List objects from the provider" choices:"OneCloud|VMware|Aliyun|Apsara|Qcloud|Azure|Aws|Huawei|OpenStack|Ucloud|RockBase|VolcEngine|ZStack|Google|Ctyun|Cloudpods|Nutanix|BingoCloud|IncloudSphere|JDcloud|Proxmox|Ceph|CephFS|Ecloud|HCSO|HCS|HCSOP|H3C|S3|RemoteFile|Ksyun|Baidu|QingCloud|OracleCloud|SangFor|ZettaKit|UIS|CNWare" json:"provider,omitempty"`
Brand []string `help:"List objects belonging to a special brand"`
CloudEnv string `help:"Cloud environment" choices:"public|private|onpremise|private_or_onpremise" json:"cloud_env,omitempty"`
PublicCloud *bool `help:"List objects belonging to public cloud" json:"public_cloud"`

View File

@@ -357,6 +357,17 @@ func (opts *SUcloudCloudAccountCreateOptions) Params() (jsonutils.JSONObject, er
return params, nil
}
type SRockbaseAccountCreateOptions struct {
SCloudAccountCreateBaseOptions
SAccessKeyCredential
}
func (opts *SRockbaseAccountCreateOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.Marshal(opts)
params.(*jsonutils.JSONDict).Add(jsonutils.NewString("RockBase"), "provider")
return params, nil
}
type SVolcengineCloudAccountCreateOptions struct {
SCloudAccountCreateBaseOptions
SAccessKeyCredential
@@ -602,6 +613,15 @@ func (opts *SUcloudCloudAccountUpdateCredentialOptions) Params() (jsonutils.JSON
return jsonutils.Marshal(opts), nil
}
type SRockbaseAccountUpdateCredentialOptions struct {
SCloudAccountIdOptions
SAccessKeyCredential
}
func (opts *SRockbaseAccountUpdateCredentialOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(opts), nil
}
type SVolcengineCloudAccountUpdateCredentialOptions struct {
SCloudAccountIdOptions
SUserPasswordCredential
@@ -1033,6 +1053,14 @@ func (opts *SUcloudCloudAccountUpdateOptions) Params() (jsonutils.JSONObject, er
return jsonutils.Marshal(opts), nil
}
type SRockbaseAccountUpdateOptions struct {
SCloudAccountUpdateBaseOptions
}
func (opts *SRockbaseAccountUpdateOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(opts), nil
}
type SVolcengineCloudAccountUpdateOptions struct {
SCloudAccountUpdateBaseOptions

4
vendor/modules.txt vendored
View File

@@ -2603,7 +2603,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.20260512084559-539f78289e9a
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260605102752-5312ff154585
## explicit; go 1.24
yunion.io/x/cloudmux/pkg/apis
yunion.io/x/cloudmux/pkg/apis/billing
@@ -2692,6 +2692,8 @@ 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

View File

@@ -52,6 +52,7 @@ const (
CLOUD_PROVIDER_CAS = "CAS"
CLOUD_PROVIDER_CNWARE = "CNware"
CLOUD_PROVIDER_OCEANBASE = "OceanBase"
CLOUD_PROVIDER_ROCKBASE = "RockBase"
CLOUD_PROVIDER_GENERICS3 = "S3"
CLOUD_PROVIDER_CEPH = "Ceph"

View File

@@ -124,6 +124,7 @@ const (
CITY_N_CALIFORNIA = "N. California" //北加州
CITY_OREGON = "Oregon" //俄勒冈州
CITY_LOS_ANGELES = "Los Angeles" //洛杉矶
CITY_DENVER = "Denver" //丹佛
CITY_SAN_FRANCISCO = "San Francisco" //旧金山
CITY_UTAH = "Utah" //犹他州
CITY_WASHINGTON = "Washington" //华盛顿
@@ -179,6 +180,7 @@ const (
// 波兰
CITY_WARSAW = "Warsaw" // 华沙
CITY_ISTANBUL = "Istanbul" // 伊斯坦布尔
// 巴西
CITY_SAO_PAULO = "Sao Paulo" //圣保罗
@@ -229,6 +231,9 @@ const (
// 新西兰
CITY_NEW_ZEALAND = "New Zealand" // 新西兰
// 菲律宾
CITY_MANILA = "Manila" // 马尼拉
COUNTRY_CODE_CN = "CN" //中国
COUNTRY_CODE_JP = "JP" //日本
COUNTRY_CODE_SG = "SG" //新加坡
@@ -265,4 +270,6 @@ const (
COUNTRY_CODE_ES = "ES" //西班牙
COUNTRY_CODE_QA = "QA" //卡塔尔
COUNTRY_CODE_NZ = "NZ" //新西兰
COUNTRY_CODE_PH = "PH" //菲律宾
COUNTRY_CODE_TR = "TR" //土耳其
)

View File

@@ -84,6 +84,7 @@ const (
HYPERVISOR_UIS = "uis"
HYPERVISOR_CAS = "cas"
HYPERVISOR_CNWARE = "cnware"
HYPERVISOR_ROCKBASE = "rockbase"
)
const (

View File

@@ -53,6 +53,7 @@ const (
HOST_TYPE_UIS = "uis"
HOST_TYPE_CAS = "cas"
HOST_TYPE_CNWARE = "cnware"
HOST_TYPE_ROCKBASE = "rockbase"
// # possible status
HOST_ONLINE = "online"

View File

@@ -318,8 +318,8 @@ var (
CountryCode: COUNTRY_CODE_JP,
}
RegionSeoul = cloudprovider.SGeographicInfo{
Latitude: 34.694,
Longitude: 135.502,
Latitude: 37.566,
Longitude: 126.978,
City: CITY_SEOUL,
CountryCode: COUNTRY_CODE_KR,
}
@@ -387,6 +387,18 @@ var (
City: CITY_BANGKOK,
CountryCode: COUNTRY_CODE_TH,
}
RegionManila = cloudprovider.SGeographicInfo{
Latitude: 14.599,
Longitude: 120.984,
City: CITY_MANILA,
CountryCode: COUNTRY_CODE_PH,
}
RegionHoChiMinh = cloudprovider.SGeographicInfo{
Latitude: 10.823,
Longitude: 106.630,
City: CITY_HO_CHI_MINH,
CountryCode: COUNTRY_CODE_VN,
}
RegionSydney = cloudprovider.SGeographicInfo{
Latitude: -33.869,
@@ -429,6 +441,12 @@ var (
City: CITY_JOHANNESBURG,
CountryCode: COUNTRY_CODE_ZA,
}
RegionLagos = cloudprovider.SGeographicInfo{
Latitude: 6.524,
Longitude: 3.379,
City: CITY_LAGOS,
CountryCode: COUNTRY_CODE_NG,
}
// Middleeast
RegionBahrain = cloudprovider.SGeographicInfo{
@@ -513,6 +531,12 @@ var (
City: CITY_WARSAW,
CountryCode: COUNTRY_CODE_PL,
}
RegionIstanbul = cloudprovider.SGeographicInfo{
Latitude: 41.008,
Longitude: 28.978,
City: CITY_ISTANBUL,
CountryCode: COUNTRY_CODE_TR,
}
RegionMadrid = cloudprovider.SGeographicInfo{
Latitude: 40.438,
Longitude: -3.82,
@@ -640,8 +664,8 @@ var (
}
RegionMexico = cloudprovider.SGeographicInfo{
Latitude: 55.118,
Longitude: 141.038,
Latitude: 19.433,
Longitude: -99.133,
City: CITY_MEXICO,
CountryCode: COUNTRY_CODE_MX,
}
@@ -732,6 +756,18 @@ var (
City: CITY_LOS_ANGELES,
CountryCode: COUNTRY_CODE_US,
}
RegionDenver = cloudprovider.SGeographicInfo{
Latitude: 39.739,
Longitude: -104.990,
City: CITY_DENVER,
CountryCode: COUNTRY_CODE_US,
}
RegionWashingtonDC = cloudprovider.SGeographicInfo{
Latitude: 38.907,
Longitude: -77.037,
City: CITY_WASHINGTON,
CountryCode: COUNTRY_CODE_US,
}
RegionSaltLakeCity = cloudprovider.SGeographicInfo{
Latitude: 40.777,
Longitude: -111.991,

View File

@@ -81,6 +81,13 @@ const (
STORAGE_UCLOUD_LOCAL_SSD = "LOCAL_SSD" // SSD本地盘
STORAGE_UCLOUD_EXCLUSIVE_LOCAL_DISK = "EXCLUSIVE_LOCAL_DISK" // 独享本地盘
// RockBase storage type
STORAGE_ROCKBASE_CLOUD_NORMAL = "CLOUD_NORMAL" // 普通云盘
STORAGE_ROCKBASE_CLOUD_SSD = "CLOUD_SSD" // SSD云盘
STORAGE_ROCKBASE_LOCAL_NORMAL = "LOCAL_NORMAL" // 普通本地盘
STORAGE_ROCKBASE_LOCAL_SSD = "LOCAL_SSD" // SSD本地盘
STORAGE_ROCKBASE_EXCLUSIVE_LOCAL_DISK = "EXCLUSIVE_LOCAL_DISK" // 独享本地盘
// Zstack storage type
STORAGE_ZSTACK_LOCAL_STORAGE = "localstorage"
STORAGE_ZSTACK_CEPH = "ceph"

View File

@@ -315,6 +315,13 @@ type ICloudProvider interface {
GetIAiGateways() ([]IAiGateway, error)
GetIAiGatewayById(id string) (IAiGateway, error)
CreateIAiGateway(opts *AiGatewayCreateOptions) (IAiGateway, error)
GetNotices() ([]INotice, error)
}
type INotice interface {
GetTitle() string
GetContent() string
}
func IsSupportCapability(prod ICloudProvider, capa string) bool {
@@ -423,6 +430,10 @@ func RegisterFactory(factory ICloudProviderFactory) {
providerTable[factory.GetId()] = factory
}
func RegisterFactoryAlias(alias string, factory ICloudProviderFactory) {
providerTable[alias] = factory
}
func GetProviderFactory(provider string) (ICloudProviderFactory, error) {
factory, ok := providerTable[provider]
if ok {
@@ -660,6 +671,10 @@ func (base *SBaseProvider) CreateIAiGateway(opts *AiGatewayCreateOptions) (IAiGa
return nil, errors.Wrapf(ErrNotImplemented, "CreateIAiGateway")
}
func (base *SBaseProvider) GetNotices() ([]INotice, error) {
return nil, errors.Wrapf(ErrNotImplemented, "GetNotices")
}
func NewBaseProvider(factory ICloudProviderFactory) SBaseProvider {
return SBaseProvider{factory: factory}
}

View File

@@ -0,0 +1,33 @@
// 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 cloudprovider
import "time"
// IsNoticePublishedToday reports whether t falls on the current calendar day in Asia/Shanghai.
func IsNoticePublishedToday(t time.Time) bool {
if t.IsZero() {
return false
}
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
loc = time.FixedZone("CST", 8*3600)
}
now := time.Now().In(loc)
t = t.In(loc)
y1, m1, d1 := now.Date()
y2, m2, d2 := t.Date()
return y1 == y2 && m1 == m2 && d1 == d2
}

View File

@@ -22,28 +22,44 @@ import (
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"cn-qingdao": api.RegionQingdao,
"cn-beijing": api.RegionBeijing,
"cn-beijing-finance-1": api.RegionBeijing,
"cn-zhangjiakou": api.RegionZhangjiakou,
"cn-huhehaote": api.RegionHuhehaote,
"cn-huhehaote-nebula-1": api.RegionHuhehaote,
"cn-wulanchabu": api.RegionWulanchabu,
"cn-hangzhou": api.RegionHangzhou,
"cn-hangzhou-finance": api.RegionHangzhou,
"cn-shanghai": api.RegionShanghai,
"cn-shanghai-finance-1": api.RegionShanghai,
"cn-nanjing": api.RegionNanjing,
"cn-fuzhou": api.RegionFujian,
"cn-fujian": api.RegionFujian,
"cn-wuhan": api.RegionWuhan,
"cn-wuhan-lr": api.RegionWuhan,
"cn-shenzhen": api.RegionShenzhen,
"cn-shenzhen-finance-1": api.RegionShenzhen,
"cn-hongkong": api.RegionHongkong,
"cn-chengdu": api.RegionChengdu,
"cn-heyuan": api.RegionHeyuan,
"cn-guangzhou": api.RegionGuangzhou,
"cn-chengdu": api.RegionChengdu,
"cn-zhongwei": api.RegionNingxia,
"cn-hongkong": api.RegionHongkong,
"cn-north-2-gov-1": api.RegionBeijing,
"ap-northeast-1": api.RegionTokyo,
"ap-northeast-2": api.RegionSeoul,
"ap-southeast-1": api.RegionSingapore,
"ap-southeast-2": api.RegionSydney,
"ap-southeast-3": api.RegionKualaLumpur,
"ap-southeast-5": api.RegionJakarta,
"ap-southeast-6": api.RegionManila,
"ap-southeast-7": api.RegionBangkok,
"ap-southeast-8": api.RegionKualaLumpur,
"ap-south-1": api.RegionMumbai,
"us-east-1": api.RegionVirginia,
"us-west-1": api.RegionSiliconValley,
"na-south-1": api.RegionMexico,
"eu-west-1": api.RegionLondon,
"me-east-1": api.RegionDubai,
"eu-west-2": api.RegionParis,
"eu-central-1": api.RegionFrankfurt,
"cn-wulanchabu": api.RegionWulanchabu,
"cn-guangzhou": api.RegionGuangzhou,
"me-east-1": api.RegionDubai,
"me-central-1": api.RegionDubai,
}

View File

@@ -0,0 +1,152 @@
// 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 aliyun
import (
"context"
"encoding/xml"
"fmt"
"html"
"io"
"net/http"
"regexp"
"strings"
"time"
"github.com/pkg/errors"
"yunion.io/x/pkg/util/httputils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
const aliyunNoticeRssURL = "https://www.aliyun.com/rss/notice/zh.xml"
var htmlTagRe = regexp.MustCompile(`<[^>]*>`)
type SNotice struct {
title string
content string
}
func (notice *SNotice) GetTitle() string {
return notice.title
}
func (notice *SNotice) GetContent() string {
return notice.content
}
type sRssFeed struct {
Channel sRssChannel `xml:"channel"`
}
type sRssChannel struct {
Items []sRssItem `xml:"item"`
}
type sRssItem struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
PubDate string `xml:"pubDate"`
ContentEncoded string `xml:"http://purl.org/rss/1.0/modules/content/ encoded"`
}
func (self *SAliyunClient) GetNotices() ([]cloudprovider.INotice, error) {
body, err := self.fetchNoticeRss()
if err != nil {
return nil, err
}
feed := &sRssFeed{}
err = xml.Unmarshal(body, feed)
if err != nil {
return nil, errors.Wrap(err, "xml.Unmarshal")
}
ret := make([]cloudprovider.INotice, 0, len(feed.Channel.Items))
for i := range feed.Channel.Items {
item := &feed.Channel.Items[i]
pubAt, err := parseRssPubDate(item.PubDate)
if err != nil || !cloudprovider.IsNoticePublishedToday(pubAt) {
continue
}
ret = append(ret, &SNotice{
title: strings.TrimSpace(item.Title),
content: formatRssNoticeContent(item),
})
}
return ret, nil
}
func parseRssPubDate(pubDate string) (time.Time, error) {
pubDate = strings.TrimSpace(pubDate)
if len(pubDate) == 0 {
return time.Time{}, errors.New("empty pubDate")
}
return http.ParseTime(pubDate)
}
func (self *SAliyunClient) fetchNoticeRss() ([]byte, error) {
client := httputils.GetAdaptiveTimeoutClient()
httputils.SetClientProxyFunc(client, self.cpcfg.ProxyFunc)
resp, err := httputils.Request(client, context.Background(), httputils.GET, aliyunNoticeRssURL, nil, nil, self.debug)
if err != nil {
return nil, errors.Wrap(err, "Request")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("unexpected status %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "ReadAll")
}
return body, nil
}
func formatRssNoticeContent(item *sRssItem) string {
content := stripHTML(item.ContentEncoded)
if len(content) == 0 {
content = stripHTML(item.Description)
}
if len(item.Link) > 0 {
if len(content) > 0 {
content = fmt.Sprintf("%s\n\nLink: %s", content, item.Link)
} else {
content = item.Link
}
}
return content
}
func stripHTML(s string) string {
if len(s) == 0 {
return ""
}
s = html.UnescapeString(s)
s = htmlTagRe.ReplaceAllString(s, "")
s = strings.ReplaceAll(s, "\u00a0", " ")
lines := strings.Split(s, "\n")
parts := []string{}
for _, line := range lines {
line = strings.TrimSpace(line)
if len(line) > 0 {
parts = append(parts, line)
}
}
return strings.Join(parts, "\n")
}

View File

@@ -518,3 +518,7 @@ func (self *SAliyunProvider) GetISSLCertificates() ([]cloudprovider.ICloudSSLCer
func (self *SAliyunProvider) GetISSLCertificate(certId string) (cloudprovider.ICloudSSLCertificate, error) {
return self.client.GetISSLCertificate(certId)
}
func (self *SAliyunProvider) GetNotices() ([]cloudprovider.INotice, error) {
return self.client.GetNotices()
}

View File

@@ -217,7 +217,7 @@ func (client *SAwsClient) getHttpClient() (*http.Client, error) {
if client.cpcfg.ReadOnly {
if len(action) > 0 {
for _, prefix := range []string{"Get", "List", "Describe"} {
for _, prefix := range []string{"Get", "List", "Describe", "AssumeRole"} {
if strings.HasPrefix(action, prefix) {
return respCheck, nil
}

View File

@@ -37,12 +37,14 @@ var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"cn-north-1": api.RegionBeijing,
"cn-northwest-1": api.RegionNingxia,
"eu-central-1": api.RegionFrankfurt,
"eusc-de-east-1": api.RegionFrankfurt,
"eu-west-1": api.RegionIreland,
"eu-west-2": api.RegionLondon,
"eu-west-3": api.RegionParis,
"eu-north-1": api.RegionStockholm,
"sa-east-1": api.RegionSaoPaulo,
"us-gov-west-1": api.RegionUSGOVWest,
"us-gov-east-1": api.RegionNothVirginia,
"af-south-1": api.RegionCapeTown,
"me-south-1": api.RegionBahrain,

View File

@@ -44,6 +44,8 @@ var AzureGeographicInfo = map[string]cloudprovider.SGeographicInfo{
"westus2": api.RegionWashington, //美国 华盛顿
"southcentralus": api.RegionTexas, //美国 德克萨斯
"brazilsouth": api.RegionSaoPaulo, //巴西 圣保罗
"brazilsoutheast": api.RegionRioDeJaneiro, // 里约热内卢
"chilecentral": api.RegionSantiago,
"koreacentral": api.RegionSeoul, //韩国 汉城 -> 首尔
"centralindia": api.RegionMaharashtra, //印度 马哈拉施特拉邦
"northcentralus": api.RegionChicago, //美国 芝加哥
@@ -61,7 +63,6 @@ var AzureGeographicInfo = map[string]cloudprovider.SGeographicInfo{
"germanywestcentral": api.RegionFrankfurt, // 法兰克福
"germanynorth": api.RegionDelmenhorst, // 代尔门霍斯特
"westus3": api.RegionPhoenix,
"brazilsoutheast": api.RegionRioDeJaneiro, // 里约热内卢
"jioindiawest": api.RegionJioIndiaWest, // 贾姆讷格尔
"jioindiacentral": api.RegionJioIndiaCentral, // 那格浦尔
"swedencentral": api.RegionSandviken,
@@ -70,6 +71,14 @@ var AzureGeographicInfo = map[string]cloudprovider.SGeographicInfo{
"italynorth": api.RegionMilan,
"israelcentral": api.RegionTelAviv,
"brazilus": api.RegionIndianapolis,
"austriaeast": api.RegionFrankfurt,
"belgiumcentral": api.RegionBelgium,
"denmarkeast": api.RegionOslo,
"indonesiacentral": api.RegionJakarta,
"malaysiawest": api.RegionKualaLumpur,
"mexicocentral": api.RegionMexico,
"newzealandnorth": api.RegionNewZealand,
"spaincentral": api.RegionMadrid,
"chinaeast": api.RegionShanghai,
"chinaeast2": api.RegionShanghai,

View File

@@ -19,7 +19,6 @@ 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"
@@ -168,18 +167,7 @@ func (self *SRegion) CreateNetworkInterface(resourceGroup string, nicName string
}
func (self *SRegion) GetINetworkInterfaces() ([]cloudprovider.ICloudNetworkInterface, error) {
nics, err := self.GetNetworkInterfaces()
if err != nil {
return nil, errors.Wrapf(err, "GetNetworkInterfaces")
}
ret := []cloudprovider.ICloudNetworkInterface{}
for i := range nics {
if len(nics[i].Properties.VirtualMachine.ID) > 0 {
continue
}
ret = append(ret, &nics[i])
}
return ret, nil
return []cloudprovider.ICloudNetworkInterface{}, nil
}
func (self *SInstanceNic) GetAssociateId() string {

View File

@@ -19,37 +19,166 @@ import (
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
// GetGeographicInfo 使用 RegionCode无 RegionCode 时使用 RegionId 作为 key
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"cn-beijing-5": api.RegionBeijing,
// 北京
"cn-beijing-5": api.RegionBeijing,
// 天津 / 华北
"cn-tianjin-2": api.RegionTianjin,
"cn-tianjin-01": api.RegionTianjin,
"e4874db42e9211ed88f70242ac110002": api.RegionTianjin, // 天津3
// 河北
"cn-he-sjz20-hybrid-ctcloud": api.RegionShijiazhuang,
"3ddd0446876d11eaab020242ac110002": api.RegionShijiazhuang, // 石家庄2
"529d1808bcf511eb93260242ac110002": api.RegionShijiazhuang, // 石家庄3
// 内蒙
"cn-neimeng-6": api.RegionNeimenggu,
"cn-huhehaote-03": api.RegionHuhehaote,
"cn-huhehaote-6": api.RegionHuhehaote, // 历史别名
// 山西
"cn-jinzhong-2": api.RegionJinzhong,
"cn-taiyuan-04": api.RegionJinzhong,
"05f7a93651a211ecbe170242ac110002": api.RegionJinzhong, // 太原2
// 辽宁
"cn-shenyang-08": api.RegionShenyang,
"cn-ln-ly1-public-ctcloud": api.RegionShenyang,
"172f2480f64b11ea98a40242ac110002": api.RegionShenyang, // 沈阳4
"200000001820": api.RegionShenyang, // 辽阳2
// 黑龙江
"cn-haerbin-2": api.RegionHaerbin,
// 上海 / 华东
"cn-shanghai-7": api.RegionShanghai,
"cn-shanghai-15": api.RegionShanghai,
"cn-shanghai-36": api.RegionShanghai,
"cn-huadong-1": api.RegionShanghai,
"db0fed10499511eb8a780242ac110002": api.RegionShanghai, // 上海8
"01aaa9182e9311edad970242ac110002": api.RegionShanghai, // 上海9
// 江苏
"cn-nanjing-2": api.RegionNanjing,
"cn-nanjing-3": api.RegionNanjing,
"cn-nanjing-4": api.RegionNanjing,
"cn-nanjing-5": api.RegionNanjing,
"0e72cfe651a211ecabbe0242ac110002": api.RegionNanjing, // 扬州
// 浙江
"cn-hangzhou-2": api.RegionHangzhou,
"cn-nanjing-4": api.RegionNanjing,
"cn-wuhan-3": api.RegionWuhan,
"cn-yaan-2": api.RegionXian,
"cn-wuhu-1": api.RegionWuhu,
"cn-kunming-2": api.RegionKunming,
"cn-hefei2": api.RegionHefei,
"cn-fujian-3": api.RegionFujian,
"cn-jinzhong-2": api.RegionJinzhong,
"cn-nanjing-5": api.RegionNanning,
"cn-huhehaote-6": api.RegionHuhehaote,
"cn-nanjing-3": api.RegionNanjing,
"cn-haikou-2": api.RegionHaikou,
"cn-tianjin-2": api.RegionTianjin,
"cn-wuhan-5": api.RegionWuhan,
"cn-foshan-3": api.RegionFoshan,
"cn-nanning-2": api.RegionNanning,
"cn-chenzhou-4": api.RegionChengzhou,
"cn-hanzhou-07": api.RegionHangzhou,
"200000001856": api.RegionHangzhou, // 浙江金华6
// 安徽
"cn-wuhu-1": api.RegionWuhu,
"cn-wuhu-04": api.RegionWuhu,
"cn-hefei2": api.RegionHefei,
"b02ba000ab1c11ec8c4a0242ac110002": api.RegionHefei, // 合肥
"200000003615": api.RegionWuhu, // 芜湖5
// 福建
"cn-fujian-3": api.RegionFujian,
"cn-fuzhou-4": api.RegionFujian,
"cn-xiamen-3": api.RegionFujian,
"cn-fuzhou-25": api.RegionFujian,
"4f23b922ab1d11ecaa9f0242ac110002": api.RegionFujian, // 福州5
// 江西
"cn-jiangxi-2": api.RegionJiangxi,
"cn-hunan-3": api.RegionChangsha,
"cn-nanchang-05": api.RegionJiangxi,
"200000001476": api.RegionJiangxi, // 南昌18
"200000004030": api.RegionJiangxi, // 南昌4
// 山东
"cn-qingdao-20": api.RegionQingdao,
"9395e62eb52511eab9d70242ac110002": api.RegionQingdao, // 青岛3
"63daa10af68111ecb89f0242ac110002": api.RegionQingdao, // 青岛4
// 河南
"cn-zhengzhou-2": api.RegionZhengzhou,
"cn-zhengzhou-05": api.RegionZhengzhou,
"200000004029": api.RegionZhengzhou, // 郑州13
// 湖北
"cn-wuhan-3": api.RegionWuhan,
"cn-wuhan-5": api.RegionWuhan,
"cn-wuhan-41": api.RegionWuhan,
// 湖南
"cn-hunan-3": api.RegionChangsha,
"cn-changsha-42": api.RegionChangsha,
"cn-chenzhou-2": api.RegionChengzhou,
"cn-chenzhou-4": api.RegionChengzhou, // 历史别名
// 广东
"cn-foshan-3": api.RegionFoshan,
"cn-guangzhou-5": api.RegionGuangzhou,
"cn-huanan-02": api.RegionGuangzhou,
"f4b6680cac0411ecb2da0242ac110002": api.RegionGuangzhou, // 广州5
// 广西
"cn-nanning-2": api.RegionNanning,
"cn-nanning-23": api.RegionNanning,
"200000001709": api.RegionNanning, // 南宁24
// 海南
"cn-haikou-2": api.RegionHaikou,
// 重庆
"cn-chongqing-2": api.RegionChongqing,
"cn-shanghai-7": api.RegionShanghai,
"cn-shanxi-2": api.RegionXian,
"cn-guiyang-1": api.RegionGuiyang,
"cn-haerbin-2": api.RegionHaerbin,
"cn-zhengzhou-2": api.RegionZhengzhou,
"cn-xian-4": api.RegionXian,
"cn-xian-5": api.RegionXian,
// 四川
"cn-yaan-2": api.RegionChengdu,
"ab8a29247cc111ec94230242ac110002": api.RegionChengdu, // 成都5
"cn-xinan-01": api.RegionChengdu,
"cn-lasa-4": api.RegionChengdu, // 拉萨
// 贵州
"cn-guiyang-1": api.RegionGuiyang,
"cn-xinan2-gz-01": api.RegionGuiyang,
// 云南
"cn-kunming-2": api.RegionKunming,
// 陕西
"cn-shanxi-2": api.RegionXian,
"cn-xian-4": api.RegionXian,
"cn-xian-5": api.RegionXian,
"cn-xian-07": api.RegionXian,
// 甘肃
"cn-lanzhou-3": api.RegionLanzhou,
"cn-nanjing-2": api.RegionNanjing,
"cn-qingyang-02": api.RegionQingYang,
"05aa842ab38d11eba6640242ac110002": api.RegionLanzhou, // 兰州3
"200000003684": api.RegionQingYang, // 庆阳3
// 青海
"cn-xining-2": api.RegionLanzhou,
// 宁夏
"cn-zhongwei-2": api.RegionNingxia,
"cn-zhongwei-05": api.RegionNingxia,
"200000003576": api.RegionNingxia, // 中卫6
// 新疆
"cn-wlmq-27": api.RegionWulumuqi,
"cn-wulumuqi-07": api.RegionWulumuqi,
"2d0cadee70d211ea8c4d0242ac110002": api.RegionWulumuqi, // 乌鲁木齐2
// 港澳及海外
"cn-xianggang-1": api.RegionHongkong,
"cn-xianggang-02": api.RegionHongkong,
"4681b038013011ea9b210242ac110002": api.RegionHongkong, // 香港5
"cn-aomen-01": api.RegionHongkong,
"cn-xinjiapo-3": api.RegionSingapore,
"cn-xinjiapo-04": api.RegionSingapore,
"cn-falankefu-1": api.RegionFrankfurt,
"cn-shengbaoluo-1": api.RegionSaoPaulo,
"cn-dibai-1": api.RegionDubai,
"cn-ydnixiya-01": api.RegionJakarta,
"cn-badayan-01": api.RegionManila,
}

View File

@@ -21,31 +21,57 @@ import (
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
// 对齐最新 RegionIdcn- 前缀),与 regionIdToPoolId 保持一致;尽量复用 compute.RegionXXX
"cn-beijing-1": api.RegionBeijing,
"cn-jiangsu-1": api.RegionSuzhou, // 江苏(无锡附近)复用苏州坐标
"cn-guangdong-1": api.RegionGuangzhou, // 广东(东莞附近)复用广州坐标
"cn-sichuan-1": api.RegionChengdu, // 四川(雅安/成都附近)复用成都坐标
"cn-henan-1": api.RegionZhengzhou, // 河南(郑州)
"cn-hunan-1": api.RegionChangsha, // 湖南(株洲/长沙
"cn-shandong-1": api.RegionJiNan, // 山东(济南)
"cn-shaanxi-1": api.RegionXian, // 陕西(西安)
"cn-shanghai-1": api.RegionShanghai, // 上海
"cn-chongqing-1": api.RegionChongqing, // 重庆
"cn-zhejiang-1": api.RegionHangzhou, // 浙江(宁波/杭州)
"cn-tianjin-1": api.RegionTianjin, // 天津
"cn-jilin-1": api.RegionChangchun, // 吉林(长春)
"cn-hubei-2": api.RegionXiangyang, // 湖北(襄阳)
"cn-jiangxi-1": api.RegionJiangxi, // 江西(南昌)
"cn-gansu-1": api.RegionLanzhou, // 甘肃(兰州)
"cn-shangxi-1": api.RegionJinzhong, // 山西(太原附近),复用晋中
"cn-liaoning-1": api.RegionShenyang, // 辽宁(沈阳)
"cn-yunnan-1": api.RegionKunming, // 云南(昆明)
"cn-hebei-1": api.RegionShijiazhuang, // 河北(石家庄)
"cn-fujian-1": api.RegionFujian, // 福建(厦门附近)
"cn-guangxi-1": api.RegionNanning, // 广西(南宁)
"cn-anhui-1": api.RegionHuainan, // 安徽(淮南)
"cn-neimenggu-1": api.RegionHuhehaote, // 内蒙古(呼和浩特)
"cn-guzhou-1": api.RegionGuiyang, // 贵州(贵阳)
"cn-hainan-1": api.RegionHaikou, // 海南(海口)
"cn-xinjiang-1": api.RegionWulumuqi, // 新疆(乌鲁木齐)
"cn-beijing-1": api.RegionBeijing,
"cn-beijing-11": api.RegionBeijing, // 华北-北京9
"cn-neimenggu-1": api.RegionHuhehaote, // 华北-呼和浩特
"cn-hebei-1": api.RegionShijiazhuang, // 河北-石家庄
"cn-shanxi-1": api.RegionJinzhong, // 山西-太原
"cn-shangxi-1": api.RegionJinzhong, // 山西-太原(历史拼写
"cn-tianjin-1": api.RegionTianjin, // 天津-天津
"cn-liaoning-1": api.RegionShenyang, // 辽宁-沈阳
"cn-jilin-1": api.RegionChangchun, // 吉林-长春
"cn-heilongjiang-1": api.RegionHaerbin, // 黑龙江-哈尔滨
"cn-heilongjiang-3": api.RegionHaerbin, // 东北-哈尔滨
"cn-jiangsu-1": api.RegionSuzhou, // 华东-苏州
"cn-jiangsu-20": api.RegionNanjing, // 华东-南京
"cn-shanghai-1": api.RegionShanghai, // 华东-上海1
"cn-shanghai-5": api.RegionShanghai, // 华东-上海5
"cn-zhejiang-1": api.RegionHangzhou, // 华东-杭州
"cn-zhejiang-6": api.RegionHangzhou, // 华东-杭州4
"cn-shandong-1": api.RegionJiNan, // 华东-济南
"cn-shandong-14": api.RegionQingdao, // 华东-青岛
"cn-anhui-1": api.RegionHuainan, // 安徽-淮南
"cn-fujian-1": api.RegionFujian, // 福建-厦门
"cn-jiangxi-1": api.RegionJiangxi, // 江西-南昌
"cn-guangdong-1": api.RegionGuangzhou, // 华南-广州3
"cn-guangxi-1": api.RegionNanning, // 广西-南宁3
"cn-guangxi-2": api.RegionNanning, // 广西-南宁
"cn-hainan-1": api.RegionHaikou, // 海南-海口
"cn-henan-1": api.RegionZhengzhou, // 华中-郑州
"cn-hubei-1": api.RegionWuhan, // 湖北-武汉
"cn-hubei-2": api.RegionXiangyang, // 湖北-襄阳
"cn-hunan-1": api.RegionChangsha, // 华中-长沙2
"cn-sichuan-1": api.RegionChengdu, // 西南-成都
"cn-sichuan-7": api.RegionChengdu, // 西南-成都4
"cn-chongqing-1": api.RegionChongqing, // 西南-重庆
"cn-guizhou-1": api.RegionGuiyang, // 西南-贵阳
"cn-guzhou-1": api.RegionGuiyang, // 西南-贵阳(历史拼写)
"cn-guizhou-5": api.RegionGuiyang, // 西南-贵阳3
"cn-yunnan-1": api.RegionKunming, // 云南-昆明2
"cn-xizang-1": api.RegionChengdu, // 西藏-拉萨
"cn-xizang-2": api.RegionChengdu, // 西藏-拉萨2
"cn-shaanxi-1": api.RegionXian, // 西北-西安
"cn-gansu-1": api.RegionLanzhou, // 甘肃-兰州
"cn-qinghai-1": api.RegionLanzhou, // 青海-海东
"cn-qinghai-2": api.RegionLanzhou, // 青海-海东2
"cn-qinghai-3": api.RegionLanzhou, // 青海-西宁
"cn-ningxia-1": api.RegionNingxia, // 宁夏-中卫
"cn-ningxia-2": api.RegionNingxia, // 宁夏-中卫2
"cn-ningxia-3": api.RegionNingxia, // 西北-中卫
"cn-xinjiang-1": api.RegionWulumuqi, // 新疆-昌吉
}

View File

@@ -29,6 +29,7 @@ var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"asia-south2": api.RegionDelhi,
"asia-southeast1": api.RegionSingapore,
"asia-southeast2": api.RegionJakarta,
"asia-southeast3": api.RegionBangkok,
"australia-southeast1": api.RegionSydney,
"australia-southeast2": api.RegionMelbourne,
@@ -77,6 +78,7 @@ var RegionNames = map[string]string{
"asia-south2": "德里",
"asia-southeast1": "新加坡",
"asia-southeast2": "雅加达",
"asia-southeast3": "曼谷",
"australia-southeast1": "悉尼",
"australia-southeast2": "墨尔本",

View File

@@ -23,22 +23,54 @@ import (
// International: https://developer-intl.huaweicloud.com/endpoint
// ref: https://countrycode.org
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"cn-east-2": api.RegionShanghai,
"cn-east-3": api.RegionShanghai,
"cn-north-1": api.RegionBeijing,
"cn-north-4": api.RegionBeijing,
"cn-south-1": api.RegionGuangzhou,
"cn-south-2": api.RegionGuangzhou,
// 华北
"cn-north-1": api.RegionBeijing,
"cn-north-4": api.RegionBeijing,
"cn-north-9": api.RegionNeimenggu,
"cn-north-11": api.RegionWulanchabu,
"cn-north-12": api.RegionBeijing,
"cn-north-219": api.RegionBeijing,
// 华东
"cn-east-2": api.RegionShanghai,
"cn-east-3": api.RegionShanghai,
"cn-east-4": api.RegionWuhu,
"cn-east-5": api.RegionQingdao,
// 华南
"cn-south-1": api.RegionGuangzhou,
"cn-south-2": api.RegionGuangzhou,
"cn-south-4": api.RegionGuangzhou,
// 西南 / 东北 / 西北
"cn-southwest-2": api.RegionGuiyang,
"cn-northeast-1": api.RegionDalian,
"cn-northwest-1": api.RegionNingxia,
// 亚太
"ap-southeast-1": api.RegionHongkong,
"ap-southeast-2": api.RegionBangkok,
"ap-southeast-3": api.RegionSingapore,
"eu-west-0": api.RegionParis,
"cn-northeast-1": api.RegionDalian,
"cn-southwest-2": api.RegionGuiyang,
"af-south-1": api.RegionJohannesburg,
"sa-brazil-1": api.RegionSaoPaulo,
"ap-southeast-4": api.RegionJakarta,
"ap-southeast-5": api.RegionManila,
// 欧洲
"eu-west-0": api.RegionParis,
"eu-west-1": api.RegionDublin,
// 中东 / 土耳其
"me-east-1": api.RegionDamman,
"tr-west-1": api.RegionIstanbul,
// 非洲
"af-south-1": api.RegionJohannesburg,
"af-north-1": api.RegionTelAviv,
// 拉美
"na-mexico-1": api.RegionMexico,
"la-north-2": api.RegionMexico,
"sa-brazil-1": api.RegionSaoPaulo,
"la-south-2": api.RegionSantiago,
"cn-north-9": api.RegionNeimenggu,
"cn-north-219": api.RegionBeijing,
"sa-argentina-1": api.RegionSantiago,
"sa-peru-1": api.RegionSantiago,
}

View File

@@ -52,11 +52,31 @@ func (region *SRegion) GetCloudEnv() string {
func (region *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
geo, ok := map[string]cloudprovider.SGeographicInfo{
"cn-northwest-1": api.RegionQingYang,
"ap-singapore-1": api.RegionSingapore,
// 华北
"cn-beijing-6": api.RegionBeijing,
"cn-guangzhou-1": api.RegionGuangzhou,
"cn-beijing-fin": api.RegionBeijing,
"cn-north-vip1": api.RegionBeijing,
"cn-hohhot-1": api.RegionHuhehaote,
// 华东
"cn-shanghai-2": api.RegionShanghai,
"cn-shanghai-fin": api.RegionShanghai,
// 华南
"cn-guangzhou-1": api.RegionGuangzhou,
// 华中 / 西南
"cn-central-1": api.RegionWuhan,
"cn-southwest-1": api.RegionChongqing,
// 西北
"cn-northwest-1": api.RegionQingYang,
"cn-northwest-3": api.RegionNingxia,
"cn-northwest-4": api.RegionLanzhou,
// 海外
"ap-singapore-1": api.RegionSingapore,
"eu-east-1": api.RegionMoscow,
}[region.Region]
if ok {
return geo

View File

@@ -44,7 +44,8 @@ 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/ucloud/provider" // object storages
_ "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
)

View File

@@ -0,0 +1,124 @@
// 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 oracle
import (
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
// ref: https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm
// ref: https://github.com/oracle/oci-go-sdk/blob/v65.110.0/common/regions.go
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
// Asia Pacific
"ap-batam-1": api.RegionJakarta,
"ap-chiyoda-1": api.RegionTokyo,
"ap-chuncheon-1": api.RegionSeoul,
"ap-chuncheon-2": api.RegionSeoul,
"ap-delhi-1": api.RegionDelhi,
"ap-hyderabad-1": api.RegionHyderabad,
"ap-ibaraki-1": api.RegionTokyo,
"ap-kulai-2": api.RegionKualaLumpur,
"ap-melbourne-1": api.RegionMelbourne,
"ap-mumbai-1": api.RegionMumbai,
"ap-osaka-1": api.RegionOsaka,
"ap-seoul-1": api.RegionSeoul,
"ap-seoul-2": api.RegionSeoul,
"ap-singapore-1": api.RegionSingapore,
"ap-singapore-2": api.RegionSingapore,
"ap-suwon-1": api.RegionSeoul,
"ap-sydney-1": api.RegionSydney,
"ap-tokyo-1": api.RegionTokyo,
"ap-dcc-canberra-1": api.RegionYarralumla,
"ap-dcc-gazipur-1": api.RegionMumbai,
// Canada
"ca-montreal-1": api.RegionMontreal,
"ca-toronto-1": api.RegionToronto,
// Europe
"eu-amsterdam-1": api.RegionHolland,
"eu-budapest-1": api.RegionWarsaw,
"eu-crissier-1": api.RegionGeneva,
"eu-frankfurt-1": api.RegionFrankfurt,
"eu-frankfurt-2": api.RegionFrankfurt,
"eu-jovanovac-1": api.RegionWarsaw,
"eu-madrid-1": api.RegionMadrid,
"eu-madrid-2": api.RegionMadrid,
"eu-madrid-3": api.RegionMadrid,
"eu-marseille-1": api.RegionParis,
"eu-milan-1": api.RegionMilan,
"eu-paris-1": api.RegionParis,
"eu-stockholm-1": api.RegionStockholm,
"eu-turin-1": api.RegionTurin,
"eu-zurich-1": api.RegionZurich,
"eu-dcc-dublin-1": api.RegionDublin,
"eu-dcc-dublin-2": api.RegionDublin,
"eu-dcc-milan-1": api.RegionMilan,
"eu-dcc-milan-2": api.RegionMilan,
"eu-dcc-rating-1": api.RegionFrankfurt,
"eu-dcc-rating-2": api.RegionFrankfurt,
"eu-dcc-zurich-1": api.RegionZurich,
"uk-cardiff-1": api.RegionLondon,
"uk-london-1": api.RegionLondon,
"uk-gov-cardiff-1": api.RegionLondon,
"uk-gov-london-1": api.RegionLondon,
// Middle East
"me-abudhabi-1": api.RegionAbuDhabi,
"me-abudhabi-2": api.RegionAbuDhabi,
"me-abudhabi-3": api.RegionAbuDhabi,
"me-abudhabi-4": api.RegionAbuDhabi,
"me-alain-1": api.RegionAbuDhabi,
"me-dcc-doha-1": api.RegionDoha,
"me-dcc-muscat-1": api.RegionDubai,
"me-dubai-1": api.RegionDubai,
"me-ibri-1": api.RegionDubai,
"me-jeddah-1": api.RegionDamman,
"me-riyadh-1": api.RegionDamman,
// Africa
"af-casablanca-1": api.RegionMadrid,
"af-johannesburg-1": api.RegionJohannesburg,
// Israel
"il-jerusalem-1": api.RegionTelAviv,
// North America
"us-ashburn-1": api.RegionNothVirginia,
"us-ashburn-2": api.RegionNothVirginia,
"us-chicago-1": api.RegionChicago,
"us-gov-ashburn-1": api.RegionNothVirginia,
"us-gov-chicago-1": api.RegionChicago,
"us-gov-phoenix-1": api.RegionPhoenix,
"us-langley-1": api.RegionVirginia,
"us-luke-1": api.RegionPhoenix,
"us-newark-1": api.RegionVirginia,
"us-phoenix-1": api.RegionPhoenix,
"us-saltlake-2": api.RegionSaltLakeCity,
"us-sanjose-1": api.RegionSiliconValley,
"us-somerset-1": api.RegionVirginia,
"us-thames-1": api.RegionVirginia,
"mx-monterrey-1": api.RegionMexico,
"mx-queretaro-1": api.RegionMexico,
// South America
"sa-bogota-1": api.RegionSaoPaulo,
"sa-riodejaneiro-1": api.RegionRioDeJaneiro,
"sa-santiago-1": api.RegionSantiago,
"sa-saopaulo-1": api.RegionSaoPaulo,
"sa-valparaiso-1": api.RegionSantiago,
"sa-vinhedo-1": api.RegionSaoPaulo,
}

View File

@@ -0,0 +1,201 @@
// 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 oracle
import (
"fmt"
"net/url"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/httputils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
type SAnnouncementSummary struct {
Id string `json:"id"`
Summary string `json:"summary"`
AnnouncementType string `json:"announcementType"`
LifecycleState string `json:"lifecycleState"`
Services []string `json:"services"`
AffectedRegions []string `json:"affectedRegions"`
ReferenceTicket string `json:"referenceTicketNumber"`
TimeCreated string `json:"timeCreated"`
TimeUpdated string `json:"timeUpdated"`
}
type SAnnouncement struct {
SAnnouncementSummary
Description string `json:"description"`
AdditionalInformation string `json:"additionalInformation"`
}
type SNotice struct {
title string
content string
}
func (notice *SNotice) GetTitle() string {
return notice.title
}
func (notice *SNotice) GetContent() string {
return notice.content
}
func (self *SOracleClient) GetNotices() ([]cloudprovider.INotice, error) {
region, err := self.Region()
if err != nil {
return nil, err
}
summaries, err := self.listAnnouncements(region)
if err != nil {
return nil, err
}
ret := make([]cloudprovider.INotice, 0, len(summaries))
for i := range summaries {
if !isAnnouncementPublishedToday(&summaries[i]) {
continue
}
content := self.announcementContent(region, &summaries[i])
ret = append(ret, &SNotice{
title: summaries[i].Summary,
content: content,
})
}
return ret, nil
}
func isAnnouncementPublishedToday(summary *SAnnouncementSummary) bool {
ts := summary.TimeCreated
if len(ts) == 0 {
ts = summary.TimeUpdated
}
if len(ts) == 0 {
return false
}
t, err := parseAnnouncementTime(ts)
if err != nil {
return false
}
return cloudprovider.IsNoticePublishedToday(t)
}
func parseAnnouncementTime(ts string) (time.Time, error) {
layouts := []string{
time.RFC3339Nano,
time.RFC3339,
"2006-01-02T15:04:05.000+0000",
}
for _, layout := range layouts {
if t, err := time.Parse(layout, ts); err == nil {
return t, nil
}
}
return time.Time{}, errors.Errorf("parse announcement time %q", ts)
}
func (self *SOracleClient) listAnnouncements(region string) ([]SAnnouncementSummary, error) {
query := url.Values{}
query.Set("compartmentId", self.tenancyOCID)
query.Set("lifecycleState", "ACTIVE")
ret := []SAnnouncementSummary{}
for {
resp, token, err := self.request(httputils.GET, SERVICE_ANNOUNCEMENTS, region, "announcements", query, nil)
if err != nil {
return nil, err
}
items, err := resp.GetArray("items")
if err != nil {
return nil, errors.Wrapf(err, "GetArray items")
}
part := []SAnnouncementSummary{}
err = jsonutils.Update(&part, items)
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal announcement summaries")
}
ret = append(ret, part...)
if len(token) == 0 {
break
}
query.Set("page", token)
}
return ret, nil
}
func (self *SOracleClient) getAnnouncement(region, id string) (*SAnnouncement, error) {
resp, err := self.get(SERVICE_ANNOUNCEMENTS, region, "announcements", id, nil)
if err != nil {
return nil, err
}
ann := &SAnnouncement{}
err = resp.Unmarshal(ann)
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal announcement")
}
return ann, nil
}
func (self *SOracleClient) announcementContent(region string, summary *SAnnouncementSummary) string {
ann, err := self.getAnnouncement(region, summary.Id)
if err == nil {
return formatAnnouncementContent(ann)
}
return formatAnnouncementSummaryContent(summary)
}
func formatAnnouncementContent(ann *SAnnouncement) string {
parts := []string{}
if len(ann.Description) > 0 {
parts = append(parts, ann.Description)
}
if len(ann.AdditionalInformation) > 0 {
parts = append(parts, ann.AdditionalInformation)
}
if len(parts) > 0 {
return strings.Join(parts, "\n\n")
}
return formatAnnouncementSummaryContent(&ann.SAnnouncementSummary)
}
func formatAnnouncementSummaryContent(summary *SAnnouncementSummary) string {
lines := []string{}
if len(summary.AnnouncementType) > 0 {
lines = append(lines, fmt.Sprintf("Type: %s", summary.AnnouncementType))
}
if len(summary.Services) > 0 {
lines = append(lines, fmt.Sprintf("Services: %s", strings.Join(summary.Services, ", ")))
}
if len(summary.AffectedRegions) > 0 {
lines = append(lines, fmt.Sprintf("Affected regions: %s", strings.Join(summary.AffectedRegions, ", ")))
}
if len(summary.ReferenceTicket) > 0 {
lines = append(lines, fmt.Sprintf("Reference: %s", summary.ReferenceTicket))
}
if len(summary.TimeCreated) > 0 {
lines = append(lines, fmt.Sprintf("Created: %s", summary.TimeCreated))
}
if len(summary.TimeUpdated) > 0 {
lines = append(lines, fmt.Sprintf("Updated: %s", summary.TimeUpdated))
}
if len(lines) == 0 {
return summary.Summary
}
return strings.Join(lines, "\n")
}

View File

@@ -48,9 +48,11 @@ const (
NEXT_TOKEN = "opc-next-page"
SERVICE_IAAS = "iaas"
SERVICE_IDENTITY = "identity"
SERVICE_TELEMETRY = "telemetry"
SERVICE_IAAS = "iaas"
SERVICE_IDENTITY = "identity"
SERVICE_TELEMETRY = "telemetry"
SERVICE_ANNOUNCEMENTS = "announcements"
ANNOUNCEMENTS_API_VERSION = "20180904"
)
type OracleClientConfig struct {
@@ -199,6 +201,8 @@ func (self *SOracleClient) getUrl(service, regionId, resource string) (string, e
return fmt.Sprintf("https://%s.%s.oraclecloud.com/%s/%s", service, regionId, DEFAULT_API_VERSION, strings.TrimPrefix(resource, "/")), nil
case SERVICE_TELEMETRY:
return fmt.Sprintf("https://%s.%s.oraclecloud.com/%s/%s", service, regionId, MONITORY_API_VERSION, strings.TrimPrefix(resource, "/")), nil
case SERVICE_ANNOUNCEMENTS:
return fmt.Sprintf("https://%s.%s.oraclecloud.com/%s/%s", service, regionId, ANNOUNCEMENTS_API_VERSION, strings.TrimPrefix(resource, "/")), nil
default:
return "", errors.Wrapf(cloudprovider.ErrNotSupported, "%s", service)
}

View File

@@ -228,3 +228,7 @@ func (self *SOracleProvider) GetCloudRegionExternalIdPrefix() string {
func (self *SOracleProvider) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
return self.client.GetMetrics(opts)
}
func (self *SOracleProvider) GetNotices() ([]cloudprovider.INotice, error) {
return self.client.GetNotices()
}

View File

@@ -54,11 +54,8 @@ func (region *SRegion) GetCloudEnv() string {
}
func (region *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
geo, ok := map[string]cloudprovider.SGeographicInfo{
"ap-singapore-1": api.RegionSingapore,
}[region.RegionName]
if ok {
return geo
if info, ok := LatitudeAndLongitude[region.RegionName]; ok {
return info
}
return cloudprovider.SGeographicInfo{}
}

View File

@@ -19,25 +19,72 @@ import (
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
// ref: https://cloud.tencent.com/document/product/213/6091
// ref: https://cloud.tencent.com/developer/article/1930067
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"ap-bangkok": api.RegionBangkok, // 腾讯云 亚太地区(曼谷)
"ap-beijing": api.RegionBeijing, // 腾讯云 华北地区(北京)
"ap-chengdu": api.RegionChengdu, // 腾讯云 西南地区(成都)
"ap-chongqing": api.RegionChongqing, // 腾讯云 西南地区(重庆)
"ap-guangzhou": api.RegionGuangzhou, // 腾讯云 华南地区(广州)
"ap-guangzhou-open": api.RegionGuangzhou, // 腾讯云 华南地区(广州Open)
"ap-hongkong": api.RegionHongkong, // 腾讯云 东南亚地区(香港)
"ap-mumbai": api.RegionMumbai, // 腾讯云 亚太地区(孟买)
"ap-seoul": api.RegionSeoul, // 腾讯云 东南亚地区(首尔)
"ap-shanghai": api.RegionShanghai, // 腾讯云 华东地区(上海)
"ap-shanghai-fsi": api.RegionShanghai, // 腾讯云 华东地区(上海金融)
"ap-shenzhen-fsi": api.RegionShenzhen, // 腾讯云 华南地区(深圳金融)
"ap-singapore": api.RegionSingapore, // 腾讯云 东南亚地区(新加坡)
"ap-tokyo": api.RegionTokyo, // 腾讯云 亚太地区(东京)
"eu-frankfurt": api.RegionFrankfurt, // 腾讯云 欧洲地区(德国)
"eu-moscow": api.RegionMoscow, // 腾讯云 欧洲地区(莫斯科)
"na-ashburn": api.RegionVirginia, // 腾讯云 美国东部(弗吉尼亚)
"na-siliconvalley": api.RegionSiliconValley, // 腾讯云 美国西部(硅谷)
"na-toronto": api.RegionToronto, // 腾讯云 北美地区(多伦多)
"ap-nanjing": api.RegionNanjing,
// 华北
"ap-beijing": api.RegionBeijing,
"ap-beijing-fsi": api.RegionBeijing,
"ap-tianjin": api.RegionTianjin,
"ap-shijiazhuang-ec": api.RegionShijiazhuang,
// 华东
"ap-shanghai": api.RegionShanghai,
"ap-shanghai-fsi": api.RegionShanghai,
"ap-shanghai-adc": api.RegionShanghai,
"ap-nanjing": api.RegionNanjing,
"ap-jinan-ec": api.RegionJiNan,
"ap-hangzhou-ec": api.RegionHangzhou,
"ap-fuzhou-ec": api.RegionFujian,
"ap-hefei-ec": api.RegionHefei,
// 华南
"ap-guangzhou": api.RegionGuangzhou,
"ap-guangzhou-open": api.RegionGuangzhou,
"ap-qingyuan": api.RegionGuangzhou,
"ap-qingyuan-xinan": api.RegionGuangzhou,
"ap-shenzhen": api.RegionShenzhen,
"ap-shenzhen-fsi": api.RegionShenzhen,
"ap-shenzhen-sycft": api.RegionShenzhen,
// 华中
"ap-wuhan-ec": api.RegionWuhan,
"ap-changsha-ec": api.RegionChangsha,
"ap-zhengzhou-ec": api.RegionZhengzhou,
// 西南 / 西北
"ap-chengdu": api.RegionChengdu,
"ap-chongqing": api.RegionChongqing,
"ap-zhongwei": api.RegionNingxia,
"ap-xian-ec": api.RegionXian,
"ap-xibei-ec": api.RegionLanzhou,
// 东北
"ap-shenyang-ec": api.RegionShenyang,
// 港澳台
"ap-hongkong": api.RegionHongkong,
"ap-taipei": api.RegionTaiwan,
// 亚太
"ap-singapore": api.RegionSingapore,
"ap-jakarta": api.RegionJakarta,
"ap-seoul": api.RegionSeoul,
"ap-tokyo": api.RegionTokyo,
"ap-osaka": api.RegionOsaka,
"ap-bangkok": api.RegionBangkok,
"ap-mumbai": api.RegionMumbai,
// 中东
"me-saudi-arabia": api.RegionDamman,
// 欧洲
"eu-frankfurt": api.RegionFrankfurt,
"eu-moscow": api.RegionMoscow,
// 北美 / 南美
"na-ashburn": api.RegionVirginia,
"na-siliconvalley": api.RegionSiliconValley,
"na-toronto": api.RegionToronto,
"sa-saopaulo": api.RegionSaoPaulo,
}

View File

@@ -0,0 +1,180 @@
// 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
}

View File

@@ -0,0 +1,130 @@
// 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
// https://docs.ucloud.cn/api/summary/regionlist
var ROCKBASE_REGION_NAMES = map[string]string{
"cn-bj1": "北京一",
"cn-bj2": "北京二",
"cn-sh": "上海金融云",
"cn-sh2": "上海二",
"cn-gd": "广州",
"hk": "香港",
"us-ca": "洛杉矶",
"us-ws": "华盛顿",
"ge-fra": "法兰克福",
"th-bkk": "曼谷",
"kr-seoul": "首尔",
"sg": "新加坡",
"tw-tp": "台北",
"tw-kh": "高雄",
"jpn-tky": "东京",
"rus-mosc": "莫斯科",
"uae-dubai": "迪拜",
"idn-jakarta": "雅加达",
"ind-mumbai": "孟买",
"bra-saopaulo": "圣保罗",
"uk-london": "伦敦",
"afr-nigeria": "拉各斯",
"vn-sng": "胡志明市",
}
var ROCKBASE_REGION_NAMES_EN = map[string]string{
"cn-bj1": "Beijing 1",
"cn-bj2": "Beijing 2",
"cn-sh": "Shanghai 1",
"cn-sh2": "Shanghai 2",
"cn-gd": "Guangzhou",
"hk": "Hongkong",
"us-ca": "Los Angeles",
"us-ws": "Washington",
"ge-fra": "Frankfurt",
"th-bkk": "Bangkok",
"kr-seoul": "Seoul",
"sg": "Singapore",
"tw-tp": "TaiPei",
"tw-kh": "Gaoxiong",
"jpn-tky": "Tokyo",
"rus-mosc": "Moscow",
"uae-dubai": "Dubai",
"idn-jakarta": "Jakarta",
"ind-mumbai": "Mumbai",
"bra-saopaulo": "Sao Paulo",
"uk-london": "London",
"afr-nigeria": "Lagos",
"vn-sng": "Ho Chi Min City",
}
var ROCKBASE_ZONE_NAMES = map[string]string{
"cn-bj1-01": "北京一可用区A",
"cn-bj2-02": "北京二可用区B",
"cn-bj2-03": "北京二可用区C",
"cn-bj2-04": "北京二可用区D",
"cn-bj2-05": "北京二可用区E",
"cn-sh-01": "上海一可用区A",
"cn-sh2-01": "上海二可用区A",
"cn-sh2-02": "上海二可用区B",
"cn-gd-02": "广州可用区B",
"hk-01": "香港可用区A",
"hk-02": "香港可用区B",
"us-ca-01": "洛杉矶可用区A",
"us-ws-01": "华盛顿可用区A",
"ge-fra-01": "法兰克福可用区A",
"th-bkk-01": "曼谷可用区A",
"kr-seoul-01": "首尔可用区A",
"sg-01": "新加坡可用区A",
"tw-kh-01": "高雄可用区A",
"tw-tp-01": "台北可用区A",
"jpn-tky-01": "东京可用区A",
"rus-mosc-01": "莫斯科可用区A",
"uae-dubai-01": "迪拜可用区A",
"idn-jakarta-01": "雅加达可用区A",
"ind-mumbai-01": "孟买可用区A",
"bra-saopaulo-01": "圣保罗可用区A",
"uk-london-01": "伦敦可用区A",
"afr-nigeria-01": "拉各斯可用区A",
"vn-sng-01": "胡志明市可用区A",
}
var ROCKBASE_ZONE_NAMES_EN = map[string]string{
"cn-bj1-01": "Beijing 1 Zone A",
"cn-bj2-02": "Beijing 2 Zone B",
"cn-bj2-03": "Beijing 2 Zone C",
"cn-bj2-04": "Beijing 2 Zone D",
"cn-bj2-05": "Beijing 2 Zone E",
"cn-sh-01": "Shanghai 1 Zone A",
"cn-sh2-01": "Shanghai 2 Zone A",
"cn-sh2-02": "Shanghai 2 Zone B",
"cn-gd-02": "Guangzhou Zone B",
"hk-01": "Hongkong Zone A",
"hk-02": "Hongkong Zone B",
"us-ca-01": "Los Angeles Zone A",
"us-ws-01": "Washington Zone A",
"ge-fra-01": "Frankfurt Zone A",
"th-bkk-01": "Bangkok Zone A",
"kr-seoul-01": "Seoul Zone A",
"sg-01": "Singapore Zone A",
"tw-kh-01": "Gaoxiong Zone A",
"tw-tp-01": "TaiPei Zone A",
"jpn-tky-01": "Tokyo Zone A",
"rus-mosc-01": "Moscow Zone A",
"uae-dubai-01": "Dubai Zone A",
"idn-jakarta-01": "Jakarta Zone A",
"ind-mumbai-01": "Mumbai Zone A",
"bra-saopaulo-01": "Sao Paulo Zone A",
"uk-london-01": "London Zone A",
"afr-nigeria-01": "Lagos Zone A",
"vn-sng-01": "Ho Chi Min City Zone A",
}

View File

@@ -0,0 +1,456 @@
// 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"
"yunion.io/x/log"
"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"
)
// 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
}
// 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 {
return time.Unix(self.ExpiredTime, 0)
}
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
return self.storage, nil
}
func (self *SDisk) GetDiskFormat() string {
return "vhd"
}
func (self *SDisk) GetDiskSizeMB() int {
return self.SizeGB * 1024
}
func (self *SDisk) GetIsAutoDelete() bool {
if self.DiskType == "SystemDisk" {
return true
}
return false
}
func (self *SDisk) GetTemplateId() string {
if strings.Contains(self.DiskType, "SystemDisk") && len(self.UHostID) > 0 {
ins, err := self.storage.zone.region.GetInstanceByID(self.UHostID)
if err != nil {
log.Errorln(err)
}
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) {
if len(diskId) == 0 {
return nil, fmt.Errorf("GetDisk id should not empty")
}
disks, err := self.GetDisks("", "", []string{diskId})
if err != nil {
return nil, err
}
if len(disks) == 1 {
return &disks[0], nil
} else if len(disks) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("GetDisk %s %d found", diskId, len(disks))
}
}
// https://docs.ucloud.cn/api/udisk-api/describe_udisk
// diskType DataDisk|SystemDisk (DataDisk表示数据盘SystemDisk表示系统盘)
func (self *SRegion) GetDisks(zoneId string, diskType string, diskIds []string) ([]SDisk, error) {
disks := make([]SDisk, 0)
params := NewRockbaseParams()
if len(zoneId) > 0 {
params.Set("Zone", zoneId)
}
if len(diskType) > 0 {
params.Set("DiskType", diskType)
}
err := self.DoListAll("DescribeUDisk", params, &disks)
if err != nil {
return nil, err
}
if len(diskIds) > 0 {
filtedDisks := make([]SDisk, 0)
for i := range disks {
if utils.IsInStringArray(disks[i].UDiskID, diskIds) {
filtedDisks = append(filtedDisks, disks[i])
}
}
return filtedDisks, nil
}
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)
}

View File

@@ -0,0 +1,15 @@
// 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"

View File

@@ -0,0 +1,289 @@
// 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"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
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_ASSOCIATE // ?
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.GetEipById(self.GetId())
if err != nil {
return err
}
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 {
return time.Unix(self.ExpireTime, 0)
}
func (self *SEip) GetIpAddr() string {
if len(self.EIPAddr) > 1 {
log.Warningf("GetIpAddr %d eip addr found", len(self.EIPAddr))
} else if len(self.EIPAddr) == 0 {
return ""
}
return self.EIPAddr[0].IP
}
func (self *SEip) GetMode() string {
return api.EIP_MODE_STANDALONE_EIP
}
func (self *SEip) GetAssociationType() string {
switch self.Resource.ResourceType {
case "uhost":
return api.EIP_ASSOCIATE_TYPE_SERVER
case "natgw":
return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
case "ulb":
return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
default:
return self.Resource.ResourceType
}
}
// 已绑定的资源类型, 枚举值为: uhost, 云主机natgwNAT网关ulb负载均衡器upm: 物理机; hadoophost: 大数据集群;fortresshost堡垒机udockhost容器udhost私有专区主机vpngwIPSec VPNucdr云灾备dbaudit数据库审计。
func (self *SEip) GetAssociationExternalId() string {
return self.Resource.ResourceID
}
func (self *SEip) GetBandwidth() int {
return self.BandwidthMb
}
func (self *SEip) GetINetworkId() string {
return ""
}
// 弹性IP的计费模式, 枚举值为: "Bandwidth", 带宽计费; "Traffic", 流量计费; "ShareBandwidth",共享带宽模式. 默认为 "Bandwidth".
func (self *SEip) GetInternetChargeType() string {
switch self.PayMode {
case "Bandwidth":
return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
case "Traffic":
return api.EIP_CHARGE_TYPE_BY_TRAFFIC
default:
return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
}
}
func (self *SEip) Delete() error {
return self.region.DeallocateEIP(self.GetId())
}
func (self *SEip) Associate(conf *cloudprovider.AssociateConfig) error {
return self.region.AssociateEip(self.GetId(), conf.InstanceId)
}
func (self *SEip) Dissociate() error {
return self.region.DissociateEip(self.GetId(), self.Resource.ResourceID)
}
func (self *SEip) ChangeBandwidth(bw int) error {
return self.region.UpdateEipBandwidth(self.GetId(), bw)
}
// https://docs.ucloud.cn/api/unet-api/allocate_eip
// 增加共享带宽模式ShareBandwidth
func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
if len(eip.BGPType) == 0 {
if strings.HasPrefix(self.GetId(), "cn-") {
eip.BGPType = "Bgp"
} else {
eip.BGPType = "International"
}
}
params := NewRockbaseParams()
params.Set("OperatorName", eip.BGPType)
params.Set("Bandwidth", eip.BandwidthMbps)
params.Set("Name", eip.Name)
var payMode string
switch eip.ChargeType {
case api.EIP_CHARGE_TYPE_BY_TRAFFIC:
payMode = "Traffic"
case api.EIP_CHARGE_TYPE_BY_BANDWIDTH:
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
}
if len(eips) == 1 {
eip := eips[0]
eip.region = self
eip.Refresh()
return &eip, nil
} else {
return nil, fmt.Errorf("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)
}

View File

@@ -0,0 +1,358 @@
// 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"
"strconv"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/billing"
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.GetInstanceByID(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(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
vmId, err := self._createVM(desc.Name, desc.ExternalImageId, desc.SysDisk, desc.Cpu, desc.MemoryMB, desc.InstanceType, desc.ExternalNetworkId, desc.IpAddr, desc.Description, desc.Password, desc.DataDisks, desc.ExternalSecgroupIds, desc.BillingCycle)
if err != nil {
return nil, err
}
vm, err := self.zone.region.GetInstanceByID(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
CPU int
MemoryMB int
GPU int
}
func ParseInstanceType(instanceType string) (SInstanceType, error) {
i := SInstanceType{}
segs := strings.Split(instanceType, ".")
if len(segs) < 3 {
return i, fmt.Errorf("invalid instance type %s", instanceType)
} else if len(segs) >= 4 {
gpu, err := strconv.Atoi(strings.TrimLeft(segs[3], "g"))
if err != nil {
return i, err
}
i.GPU = gpu
}
cpu, err := strconv.Atoi(strings.TrimLeft(segs[1], "c"))
if err != nil {
return i, err
}
mem, err := strconv.Atoi(strings.TrimLeft(segs[2], "m"))
if err != nil {
return i, err
}
i.UHostType = segs[0]
i.CPU = cpu
i.MemoryMB = mem * 1024
return i, nil
}
func (self *SHost) _createVM(name, imgId string, sysDisk cloudprovider.SDiskInfo, cpu, memMB int, instanceType string,
networkId, ipAddr, desc, passwd string,
dataDisks []cloudprovider.SDiskInfo, secgroupIds []string, bc *billing.SBillingCycle) (string, error) {
// 网络配置及安全组绑定
net, _ := self.zone.region.getNetwork(networkId)
if net == nil {
return "", fmt.Errorf("invalid network ID %s", networkId)
}
if net.wire == nil {
log.Errorf("network's wire is empty")
return "", fmt.Errorf("network's wire is empty")
}
if net.wire.vpc == nil {
log.Errorf("wire's vpc is empty")
return "", fmt.Errorf("wire's vpc is empty")
}
if len(passwd) == 0 {
return "", fmt.Errorf("CreateVM password should not be emtpty")
}
// 镜像及硬盘配置
img, err := self.zone.region.GetImage(imgId)
if err != nil {
log.Errorf("GetImage %s fail %s", imgId, err)
return "", err
}
if img.GetStatus() != api.CACHED_IMAGE_STATUS_ACTIVE {
log.Errorf("image %s status %s, expect %s", imgId, img.GetStatus(), api.CACHED_IMAGE_STATUS_ACTIVE)
return "", fmt.Errorf("image not ready")
}
disks := make([]SDisk, len(dataDisks)+1)
disks[0].SizeGB = int(img.ImageSizeGB)
if sysDisk.SizeGB > 0 && sysDisk.SizeGB > int(img.ImageSizeGB) {
disks[0].SizeGB = sysDisk.SizeGB
}
disks[0].DiskType = sysDisk.StorageType
for i, dataDisk := range dataDisks {
disks[i+1].SizeGB = dataDisk.SizeGB
disks[i+1].DiskType = dataDisk.StorageType
}
// 创建实例
// https://docs.ucloud.cn/api/uhost-api/uhost_type
// https://docs.ucloud.cn/compute/uhost/introduction/uhost/type
var vmId string
i, err := ParseInstanceType(instanceType)
if err != nil {
if cpu <= 0 || memMB <= 0 {
return "", err
} else {
i.UHostType = "N2"
i.CPU = cpu
i.MemoryMB = memMB
}
}
vmId, err = self.zone.region.CreateInstance(name, imgId, i.UHostType, passwd, net.wire.vpc.GetId(), networkId, secgroupIds, self.zone.ZoneId, desc, ipAddr, i.CPU, i.MemoryMB, i.GPU, disks, bc)
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(name, imageId, hostType, password, vpcId, SubnetId string, securityGroupId []string,
zoneId, desc, ipAddr string, cpu, memMB, gpu int, disks []SDisk, bc *billing.SBillingCycle) (string, error) {
params := NewRockbaseParams()
params.Set("Zone", zoneId)
params.Set("ImageId", imageId)
params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
params.Set("LoginMode", "Password")
params.Set("Name", name)
params.Set("UHostType", hostType)
params.Set("CPU", cpu)
params.Set("Memory", memMB)
params.Set("VPCId", vpcId)
params.Set("SubnetId", SubnetId)
for _, id := range securityGroupId {
params.Set("SecurityGroupId", id)
}
if gpu > 0 {
params.Set("GPU", gpu)
}
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", disks[0].DiskType)
params.Set("Disks.0.Size", disks[0].SizeGB)
// data disk
for i, disk := range disks[1:] {
N := i + 1
params.Set(fmt.Sprintf("Disks.%d.IsBoot", N), "False")
params.Set(fmt.Sprintf("Disks.%d.Type", N), disk.DiskType)
params.Set(fmt.Sprintf("Disks.%d.Size", N), disk.SizeGB)
}
type Ret struct {
UHostIds []string
}
ret := Ret{}
err := self.DoAction("CreateUHostInstance", params, &ret)
if err != nil {
return "", err
}
if len(ret.UHostIds) == 1 {
return ret.UHostIds[0], nil
}
return "", fmt.Errorf("CreateInstance %d instance created. %s", len(ret.UHostIds), ret.UHostIds)
}

View File

@@ -0,0 +1,228 @@
// 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/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)
}

View File

@@ -0,0 +1,679 @@
// 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"`
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 {
new, err := self.host.zone.region.GetInstanceByID(self.GetId())
if err != nil {
return err
}
new.host = self.host
return jsonutils.Update(self, new)
}
// 计费模式,枚举值为: 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 self.AutoRenew != "Yes" {
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)
diskIds := make([]string, 0)
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))
} else {
diskIds = append(diskIds, disk.DiskID)
}
}
disks := []SDisk{}
var err error
if len(diskIds) > 0 {
disks, err = self.host.zone.region.GetDisks("", "", diskIds)
if err != nil {
return nil, err
}
}
disks = append(disks, localDisks...)
idisks := make([]cloudprovider.ICloudDisk, len(disks))
for i := 0; i < len(disks); i += 1 {
if disks[i].storage == nil {
var category string
if strings.Contains(disks[i].DiskType, "SSD") {
category = api.STORAGE_ROCKBASE_CLOUD_SSD
} else {
category = api.STORAGE_ROCKBASE_CLOUD_NORMAL
}
storage, err := self.host.zone.getStorageByCategory(category)
if err != nil {
return nil, err
}
disks[i].storage = storage
}
idisks[i] = &disks[i]
// 将系统盘放到第0个位置
if disks[i].GetDiskType() == api.DISK_TYPE_SYS {
idisks[0], idisks[i] = idisks[i], idisks[0]
}
}
return idisks, nil
}
func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
nics := make([]cloudprovider.ICloudNic, 0)
for _, ip := range self.IPSet {
if len(ip.SubnetID) == 0 {
continue
}
nic := SInstanceNic{
instance: self,
ipAddr: ip.IP,
macAddr: ip.MAC,
}
nics = append(nics, &nic)
}
return nics, nil
}
// 国际: InternationBGP: BGP内网: Private
func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
for _, ip := range self.IPSet {
if len(ip.IPId) > 0 {
eip, err := self.host.zone.region.GetEipById(ip.IPId)
if err != nil {
return nil, err
}
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) GetInstanceType() string {
// C1.c8.m24
if strings.HasPrefix(self.HostType, "G") {
return fmt.Sprintf("%s.c%d.m%d.g%d", self.HostType, self.CPU, self.MemoryMB/1014, self.GPU)
} else {
return fmt.Sprintf("%s.c%d.m%d", self.HostType, self.CPU, self.MemoryMB/1014)
}
}
// 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
// 1.请确认在重新安装之前,该实例已被关闭;
// 2.请确认该实例未挂载UDisk
// todo:// 3.将原系统重装为不同类型的系统时(Linux-&gt;Windows),不可选择保留数据盘;
// 4.重装不同版本的系统时(CentOS6-&gt;CentOS7),若选择保留数据盘,请注意数据盘的文件系统格式;
// 5.若主机CPU低于2核不可重装为Windows系统。
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
if len(desc.PublicKey) > 0 {
return "", fmt.Errorf("DeployVM not support assign ssh keypair")
}
if self.GetStatus() != api.VM_READY {
return "", fmt.Errorf("DeployVM instance status %s , expected %s.", self.GetStatus(), api.VM_READY)
}
if len(self.DiskSet) > 1 {
for _, disk := range self.DiskSet {
if disk.Type == "Data" {
err := self.host.zone.region.DetachDisk(self.host.zone.GetId(), self.GetId(), disk.DiskID)
if err != nil {
return "", fmt.Errorf("RebuildRoot detach disk %s", err)
}
defer self.host.zone.region.AttachDisk(self.host.zone.GetId(), self.GetId(), disk.DiskID)
}
}
}
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")
}
disks, err := self.GetIDisks()
if len(disks) > 0 {
return disks[0].GetId(), nil
} else {
return "", fmt.Errorf("RebuildRoot %s", err)
}
}
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) 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,
}
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) // 跳过回收站,直接删除
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)
}

View File

@@ -0,0 +1,56 @@
// 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 ""
}

View File

@@ -0,0 +1,41 @@
// 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{
"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,
"us-den": api.RegionDenver,
"us-ca": api.RegionLosAngeles,
"us-ws": api.RegionWashingtonDC,
"bra-saopaulo": api.RegionSaoPaulo,
"ge-fra": api.RegionFrankfurt,
"uk-london": api.RegionLondon,
"uae-dubai": api.RegionDubai,
"afr-nigeria": api.RegionLagos,
}

View File

@@ -0,0 +1,205 @@
// 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/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) {
if len(networkId) == 0 {
return nil, fmt.Errorf("getNetwork network id should not be empty")
}
networks := make([]SNetwork, 0)
params := NewRockbaseParams()
params.Set("SubnetId", networkId)
err := self.DoListAll("DescribeSubnet", params, &networks)
if err != nil {
return nil, err
}
if len(networks) == 1 {
network := networks[0]
vpc, err := self.GetVpc(network.VpcId)
if err != nil {
return nil, err
}
network.wire = &SWire{vpc: vpc}
return &network, nil
} else if len(networks) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("getNetwork %s %d found", networkId, len(networks))
}
}
// 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)
}

View File

@@ -0,0 +1,64 @@
// 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
}

View File

@@ -0,0 +1,15 @@
// 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 "yunion.io/x/cloudmux/pkg/multicloud/rockbase/provider"

View File

@@ -0,0 +1,195 @@
// 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"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"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 &cloudprovider.SBalanceInfo{
Amount: 0.0,
Currency: "CNY",
Status: api.CLOUD_PROVIDER_HEALTH_NORMAL,
}, cloudprovider.ErrNotSupported
}
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()
}

View File

@@ -0,0 +1,504 @@
// 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.GetInstanceByID(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) GetEipById(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 SEip{}, err
}
if len(eips) == 1 {
eip := eips[0]
eip.region = self
return eip, nil
} else if len(eips) == 0 {
return SEip{}, cloudprovider.ErrNotFound
} else {
return SEip{}, fmt.Errorf("GetEipById %d eip found", len(eips))
}
}
func (self *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) {
eip, err := self.GetEipById(id)
return &eip, err
}
// https://docs.ucloud.cn/api/unet-api/delete_firewall
func (self *SRegion) DeleteSecurityGroup(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
}
func (self *SRegion) GetInstanceByID(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 SInstance{}, err
}
if len(instances) == 1 {
return instances[0], nil
} else if len(instances) == 0 {
return SInstance{}, cloudprovider.ErrNotFound
} else {
return SInstance{}, fmt.Errorf("GetInstanceByID %s %d found.", instanceId, len(instances))
}
}
func (self *SRegion) 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
}

View File

@@ -0,0 +1,389 @@
// 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
}

View File

@@ -0,0 +1,99 @@
// 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
}

View File

@@ -0,0 +1,214 @@
// 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)
}

View File

@@ -0,0 +1,207 @@
// 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)
}

View File

@@ -0,0 +1,157 @@
// 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"
)
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) {
disks, err := self.zone.region.GetDisks(self.zone.GetId(), "", nil)
if err != nil {
return nil, err
}
filtedDisks := make([]SDisk, 0)
for _, disk := range disks {
// ssd 盘
if self.storageType == api.STORAGE_ROCKBASE_CLOUD_SSD && strings.Contains(disk.DiskType, "SSD") {
filtedDisks = append(filtedDisks, disk)
}
// 普通盘
if self.storageType == api.STORAGE_ROCKBASE_CLOUD_NORMAL && !strings.Contains(disk.DiskType, "SSD") {
filtedDisks = append(filtedDisks, disk)
}
}
idisks := make([]cloudprovider.ICloudDisk, len(filtedDisks))
for i := 0; i < len(filtedDisks); i += 1 {
filtedDisks[i].storage = self
idisks[i] = &filtedDisks[i]
}
return idisks, nil
}
func (self *SStorage) GetStorageType() string {
return self.storageType
}
func (self *SStorage) GetMediumType() string {
if self.storageType == api.STORAGE_ROCKBASE_CLOUD_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"
}
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) {
if disk, err := self.zone.region.GetDisk(idStr); err != nil {
return nil, err
} else {
disk.storage = self
return disk, nil
}
}
func (self *SStorage) GetMountPoint() string {
return ""
}
func (self *SStorage) IsSysDiskStore() bool {
return true
}

View File

@@ -0,0 +1,376 @@
// 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)
image.storageCache = self
return &image, err
}
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
}
// https://docs.ucloud.cn/api/uhost-api/create_custom_image
func (self *SRegion) createIImage(snapshotId, imageName, imageDesc string) (string, error) {
return "", cloudprovider.ErrNotSupported
}
/*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
}

View File

@@ -0,0 +1,36 @@
// 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")
}

View File

@@ -0,0 +1,394 @@
// 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
}

View File

@@ -0,0 +1,180 @@
// 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"
)
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, respErr error, result interface{}) error {
if respErr != nil {
return respErr
}
if result == nil {
return nil
}
resultKey := detectResultKey(resp, result)
if len(resultKey) > 0 {
respErr = resp.Unmarshal(result, resultKey)
} else {
respErr = resp.Unmarshal(result)
}
if respErr != nil {
log.Errorf("unmarshal json error %s", respErr)
}
return nil
}
func doListPart(client *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)
return unmarshalResult(resp, err, 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
}

View File

@@ -0,0 +1,119 @@
// 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
}

View File

@@ -0,0 +1,173 @@
// 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
}

View File

@@ -0,0 +1,97 @@
// 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)
}

View File

@@ -0,0 +1,184 @@
// 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"
)
// https://docs.ucloud.cn/api/udisk-api/create_udisk
// UDisk 类型: DataDisk普通数据盘SSDDataDiskSSD数据盘默认值DataDisk
var StorageTypes = []string{
api.STORAGE_ROCKBASE_CLOUD_NORMAL,
api.STORAGE_ROCKBASE_CLOUD_SSD,
api.STORAGE_ROCKBASE_LOCAL_NORMAL, // 本地盘
api.STORAGE_ROCKBASE_LOCAL_SSD, // 本地SSD盘
}
type SZone struct {
multicloud.SResourceBase
RockbaseTags
region *SRegion
host *SHost
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) GetId() string {
return self.ZoneId
}
func (self *SZone) GetName() string {
if len(self.LocalName) > 0 {
return self.LocalName
}
if name, exists := ROCKBASE_ZONE_NAMES[self.GetId()]; exists {
return name
}
return self.GetId()
}
func (self *SZone) GetI18n() cloudprovider.SModelI18nTable {
var en string
if len(self.LocalName) > 0 {
en = self.LocalName
} else if name, exists := ROCKBASE_ZONE_NAMES_EN[self.GetId()]; exists {
en = name
} else {
en = self.GetId()
}
table := cloudprovider.SModelI18nTable{}
table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()).EN(en)
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) {
ret := make([]cloudprovider.ICloudStorage, len(StorageTypes))
for i, sc := range StorageTypes {
ret[i] = &SStorage{zone: self, storageType: sc}
}
return ret, nil
}
func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
storages, err := self.GetIStorages()
if err != nil {
return nil, err
}
for i := range storages {
if storages[i].GetGlobalId() == id {
return storages[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)
}

View File

@@ -19,8 +19,23 @@ import (
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
// ref: https://www.volcengine.com/docs/6534/1131814
var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{
"cn-guangzhou": api.RegionGuangzhou,
"cn-beijing": api.RegionBeijing,
// 华北
"cn-beijing": api.RegionBeijing,
"cn-beijing2": api.RegionBeijing,
"cn-datong": api.RegionJinzhong,
"cn-wulanchabu": api.RegionWulanchabu,
// 华东 / 华南
"cn-shanghai": api.RegionShanghai,
"cn-guangzhou": api.RegionGuangzhou,
// 港澳台
"cn-hongkong": api.RegionHongkong,
"cn-hongkong-pop": api.RegionHongkong,
// 亚太
"ap-southeast-1": api.RegionKualaLumpur,
"ap-southeast-3": api.RegionJakarta,
}