Files
cloudpods/pkg/util/ucloud/region.go
T

625 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 ucloud
import (
"fmt"
"net"
"sort"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/secrules"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SRegion struct {
multicloud.SRegion
client *SUcloudClient
RegionID string
izones []cloudprovider.ICloudZone
ivpcs []cloudprovider.ICloudVpc
storageCache *SStoragecache
latitude float64
longitude float64
fetchLocation bool
}
func (self *SRegion) GetId() string {
return self.RegionID
}
func (self *SRegion) GetName() string {
if name, exist := UCLOUD_REGION_NAMES[self.GetId()]; exist {
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_UCLOUD_CN, name)
}
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_UCLOUD_CN, self.GetId())
}
func (self *SRegion) GetGlobalId() string {
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_UCLOUD, self.GetId())
}
func (self *SRegion) GetStatus() string {
return api.CLOUD_REGION_STATUS_INSERVER
}
func (self *SRegion) Refresh() error {
return nil
}
func (self *SRegion) IsEmulated() bool {
return false
}
func (self *SRegion) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
if info, ok := LatitudeAndLongitude[self.GetId()]; ok {
return info
}
return cloudprovider.SGeographicInfo{}
}
func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) {
if self.izones == nil {
var err error
err = self.fetchInfrastructure()
if err != nil {
return nil, err
}
}
return self.izones, nil
}
func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
if self.ivpcs == nil {
err := self.fetchInfrastructure()
if err != nil {
return nil, err
}
}
return self.ivpcs, nil
}
// https://docs.ucloud.cn/api/unet-api/describe_eip
func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
params := NewUcloudParams()
eips := make([]SEip, 0)
err := self.DoListAll("DescribeEIP", params, &eips)
if err != nil {
return nil, err
}
ieips := []cloudprovider.ICloudEIP{}
for 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) {
ivpcs, err := self.GetIVpcs()
if err != nil {
return nil, err
}
for i := 0; i < len(ivpcs); i += 1 {
if ivpcs[i].GetGlobalId() == id {
return ivpcs[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
if izones[i].GetGlobalId() == id {
return izones[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetEipById(eipId string) (SEip, error) {
params := NewUcloudParams()
params.Set("EIPIds.0", eipId)
eips := make([]SEip, 0)
err := self.DoListAll("DescribeEIP", params, &eips)
if err != nil {
return SEip{}, err
}
if len(eips) == 1 {
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(vpcId, secgroupId string) error {
params := NewUcloudParams()
params.Set("FWId", secgroupId)
return self.DoAction("DeleteFirewall", params, nil)
}
// https://docs.ucloud.cn/api/unet-api/describe_firewall
// 绑定防火墙组的资源类型,默认为全部资源类型。枚举值为:"unatgw"NAT网关; "uhost",云主机; "upm",物理云主机; "hadoophost"hadoop节点; "fortresshost",堡垒机; "udhost",私有专区主机;"udockhost",容器;"dbaudit",数据库审计.
// todo: 是否需要过滤出仅绑定云主机的安全组?
func (self *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) {
if len(secgroupId) > 0 {
_, err := self.GetSecurityGroupById(secgroupId)
if err == cloudprovider.ErrNotFound {
secgroupId = ""
} else if err != nil {
return "", err
}
}
if len(secgroupId) == 0 {
extID, err := self.CreateDefaultSecurityGroup(name, desc)
if err != nil {
return "", err
}
secgroupId = extID
}
// 如果是空规则,onecloud。默认拒绝所有流量
if len(rules) == 0 {
_, IpNet, _ := net.ParseCIDR("0.0.0.0/0")
rules = []secrules.SecurityRule{{
Priority: 0,
Action: secrules.SecurityRuleDeny,
IPNet: IpNet,
Protocol: secrules.PROTO_ANY,
Direction: secrules.SecurityRuleIngress,
PortStart: 0,
PortEnd: 0,
Ports: nil,
Description: "",
}}
}
return secgroupId, self.syncSecgroupRules(secgroupId, rules)
}
func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) {
params := NewUcloudParams()
params.Set("Name", name)
params.Set("Remark", desc)
for i, cidr := range strings.Split(cidr, ",") {
params.Set(fmt.Sprintf("Network.%d", i), cidr)
}
vpcId := ""
err := self.DoAction("CreateVPC", params, &vpcId)
if err != nil {
return nil, err
}
return self.GetIVpcById(vpcId)
}
// https://docs.ucloud.cn/api/udisk-api/describe_udisk_snapshot
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
params := NewUcloudParams()
snapshots := make([]SSnapshot, 0)
err := self.DoListAll("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
isnapshots := make([]cloudprovider.ICloudSnapshot, 0)
for _, snapshot := range snapshots {
snapshot.region = self
isnapshots = append(isnapshots, &snapshot)
}
return isnapshots, nil
}
func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
params := NewUcloudParams()
params.Set("SnapshotId", snapshotId)
snapshots := make([]SSnapshot, 0)
err := self.DoListAll("DescribeUDiskSnapshot", params, &snapshots)
if err != nil {
return nil, err
}
if len(snapshots) == 1 {
snapshot := snapshots[0]
snapshot.region = self
return &snapshot, nil
} else if len(snapshots) == 0 {
return nil, cloudprovider.ErrNotFound
} else {
return nil, fmt.Errorf("GetISnapshotById %s %d snapshot found", snapshotId, len(snapshots))
}
}
func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) {
iHosts := make([]cloudprovider.ICloudHost, 0)
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
iZoneHost, err := izones[i].GetIHosts()
if err != nil {
return nil, err
}
iHosts = append(iHosts, iZoneHost...)
}
return iHosts, nil
}
func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
ihost, err := izones[i].GetIHostById(id)
if err == nil {
return ihost, nil
} else if err != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
iStores := make([]cloudprovider.ICloudStorage, 0)
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
iZoneStores, err := izones[i].GetIStorages()
if err != nil {
return nil, err
}
iStores = append(iStores, iZoneStores...)
}
return iStores, nil
}
func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
izones, err := self.GetIZones()
if err != nil {
return nil, err
}
for i := 0; i < len(izones); i += 1 {
istore, err := izones[i].GetIStorageById(id)
if err == nil {
return istore, nil
} else if err != cloudprovider.ErrNotFound {
return nil, err
}
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) getStoragecache() *SStoragecache {
if self.storageCache == nil {
self.storageCache = &SStoragecache{region: self}
}
return self.storageCache
}
func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
storageCache := self.getStoragecache()
return []cloudprovider.ICloudStoragecache{storageCache}, nil
}
func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
storageCache := self.getStoragecache()
if storageCache.GetGlobalId() == id {
return storageCache, nil
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SRegion) GetProvider() string {
return CLOUD_PROVIDER_UCLOUD
}
func (self *SRegion) DoListAll(action string, params SParams, result interface{}) error {
params.Set("Region", self.GetId())
return self.client.DoListAll(action, params, result)
}
// return total,lenght,error
func (self *SRegion) DoListPart(action string, limit int, offset int, params SParams, result interface{}) (int, int, error) {
params.Set("Region", self.GetId())
return self.client.DoListPart(action, limit, offset, params, result)
}
func (self *SRegion) DoAction(action string, params SParams, result interface{}) error {
params.Set("Region", self.GetId())
return self.client.DoAction(action, params, result)
}
func (self *SRegion) fetchInfrastructure() error {
if err := self.fetchZones(); err != nil {
return err
}
if err := self.fetchIVpcs(); err != nil {
return err
}
for i := 0; i < len(self.ivpcs); i += 1 {
vpc := self.ivpcs[i].(*SVPC)
wire := SWire{region: self, vpc: vpc}
vpc.addWire(&wire)
for j := 0; j < len(self.izones); j += 1 {
zone := self.izones[j].(*SZone)
zone.addWire(&wire)
}
}
return nil
}
func (self *SRegion) fetchZones() error {
type Region struct {
RegionID int64 `json:"RegionId"`
RegionName string `json:"RegionName"`
IsDefault bool `json:"IsDefault"`
BitMaps string `json:"BitMaps"`
Region string `json:"Region"`
Zone string `json:"Zone"`
}
params := NewUcloudParams()
regions := make([]Region, 0)
err := self.client.DoListAll("GetRegion", params, &regions)
if err != nil {
return err
}
for _, r := range regions {
if r.Region != self.GetId() {
continue
}
szone := SZone{}
szone.ZoneId = r.Zone
szone.RegionId = r.Region
szone.region = self
self.izones = append(self.izones, &szone)
}
return nil
}
func (self *SRegion) fetchIVpcs() error {
vpcs := make([]SVPC, 0)
params := NewUcloudParams()
err := self.DoListAll("DescribeVPC", params, &vpcs)
if err != nil {
return err
}
for i := range vpcs {
vpc := vpcs[i]
vpc.region = self
self.ivpcs = append(self.ivpcs, &vpc)
}
return nil
}
// https://docs.ucloud.cn/api/uhost-api/describe_uhost_instance
func (self *SRegion) GetInstanceByID(instanceId string) (SInstance, error) {
params := NewUcloudParams()
params.Set("UHostIds.0", instanceId)
instances := make([]SInstance, 0)
err := self.DoAction("DescribeUHostInstance", params, &instances)
if err != nil {
return SInstance{}, err
}
if len(instances) == 1 {
return instances[0], nil
} else if len(instances) == 0 {
return SInstance{}, cloudprovider.ErrNotFound
} else {
return SInstance{}, fmt.Errorf("GetInstanceByID %s %d found.", instanceId, len(instances))
}
}
func inList(s int, lst []int) bool {
for _, e := range lst {
if s == e {
return true
}
}
return false
}
func toUcloudSecurityRules(rules []secrules.SecurityRule) ([]string, error) {
ps := make([]int, 0)
for _, rule := range rules {
if rule.Direction == secrules.SecurityRuleIngress && !inList(rule.Priority, ps) {
ps = append(ps, rule.Priority)
}
}
if len(ps) > 3 {
return nil, fmt.Errorf("unable map local security group rule priority %v to LOW/MEDIUM/LOW", ps)
}
sort.Ints(ps)
pmap := map[int]string{}
for i, p := range ps {
pmap[p] = []string{"LOW", "MEDIUM", "HIGH"}[i]
}
ucloudRules := make([]string, 0)
for _, rule := range rules {
if rule.Direction == secrules.SecurityRuleIngress {
// GRE协议被忽略了
ucloudRules = append(ucloudRules, toUcloudSecRule(rule, pmap)...)
}
}
return ucloudRules, nil
}
// GRE协议被忽略了
func toUcloudSecRule(rule secrules.SecurityRule, pmap map[int]string) []string {
net := rule.IPNet.String()
priority := pmap[rule.Priority]
action := "DROP"
if rule.Action == secrules.SecurityRuleAllow {
action = "ACCEPT"
}
rules := make([]string, 0)
if len(rule.Ports) > 0 {
for _, port := range rule.Ports {
_rules := generatorRule(rule.Protocol, priority, net, action, port, port)
rules = append(rules, _rules...)
}
} else {
_rules := generatorRule(rule.Protocol, priority, net, action, rule.PortStart, rule.PortEnd)
rules = append(rules, _rules...)
}
return rules
}
func generatorRule(protocol, priority, net, action string, startPort, endPort int) []string {
rules := make([]string, 0)
var ports string
if startPort <= 0 || endPort <= 0 {
ports = "1-65535"
} else if startPort == endPort {
ports = fmt.Sprintf("%d", startPort)
} else {
ports = fmt.Sprintf("%d-%d", startPort, endPort)
}
template := fmt.Sprintf("%s|%s|%s|%s|%s|", "%s", "%s", net, action, priority)
switch protocol {
case secrules.PROTO_ANY:
rules = append(rules, fmt.Sprintf(template, "TCP", ports))
rules = append(rules, fmt.Sprintf(template, "UDP", ports))
rules = append(rules, fmt.Sprintf(template, "ICMP", ""))
case secrules.PROTO_TCP:
rules = append(rules, fmt.Sprintf(template, "TCP", ports))
case secrules.PROTO_UDP:
rules = append(rules, fmt.Sprintf(template, "UDP", ports))
case secrules.PROTO_ICMP:
rules = append(rules, fmt.Sprintf(template, "ICMP", ""))
}
return rules
}
// https://docs.ucloud.cn/api/unet-api/update_firewall
func (self *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.SecurityRule) error {
_rules, err := toUcloudSecurityRules(rules)
if err != nil {
return err
}
params := NewUcloudParams()
params.Set("FWId", secgroupId)
for i, r := range _rules {
N := i + 1
params.Set(fmt.Sprintf("Rule.%d", N), r)
}
return self.DoAction("UpdateFirewall", params, nil)
}
func (self *SRegion) GetClient() *SUcloudClient {
return self.client
}