| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827 |
- // 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 ecloud
- import (
- "context"
- "fmt"
- "strconv"
- "strings"
- "time"
- "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"
- )
- // SOpenApiRegion 用于接收 OpenAPI 区域列表返回的单个区域结构
- type SRegion struct {
- cloudprovider.SFakeOnPremiseRegion
- multicloud.SRegion
- multicloud.SNoObjectStorageRegion
- client *SEcloudClient
- RegionId string `json:"regionId"`
- RegionCode string `json:"regionCode"`
- RegionName string `json:"regionName"`
- RegionArea string `json:"regionArea"`
- }
- func (r *SRegion) GetId() string {
- return r.RegionId
- }
- func (r *SRegion) GetName() string {
- return r.RegionName
- }
- func (r *SRegion) GetGlobalId() string {
- return fmt.Sprintf("%s/%s", r.client.GetAccessEnv(), r.RegionId)
- }
- func (r *SRegion) GetStatus() string {
- return api.CLOUD_REGION_STATUS_INSERVER
- }
- func (r *SRegion) Refresh() error {
- return nil
- }
- func (r *SRegion) IsEmulated() bool {
- return false
- }
- func (r *SRegion) GetI18n() cloudprovider.SModelI18nTable {
- table := cloudprovider.SModelI18nTable{}
- return table
- }
- // GetLatitude() float32
- // GetLongitude() float32
- func (r *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
- if info, ok := LatitudeAndLongitude[r.RegionId]; ok {
- return info
- }
- return cloudprovider.SGeographicInfo{}
- }
- func (r *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) {
- zones, err := r.GetZones()
- if err != nil {
- return nil, err
- }
- izones := make([]cloudprovider.ICloudZone, len(zones))
- for i := range zones {
- izones[i] = &zones[i]
- }
- return izones, nil
- }
- func (r *SRegion) GetZones() ([]SZone, error) {
- zones := make([]SZone, 0)
- req := NewOpenApiZoneRequest(r.RegionId, nil)
- if err := r.client.doList(context.Background(), req.Base(), &zones); err != nil {
- return nil, errors.Wrap(err, "GetZones")
- }
- for i := range zones {
- zones[i].region = r
- }
- return zones, nil
- }
- func (r *SRegion) GetVpcs() ([]SVpc, error) {
- req := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/vpc", nil, nil)
- vpcs := make([]SVpc, 0)
- if err := r.client.doList(context.Background(), req.Base(), &vpcs); err != nil {
- return nil, err
- }
- for i := range vpcs {
- vpcs[i].region = r
- }
- return vpcs, nil
- }
- func (r *SRegion) GetVpc(id string) (*SVpc, error) {
- base := NewOpenApiVpcRequest(r.RegionId, fmt.Sprintf("/api/openapi-vpc/customer/v3/vpc/%s", id), nil, nil).Base()
- base.SetMethod("GET")
- resp, err := r.client.request(context.Background(), base)
- if err != nil {
- // 可能传入的是 routerId,再试详情接口 by routerId
- return r.getVpcByRouterId(id)
- }
- vpc := SVpc{}
- if err := resp.Unmarshal(&vpc); err != nil {
- return nil, errors.Wrap(err, "Unmarshal vpc")
- }
- vpc.region = r
- return &vpc, nil
- }
- func (r *SRegion) getVpcByRouterId(routerId string) (*SVpc, error) {
- base := NewOpenApiVpcRequest(r.RegionId, fmt.Sprintf("/api/openapi-vpc/customer/v3/vpc/router/%s", routerId), nil, nil).Base()
- base.SetMethod("GET")
- resp, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, err
- }
- vpc := SVpc{}
- if err := resp.Unmarshal(&vpc); err != nil {
- return nil, errors.Wrap(err, "Unmarshal vpc")
- }
- vpc.region = r
- return &vpc, nil
- }
- // CreateVpc 创建 VPC,OpenAPI POST /api/openapi-vpc/customer/v3/order/create/vpc。
- // name/networkName 须 5-22 位、字母开头;body 使用 vpcOrderCreateBody 包装以兼容网关。
- func (r *SRegion) CreateVpc(opts *cloudprovider.VpcCreateOptions) (*SVpc, error) {
- name := opts.NAME
- if name == "" {
- name = "vpc-default"
- }
- // 规范:必须以字母开头,长度 5-22(数字、字母、下划线)
- if name[0] < 'a' || name[0] > 'z' {
- if name[0] < 'A' || name[0] > 'Z' {
- name = "v" + name
- }
- }
- if len(name) < 5 {
- pad := 5 - len(name)
- if pad > 4 {
- pad = 4
- }
- name = name + "xxxx"[:pad]
- }
- if len(name) > 22 {
- name = name[:22]
- }
- cidr := opts.CIDR
- if cidr == "" {
- cidr = "192.168.0.0/16"
- }
- networkName := name + "-subnet1"
- if len(networkName) > 22 {
- networkName = name + "-s1"
- if len(networkName) > 22 {
- networkName = name[:20] + "-s1"
- }
- }
- poolID := regionIdToPoolId[r.RegionId]
- if poolID == "" {
- poolID = r.RegionId
- }
- inner := jsonutils.NewDict()
- inner.Set("name", jsonutils.NewString(name))
- inner.Set("cidr", jsonutils.NewString(cidr))
- inner.Set("networkName", jsonutils.NewString(networkName))
- inner.Set("region", jsonutils.NewString(poolID))
- inner.Set("specs", jsonutils.NewString("high"))
- inner.Set("networkTypeEnum", jsonutils.NewString("VM"))
- if opts.Desc != "" {
- inner.Set("description", jsonutils.NewString(opts.Desc))
- }
- body := jsonutils.NewDict()
- body.Set("vpcOrderCreateBody", inner)
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/order/create/vpc", nil, body).Base()
- base.SetMethod("POST")
- _, err := r.client.request(context.Background(), base)
- if err != nil {
- if strings.Contains(err.Error(), "该可用区暂无可用的网络资源") || strings.Contains(err.Error(), "THIS_AZ_MUST_AUTH") {
- return nil, errors.Wrap(err, "当前区域/可用区暂无可用网络资源或需开通权限,请尝试其他区域或联系移动云")
- }
- return nil, errors.Wrap(err, "CreateVpc")
- }
- // 创建为订购接口,返回 orderId;轮询列表按名称查找新 VPC
- for retry := 0; retry < 3; retry++ {
- if retry > 0 {
- time.Sleep(time.Duration(3+retry*2) * time.Second)
- }
- vpcs, err := r.GetVpcs()
- if err != nil {
- continue
- }
- for i := range vpcs {
- if vpcs[i].Name == name {
- vpcs[i].region = r
- return &vpcs[i], nil
- }
- }
- }
- return nil, errors.Wrap(errors.ErrNotFound, "created vpc not found in list yet")
- }
- // DeleteVpc 退订 VPC,OpenAPI POST /api/openapi-vpc/customer/v3/order/delete。
- // 参数 idOrRouterId 可为 vpc Id 或 routerId;若为 vpc Id 会先查详情取 routerId 再退订。
- func (r *SRegion) DeleteVpc(idOrRouterId string) error {
- routerId := idOrRouterId
- if vpc, err := r.GetVpc(idOrRouterId); err == nil {
- routerId = vpc.RouterId
- }
- commonBody := jsonutils.NewDict()
- commonBody.Set("resourceId", jsonutils.NewString(routerId))
- commonBody.Set("productType", jsonutils.NewString("router"))
- reqBody := jsonutils.NewDict()
- reqBody.Set("commonMopOrderDeleteVpcBody", commonBody)
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/order/delete", nil, reqBody).Base()
- base.SetMethod("POST")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "DeleteVpc")
- }
- // DeleteNetwork 删除 VPC 下网络(子网),与 ecloudsdkvpc DeleteNetwork 一致。
- func (r *SRegion) DeleteNetwork(networkId string) error {
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/network/"+networkId, nil, nil).Base()
- base.SetMethod("DELETE")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "DeleteNetwork")
- }
- // CreateNetwork 在 VPC 下创建网络(子网),与 ecloudsdkvpc CreateNetwork 一致。
- // networkName 须 5-22 位、字母开头;cidr 如 192.168.1.0/24。
- func (r *SRegion) CreateNetwork(routerId, regionPoolId, networkName, cidr string) (*SNetwork, error) {
- body := jsonutils.NewDict()
- inner := jsonutils.NewDict()
- inner.Set("routerId", jsonutils.NewString(routerId))
- inner.Set("networkName", jsonutils.NewString(networkName))
- inner.Set("networkTypeEnum", jsonutils.NewString("VM"))
- if regionPoolId != "" {
- inner.Set("availabilityZoneHints", jsonutils.NewString(regionPoolId))
- }
- subnet := jsonutils.NewDict()
- subnet.Set("cidr", jsonutils.NewString(cidr))
- subnet.Set("ipVersion", jsonutils.NewString("4"))
- inner.Set("subnets", jsonutils.NewArray(subnet))
- body.Set("createNetworkBody", inner)
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/network", nil, body).Base()
- base.SetMethod("POST")
- data, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, errors.Wrap(err, "CreateNetwork")
- }
- // 响应可能含 body(网络 id)或直接返回网络信息
- var networkId string
- if data.Contains("body") {
- networkId, _ = data.GetString("body")
- }
- if networkId == "" && data.Contains("id") {
- networkId, _ = data.GetString("id")
- }
- if networkId == "" {
- // 通过名称再查一次
- networks, listErr := r.GetNetworks(routerId, "")
- if listErr != nil {
- return nil, errors.Wrapf(listErr, "CreateNetwork succeeded but list networks failed")
- }
- for i := range networks {
- if networks[i].Name == networkName {
- return &networks[i], nil
- }
- }
- return nil, errors.Errorf("CreateNetwork succeeded but could not find created network by name %q", networkName)
- }
- return r.GetNetwork(networkId)
- }
- func (r *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
- vpcs, err := r.GetVpcs()
- if err != nil {
- return nil, err
- }
- ivpcs := make([]cloudprovider.ICloudVpc, len(vpcs))
- for i := range vpcs {
- ivpcs[i] = &vpcs[i]
- }
- return ivpcs, nil
- }
- func (r *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
- // 使用 OpenAPI EIP 公网 IP 列表(含带宽信息):
- // GET /api/openapi-eip/acl/v3/floatingip/listWithBw
- base := NewOpenApiEbsRequest(r.RegionId, "/api/openapi-eip/acl/v3/floatingip/listWithBw", nil, nil).Base()
- eips := make([]SEip, 0, 20)
- if err := r.client.doList(context.Background(), base, &eips); err != nil {
- return nil, err
- }
- ret := make([]cloudprovider.ICloudEIP, len(eips))
- for i := range eips {
- eips[i].region = r
- ret[i] = &eips[i]
- }
- return ret, nil
- }
- func (r *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
- vpc, err := r.GetVpc(id)
- if err != nil {
- return nil, err
- }
- vpc.region = r
- return vpc, nil
- }
- func (r *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) {
- vpc, err := r.CreateVpc(opts)
- if err != nil {
- return nil, err
- }
- return vpc, nil
- }
- func (r *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
- zones, err := r.GetZones()
- if err != nil {
- return nil, err
- }
- for i := 0; i < len(zones); i += 1 {
- if zones[i].GetGlobalId() == id || zones[i].GetId() == id {
- return &zones[i], nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (r *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) {
- return nil, cloudprovider.ErrNotImplemented
- }
- func (r *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
- vm, err := r.GetInstance(id)
- if err != nil {
- return nil, err
- }
- zones, err := r.GetZones()
- if err != nil {
- return nil, err
- }
- for i := range zones {
- if zones[i].ZoneId == vm.ZoneId {
- vm.host = &SHost{
- zone: &zones[i],
- }
- return vm, nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (r *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) {
- return r.GetDisk(id)
- }
- // ResizeDisk 扩容云盘,使用 OpenAPI /api/v2/volume/volume/change/ebs,与 ecloudsdkebs ChangeVolume 一致。
- // newSizeGB 为目标容量(GB),只做直通调用,参数合法性由上层调用方保证。
- func (r *SRegion) ResizeDisk(ctx context.Context, diskId string, newSizeGB int64) error {
- body := jsonutils.NewDict()
- body.Set("size", jsonutils.NewInt(newSizeGB))
- body.Set("changeType", jsonutils.NewString("CHANGE"))
- body.Set("volumeId", jsonutils.NewString(diskId))
- reqBody := jsonutils.NewDict()
- reqBody.Set("changeVolumeBody", body)
- base := NewOpenApiEbsRequest(r.RegionId, "/api/v2/volume/volume/change/ebs", nil, reqBody).Base()
- base.SetMethod("POST")
- _, err := r.client.request(ctx, base)
- return err
- }
- // PreDeleteVolume 退订/删除云盘(预删除),与 ecloudsdkebs PreDeleteResources 一致。
- func (r *SRegion) PreDeleteVolume(volumeId string) error {
- body := jsonutils.NewDict()
- body.Set("resourceId", jsonutils.NewString(volumeId))
- body.Set("resourceType", jsonutils.NewString("VOLUME"))
- reqBody := jsonutils.NewDict()
- reqBody.Set("preDeleteResourcesBody", body)
- base := NewOpenApiEbsRequest(r.RegionId, "/api/ebs/acl/v3/common/resource/preDelete", nil, reqBody).Base()
- base.SetMethod("POST")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "PreDeleteVolume")
- }
- // CreateEbsSnapshot 创建云盘快照,与 ecloudsdkebs CreateSnapshot 一致。
- func (r *SRegion) CreateEbsSnapshot(volumeId, name, description string) (string, error) {
- snapBody := jsonutils.NewDict()
- snapBody.Set("volumeId", jsonutils.NewString(volumeId))
- snapBody.Set("name", jsonutils.NewString(name))
- if description != "" {
- snapBody.Set("description", jsonutils.NewString(description))
- }
- reqBody := jsonutils.NewDict()
- reqBody.Set("createSnapshotBody", snapBody)
- base := NewOpenApiEbsRequest(r.RegionId, "/api/v2/volume/openApi/volumeSnapshot/create", nil, reqBody).Base()
- base.SetMethod("POST")
- data, err := r.client.request(context.Background(), base)
- if err != nil {
- return "", errors.Wrap(err, "CreateEbsSnapshot")
- }
- // 响应可能含 snapshotId 或 id
- if data.Contains("snapshotId") {
- id, _ := data.GetString("snapshotId")
- return id, nil
- }
- if data.Contains("id") {
- id, _ := data.GetString("id")
- return id, nil
- }
- return "", errors.Errorf("CreateEbsSnapshot response has no snapshotId/id: %s", data)
- }
- // DeleteEbsSnapshot 删除云盘快照,与 ecloudsdkebs Deletes 一致。
- func (r *SRegion) DeleteEbsSnapshot(snapshotId string) error {
- base := NewOpenApiEbsRequest(r.RegionId, "/api/ebs/acl/v3/openApi/volumeSnapshot/"+snapshotId, nil, nil).Base()
- base.SetMethod("DELETE")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "DeleteEbsSnapshot")
- }
- func (r *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) {
- izones, err := r.GetIZones()
- if err != nil {
- return nil, err
- }
- iHosts := make([]cloudprovider.ICloudHost, 0, len(izones))
- for i := range izones {
- hosts, err := izones[i].GetIHosts()
- if err != nil {
- return nil, err
- }
- iHosts = append(iHosts, hosts...)
- }
- return iHosts, nil
- }
- func (r *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
- hosts, err := r.GetIHosts()
- if err != nil {
- return nil, err
- }
- for i := range hosts {
- if hosts[i].GetGlobalId() == id {
- return hosts[i], nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (r *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
- iStores := make([]cloudprovider.ICloudStorage, 0)
- izones, err := r.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 (r *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
- istores, err := r.GetIStorages()
- if err != nil {
- return nil, err
- }
- for i := range istores {
- if istores[i].GetGlobalId() == id {
- return istores[i], nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (r *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
- sc := r.getStoragecache()
- return []cloudprovider.ICloudStoragecache{sc}, nil
- }
- func (r *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
- storageCache := r.getStoragecache()
- if storageCache.GetGlobalId() == id {
- return storageCache, nil
- }
- return nil, cloudprovider.ErrNotFound
- }
- type SPoolZoneInfo struct {
- ZoneId string `json:"zoneId"`
- ZoneName string `json:"zoneName"`
- ZoneCode string `json:"zoneCode"`
- }
- type SPoolInfo struct {
- ZoneInfo []SPoolZoneInfo `json:"zoneInfo"`
- PoolId string `json:"poolId"`
- PoolArea string `json:"poolArea"`
- ProductType string `json:"productType"`
- PoolName string `json:"poolName"`
- }
- type SPoolInfosRespBody struct {
- PoolList []SPoolInfo `json:"poolList"`
- }
- func (r *SRegion) GetPoolInfo(productType string) ([]SPoolInfo, error) {
- query := map[string]string{
- "productType": productType,
- }
- base := NewOpenApiEbsRequest(r.RegionId, "/api/ebs/acl/v3/mop/common/getPoolInfo", query, nil).Base()
- base.SetMethod("GET")
- data, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, err
- }
- resp := SPoolInfosRespBody{}
- if err := data.Unmarshal(&resp); err != nil {
- return nil, err
- }
- return resp.PoolList, nil
- }
- // GetStorages 返回当前区域(可选指定可用区)的可用存储列表。
- // 若 zoneCode 为空,则返回所有可用区;否则仅返回指定可用区的存储。
- func (r *SRegion) GetStorages(zoneCode string) ([]SStorage, error) {
- volumeConfig, err := r.GetVolumeConfig()
- if err != nil {
- return nil, err
- }
- ret := make([]SStorage, 0)
- for _, config := range volumeConfig {
- if config.Region == zoneCode {
- ret = append(ret, config)
- }
- }
- ret = append(ret, SStorage{
- StorageType: api.STORAGE_ECLOUD_LOCAL,
- Region: zoneCode,
- })
- return ret, nil
- }
- func (r *SRegion) GetVolumeConfig() ([]SStorage, error) {
- base := NewOpenApiEbsRequest(r.RegionId, "/api/ebs/customer/v3/volume/volumeType/list", nil, nil).Base()
- base.SetMethod("GET")
- data, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, errors.Wrap(err, "GetVolumeConfigs")
- }
- resp := []SStorage{}
- if err := data.Unmarshal(&resp); err != nil {
- return nil, errors.Wrap(err, "Unmarshal volume config")
- }
- return resp, nil
- }
- func (r *SRegion) GetProvider() string {
- return api.CLOUD_PROVIDER_ECLOUD
- }
- func (r *SRegion) GetCapabilities() []string {
- return r.client.GetCapabilities()
- }
- func (r *SRegion) GetClient() *SEcloudClient {
- return r.client
- }
- // NewPlaceholderRegion 返回仅持有 client 的 SRegion,用于无有效 region 时仍可执行 region-list(如 OpenAPI 失败导致列表为空)。
- func NewPlaceholderRegion(client *SEcloudClient, regionId string) *SRegion {
- return &SRegion{RegionId: regionId, client: client}
- }
- func (r *SRegion) FindZone(zoneCode string) (*SZone, error) {
- zones, err := r.GetZones()
- if err != nil {
- return nil, errors.Wrap(err, "unable to GetZones")
- }
- for i := range zones {
- if zones[i].ZoneCode == zoneCode {
- return &zones[i], nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (region *SRegion) GetIVMs() ([]cloudprovider.ICloudVM, error) {
- vms, err := region.GetInstances("", "")
- if err != nil {
- return nil, errors.Wrap(err, "GetVMs")
- }
- ivms := make([]cloudprovider.ICloudVM, len(vms))
- for i := range vms {
- ivms[i] = &vms[i]
- }
- return ivms, nil
- }
- func (r *SRegion) GetSecurityGroups() ([]SSecurityGroup, error) {
- // --- 安全组 OpenAPI(ecloudsdkvpc 路径,使用 console 主机)---
- query := map[string]string{
- "region": r.RegionId,
- }
- req := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/SecurityGroup", query, nil)
- sgs := make([]SSecurityGroup, 0, 8)
- if err := r.client.doList(context.Background(), req.Base(), &sgs); err != nil {
- return nil, err
- }
- for i := range sgs {
- sgs[i].region = r
- }
- return sgs, nil
- }
- // GetSecurityGroup 按 ID 获取安全组,返回底层 SSecurityGroup 结构。
- func (r *SRegion) GetSecurityGroup(id string) (*SSecurityGroup, error) {
- base := NewOpenApiVpcRequest(r.RegionId, fmt.Sprintf("/api/openapi-vpc/customer/v3/SecurityGroup/%s", id), nil, nil).Base()
- base.SetMethod("GET")
- resp, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, err
- }
- sg := SSecurityGroup{}
- if err := resp.Unmarshal(&sg); err != nil {
- return nil, errors.Wrap(err, "Unmarshal security group")
- }
- sg.region = r
- return &sg, nil
- }
- func (r *SRegion) CreateSecurityGroup(opts *cloudprovider.SecurityGroupCreateInput) (*SSecurityGroup, error) {
- name := opts.Name
- if name == "" {
- name = "sg-default"
- }
- poolID := regionIdToPoolId[r.RegionId]
- if poolID == "" {
- poolID = r.RegionId
- }
- // SDK 发送的 Body 为 createSecurityGroupBody 内层,与 VPC 一致
- body := jsonutils.NewDict()
- body.Set("name", jsonutils.NewString(name))
- body.Set("region", jsonutils.NewString(poolID))
- body.Set("type", jsonutils.NewString("VM"))
- if opts.Desc != "" {
- body.Set("description", jsonutils.NewString(opts.Desc))
- }
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/SecurityGroup", nil, body).Base()
- base.SetMethod("POST")
- resp, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, errors.Wrap(err, "CreateSecurityGroup")
- }
- sg := SSecurityGroup{}
- if err := resp.Unmarshal(&sg); err != nil {
- return nil, errors.Wrap(err, "Unmarshal created security group")
- }
- sg.region = r
- return &sg, nil
- }
- func (r *SRegion) DeleteSecurityGroup(id string) error {
- base := NewOpenApiVpcRequest(r.RegionId, fmt.Sprintf("/api/openapi-vpc/customer/v3/SecurityGroup/%s", id), nil, nil).Base()
- base.SetMethod("DELETE")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "DeleteSecurityGroup")
- }
- func (r *SRegion) GetSecurityGroupRules(sgId string) ([]SSecurityGroupRule, error) {
- query := map[string]string{"securityGroupId": sgId, "page": "1", "pageSize": "100"}
- req := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/SecurityGroupRule", query, nil)
- rules := make([]SSecurityGroupRule, 0)
- if err := r.client.doList(context.Background(), req.Base(), &rules); err != nil {
- return nil, err
- }
- for i := range rules {
- rules[i].region = r
- rules[i].SecgroupId = sgId
- }
- return rules, nil
- }
- func (r *SRegion) CreateSecurityGroupRule(sgId string, opts *cloudprovider.SecurityGroupRuleCreateOptions) (*SSecurityGroupRule, error) {
- body := jsonutils.NewDict()
- body.Set("securityGroupId", jsonutils.NewString(sgId))
- body.Set("remoteType", jsonutils.NewString("cidr"))
- body.Set("direction", jsonutils.NewString(string(opts.Direction)))
- proto := strings.ToUpper(opts.Protocol)
- if proto == "" || proto == "ANY" {
- proto = "ANY"
- }
- body.Set("protocol", jsonutils.NewString(proto))
- if opts.CIDR != "" {
- body.Set("remoteIpPrefix", jsonutils.NewString(opts.CIDR))
- }
- if opts.Desc != "" {
- body.Set("description", jsonutils.NewString(opts.Desc))
- }
- minPort, maxPort := parsePorts(opts.Ports)
- if minPort >= 0 {
- body.Set("minPortRange", jsonutils.NewInt(int64(minPort)))
- }
- if maxPort >= 0 {
- body.Set("maxPortRange", jsonutils.NewInt(int64(maxPort)))
- }
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/SecurityGroupRule", nil, body).Base()
- base.SetMethod("POST")
- resp, err := r.client.request(context.Background(), base)
- if err != nil {
- return nil, errors.Wrap(err, "CreateSecurityGroupRule")
- }
- rule := SSecurityGroupRule{}
- if err := resp.Unmarshal(&rule); err != nil {
- return nil, errors.Wrap(err, "Unmarshal created rule")
- }
- rule.region = r
- rule.SecgroupId = sgId
- return &rule, nil
- }
- func (r *SRegion) DeleteSecurityGroupRule(ruleId string) error {
- base := NewOpenApiVpcRequest(r.RegionId, fmt.Sprintf("/api/openapi-vpc/customer/v3/SecurityGroupRule/%s", ruleId), nil, nil).Base()
- base.SetMethod("DELETE")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "DeleteSecurityGroupRule")
- }
- func (r *SRegion) UpdatePortSecurityGroups(portId string, securityGroupIds []string) error {
- body := jsonutils.NewDict()
- body.Set("id", jsonutils.NewString(portId))
- arr := jsonutils.NewArray()
- for _, id := range securityGroupIds {
- arr.Add(jsonutils.NewString(id))
- }
- body.Set("securityGroupIds", arr)
- reqBody := jsonutils.NewDict()
- reqBody.Set("updatePortSecurityGroupsBody", body)
- base := NewOpenApiVpcRequest(r.RegionId, "/api/openapi-vpc/customer/v3/port/portSecurityGroups", nil, reqBody).Base()
- base.SetMethod("PUT")
- _, err := r.client.request(context.Background(), base)
- return errors.Wrap(err, "UpdatePortSecurityGroups")
- }
- func (r *SRegion) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
- sgs, err := r.GetSecurityGroups()
- if err != nil {
- return nil, err
- }
- ret := make([]cloudprovider.ICloudSecurityGroup, len(sgs))
- for i := range sgs {
- ret[i] = &sgs[i]
- }
- return ret, nil
- }
- func (r *SRegion) GetISecurityGroupById(id string) (cloudprovider.ICloudSecurityGroup, error) {
- sg, err := r.GetSecurityGroup(id)
- if err != nil {
- return nil, err
- }
- return sg, nil
- }
- func (r *SRegion) CreateISecurityGroup(opts *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
- sg, err := r.CreateSecurityGroup(opts)
- if err != nil {
- return nil, err
- }
- return sg, nil
- }
- // parsePorts 解析 "80" 或 "80-443" 为 min, max;无法解析返回 -1,-1。
- func parsePorts(ports string) (minPort, maxPort int32) {
- if ports == "" {
- return -1, -1
- }
- parts := strings.Split(ports, "-")
- if len(parts) == 1 {
- p, err := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 32)
- if err != nil {
- return -1, -1
- }
- return int32(p), int32(p)
- }
- if len(parts) == 2 {
- p1, e1 := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 32)
- p2, e2 := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 32)
- if e1 != nil || e2 != nil {
- return -1, -1
- }
- return int32(p1), int32(p2)
- }
- return -1, -1
- }
|