| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package aws
- import (
- "context"
- "fmt"
- "sort"
- "strconv"
- "strings"
- "yunion.io/x/jsonutils"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/utils"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/cloudmux/pkg/multicloud"
- )
- /*
- https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/Welcome.html
- */
- type SElbs struct {
- LoadBalancers []SElb `xml:"LoadBalancers>member"`
- NextMarker string `xml:"NextMarker"`
- }
- type SElb struct {
- multicloud.SLoadbalancerBase
- region *SRegion
- AwsTags
- Type string `xml:"Type"`
- Scheme string `xml:"Scheme"`
- IPAddressType string `xml:"IpAddressType"`
- VpcId string `xml:"VpcId"`
- AvailabilityZones []AvailabilityZone `xml:"AvailabilityZones>member"`
- CreatedTime string `xml:"CreatedTime"`
- CanonicalHostedZoneID string `xml:"CanonicalHostedZoneId"`
- DNSName string `xml:"DNSName"`
- SecurityGroups []string `xml:"SecurityGroups>member"`
- LoadBalancerName string `xml:"LoadBalancerName"`
- State State `xml:"State"`
- LoadBalancerArn string `xml:"LoadBalancerArn"`
- }
- type AvailabilityZone struct {
- LoadBalancerAddresses []LoadBalancerAddress `xml:"LoadBalancerAddresses>member"`
- ZoneName string `xml:"ZoneName"`
- SubnetId string `xml:"SubnetId"`
- }
- type LoadBalancerAddress struct {
- IPAddress string `xml:"IpAddress"`
- AllocationID string `xml:"AllocationId"`
- }
- type State struct {
- Code string `xml:"Code"`
- }
- func (self *SElb) GetId() string {
- return self.LoadBalancerArn
- }
- func (self *SElb) GetName() string {
- return self.LoadBalancerName
- }
- func (self *SElb) GetGlobalId() string {
- return self.GetId()
- }
- func (self *SElb) GetStatus() string {
- switch self.State.Code {
- case "provisioning":
- return api.LB_STATUS_INIT
- case "active":
- return api.LB_STATUS_ENABLED
- case "failed":
- return api.LB_STATUS_START_FAILED
- default:
- return api.LB_STATUS_UNKNOWN
- }
- }
- func (self *SElb) Refresh() error {
- lb, err := self.region.GetLoadBalancer(self.GetId())
- if err != nil {
- return err
- }
- return jsonutils.Update(self, lb)
- }
- func (self *SElb) GetSysTags() map[string]string {
- data := map[string]string{}
- data["loadbalance_type"] = self.Type
- attrs, err := self.region.GetElbAttributes(self.GetId())
- if err != nil {
- return data
- }
- for k, v := range attrs {
- data[k] = v
- }
- return data
- }
- func (self *SElb) GetTags() (map[string]string, error) {
- tagBase, err := self.region.DescribeElbTags(self.LoadBalancerArn)
- if err != nil {
- return nil, errors.Wrap(err, "DescribeElbTags")
- }
- return tagBase.GetTags()
- }
- func (self *SElb) GetAddress() string {
- return self.DNSName
- }
- func (lb *SElb) GetSecurityGroupIds() ([]string, error) {
- return lb.SecurityGroups, nil
- }
- func (self *SElb) GetAddressType() string {
- switch self.Scheme {
- case "internal":
- return api.LB_ADDR_TYPE_INTRANET
- case "internet-facing":
- return api.LB_ADDR_TYPE_INTERNET
- default:
- return api.LB_ADDR_TYPE_INTRANET
- }
- }
- func (self *SElb) GetNetworkType() string {
- return api.LB_NETWORK_TYPE_VPC
- }
- func (self *SElb) GetNetworkIds() []string {
- ret := []string{}
- for i := range self.AvailabilityZones {
- ret = append(ret, self.AvailabilityZones[i].SubnetId)
- }
- return ret
- }
- func (self *SElb) GetVpcId() string {
- return self.VpcId
- }
- func (self *SElb) GetZoneId() string {
- zoneNames := []string{}
- for i := range self.AvailabilityZones {
- zoneNames = append(zoneNames, self.AvailabilityZones[i].ZoneName)
- }
- sort.Strings(zoneNames)
- for i := range zoneNames {
- return zoneNames[i]
- }
- return ""
- }
- func (self *SElb) GetZone1Id() string {
- zoneNames := []string{}
- for i := range self.AvailabilityZones {
- zoneNames = append(zoneNames, self.AvailabilityZones[i].ZoneName)
- }
- sort.Strings(zoneNames)
- for i := range zoneNames {
- if i != 0 {
- return zoneNames[i]
- }
- }
- return ""
- }
- func (self *SElb) GetLoadbalancerSpec() string {
- return self.Type
- }
- func (self *SElb) GetChargeType() string {
- return api.LB_CHARGE_TYPE_BY_TRAFFIC
- }
- func (self *SElb) GetEgressMbps() int {
- return 0
- }
- func (self *SElb) Delete(ctx context.Context) error {
- return self.region.DeleteElb(self.GetId())
- }
- func (self *SElb) Start() error {
- return nil
- }
- func (self *SElb) Stop() error {
- return cloudprovider.ErrNotSupported
- }
- func (self *SElb) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
- ret := []cloudprovider.ICloudLoadbalancerListener{}
- marker := ""
- for {
- part, marker, err := self.region.GetElbListeners(self.LoadBalancerArn, "", marker)
- if err != nil {
- return nil, err
- }
- for i := range part {
- part[i].lb = self
- ret = append(ret, &part[i])
- }
- if len(marker) == 0 || len(part) == 0 {
- break
- }
- }
- return ret, nil
- }
- func (self *SElb) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
- groups, err := self.region.GetElbBackendgroups(self.LoadBalancerArn, "")
- if err != nil {
- return nil, errors.Wrapf(err, "GetElbBackendgroups")
- }
- ret := []cloudprovider.ICloudLoadbalancerBackendGroup{}
- for i := range groups {
- groups[i].lb = self
- ret = append(ret, &groups[i])
- }
- return ret, nil
- }
- func (self *SElb) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
- backendgroup, err := self.region.CreateElbBackendgroup(group)
- if err != nil {
- return nil, errors.Wrap(err, "CreateElbBackendgroup")
- }
- backendgroup.lb = self
- return backendgroup, nil
- }
- func (self *SElb) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
- lbbg, err := self.region.GetElbBackendgroup(groupId)
- if err != nil {
- return nil, err
- }
- lbbg.lb = self
- return lbbg, nil
- }
- func (self *SElb) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListenerCreateOptions) (cloudprovider.ICloudLoadbalancerListener, error) {
- ret, err := self.region.CreateElbListener(self.LoadBalancerArn, listener)
- if err != nil {
- return nil, errors.Wrap(err, "CreateElbListener")
- }
- ret.lb = self
- return ret, nil
- }
- func (self *SElb) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
- lis, err := self.region.GetElbListener(listenerId)
- if err != nil {
- return nil, err
- }
- lis.lb = self
- return lis, nil
- }
- func (self *SElb) GetIEIPs() ([]cloudprovider.ICloudEIP, error) {
- ret := []cloudprovider.ICloudEIP{}
- for _, zone := range self.AvailabilityZones {
- for _, addr := range zone.LoadBalancerAddresses {
- if len(addr.IPAddress) > 0 && strings.Contains(addr.AllocationID, "eip") {
- eip, err := self.region.GetEipByIpAddress(addr.IPAddress)
- if err != nil {
- return nil, err
- }
- ret = append(ret, eip)
- }
- }
- }
- return ret, nil
- }
- func (self *SRegion) DeleteElb(id string) error {
- params := map[string]string{"LoadBalancerArn": id}
- return self.elbRequest("DeleteLoadBalancer", params, nil)
- }
- func (self *SRegion) GetElbBackendgroups(elbId, id string) ([]SElbBackendGroup, error) {
- params := map[string]string{}
- if len(elbId) > 0 {
- params["LoadBalancerArn"] = elbId
- }
- if len(id) > 0 {
- params["TargetGroupArns.member.1"] = id
- }
- ret := []SElbBackendGroup{}
- for {
- part := struct {
- NextMarker string `xml:"NextMarker"`
- TargetGroups []SElbBackendGroup `xml:"TargetGroups>member"`
- }{}
- err := self.elbRequest("DescribeTargetGroups", params, &part)
- if err != nil {
- return nil, errors.Wrapf(err, "DescribeTargetGroups")
- }
- ret = append(ret, part.TargetGroups...)
- if len(part.NextMarker) == 0 || len(part.TargetGroups) == 0 {
- break
- }
- params["Marker"] = part.NextMarker
- }
- return ret, nil
- }
- func (self *SRegion) GetElbBackendgroup(id string) (*SElbBackendGroup, error) {
- groups, err := self.GetElbBackendgroups("", id)
- if err != nil {
- return nil, err
- }
- for i := range groups {
- if groups[i].TargetGroupArn == id {
- return &groups[i], nil
- }
- }
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", id)
- }
- func ToAwsHealthCode(s string) string {
- ret := []string{}
- segs := strings.Split(s, ",")
- for _, seg := range segs {
- if seg == api.LB_HEALTH_CHECK_HTTP_CODE_4xx && !utils.IsInStringArray("400-499", ret) {
- ret = append(ret, "400-499")
- } else if seg == api.LB_HEALTH_CHECK_HTTP_CODE_3xx && !utils.IsInStringArray("300-399", ret) {
- ret = append(ret, "300-399")
- } else if seg == api.LB_HEALTH_CHECK_HTTP_CODE_2xx && !utils.IsInStringArray("200-299", ret) {
- ret = append(ret, "200-299")
- }
- }
- return strings.Join(ret, ",")
- }
- func ToOnecloudHealthCode(s string) string {
- ret := []string{}
- segs := strings.Split(s, ",")
- for _, seg := range segs {
- codes := strings.Split(seg, "-")
- for _, code := range codes {
- c, _ := strconv.Atoi(code)
- if c >= 400 && !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_4xx, ret) {
- ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_4xx)
- } else if c >= 300 && !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_3xx, ret) {
- ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_3xx)
- } else if c >= 200 && !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_2xx, ret) {
- ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_2xx)
- }
- }
- if len(codes) == 2 {
- min, _ := strconv.Atoi(codes[0])
- max, _ := strconv.Atoi(codes[1])
- if min >= 200 && max >= 400 {
- if !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_3xx, ret) {
- ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_3xx)
- }
- }
- }
- }
- return strings.Join(ret, ",")
- }
- // 目前只支持target type :instance
- func (self *SRegion) CreateElbBackendgroup(opts *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) {
- params := map[string]string{
- "Protocol": strings.ToUpper(opts.Protocol),
- "Name": opts.Name,
- "Port": fmt.Sprintf("%d", opts.ListenPort),
- "TargetType": "instance",
- "VpcId": opts.VpcId,
- }
- ret := struct {
- TargetGroups []SElbBackendGroup `xml:"TargetGroups>member"`
- }{}
- err := self.elbRequest("CreateTargetGroup", params, &ret)
- if err != nil {
- return nil, err
- }
- for i := range ret.TargetGroups {
- return &ret.TargetGroups[i], nil
- }
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created")
- }
- func (self *SElb) SetTags(tags map[string]string, replace bool) error {
- return self.region.setElbTags(self.LoadBalancerArn, tags, replace)
- }
- func (self *SRegion) setElbTags(arn string, tags map[string]string, replace bool) error {
- tagBase, err := self.DescribeElbTags(arn)
- if err != nil {
- return errors.Wrapf(err, "DescribeElbTags")
- }
- oldTags, err := tagBase.GetTags()
- if err != nil {
- return errors.Wrap(err, "get tags")
- }
- added, removed := map[string]string{}, map[string]string{}
- for k, v := range tags {
- oldValue, ok := oldTags[k]
- if !ok {
- added[k] = v
- } else if oldValue != v {
- removed[k] = oldValue
- added[k] = v
- }
- }
- if replace {
- for k, v := range oldTags {
- newValue, ok := tags[k]
- if !ok {
- removed[k] = v
- } else if v != newValue {
- added[k] = newValue
- removed[k] = v
- }
- }
- }
- if len(removed) > 0 {
- err = self.RemoveElbTags(arn, removed)
- if err != nil {
- return errors.Wrapf(err, "RemoveElbTags %s", removed)
- }
- }
- if len(added) > 0 {
- return self.AddElbTags(arn, added)
- }
- return nil
- }
- func (self *SRegion) AddElbTags(arn string, tags map[string]string) error {
- params := map[string]string{
- "ResourceArns.member.1": arn,
- }
- idx := 1
- for k, v := range tags {
- params[fmt.Sprintf("Tags.member.%d.Key", idx)] = k
- params[fmt.Sprintf("Tags.member.%d.Value", idx)] = v
- idx++
- }
- ret := struct {
- }{}
- return self.elbRequest("AddTags", params, &ret)
- }
- func (self *SRegion) RemoveElbTags(arn string, tags map[string]string) error {
- params := map[string]string{
- "ResourceArns.member.1": arn,
- }
- idx := 1
- for k := range tags {
- params[fmt.Sprintf("TagKeys.member.%d", idx)] = k
- idx++
- }
- ret := struct {
- }{}
- return self.elbRequest("RemoveTags", params, &ret)
- }
- func (self *SRegion) DescribeElbTags(arn string) (*AwsTags, error) {
- ret := struct {
- TagDescriptions []struct {
- ResourceArn string `xml:"ResourceArn"`
- AwsTags
- } `xml:"TagDescriptions>member"`
- }{}
- err := self.elbRequest("DescribeTags", map[string]string{"ResourceArns.member.1": arn}, &ret)
- if err != nil {
- return nil, errors.Wrapf(err, "DescribeTags")
- }
- for _, res := range ret.TagDescriptions {
- if res.ResourceArn == arn {
- return &res.AwsTags, nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (self *SRegion) GetLoadbalancers(id, marker string) ([]SElb, string, error) {
- ret := &SElbs{}
- params := map[string]string{}
- if len(id) > 0 {
- params["LoadBalancerArns.member.1"] = id
- }
- if len(marker) > 0 {
- params["Marker"] = marker
- }
- err := self.elbRequest("DescribeLoadBalancers", params, ret)
- if err != nil {
- return nil, "", errors.Wrapf(err, "DescribeLoadBalancers")
- }
- return ret.LoadBalancers, ret.NextMarker, nil
- }
- func (self *SRegion) CreateLoadbalancer(opts *cloudprovider.SLoadbalancerCreateOptions) (*SElb, error) {
- ret := &SElbs{}
- params := map[string]string{
- "Name": opts.Name,
- "Type": opts.LoadbalancerSpec,
- "Scheme": "internal",
- "IpAddressType": "ipv4",
- }
- if opts.AddressType == api.LB_ADDR_TYPE_INTERNET {
- params["Scheme"] = "internet-facing"
- }
- if opts.LoadbalancerSpec == api.LB_AWS_SPEC_APPLICATION && len(opts.NetworkIds) == 1 {
- nets, err := self.GetNetwroks(nil, opts.ZoneId, opts.VpcId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetNetworks(%s)", opts.VpcId)
- }
- for i := range nets {
- if !utils.IsInStringArray(nets[i].SubnetId, opts.NetworkIds) {
- opts.NetworkIds = append(opts.NetworkIds, nets[i].SubnetId)
- break
- }
- }
- }
- for i, net := range opts.NetworkIds {
- params[fmt.Sprintf("Subnets.member.%d", i+1)] = net
- }
- idx := 1
- for k, v := range opts.Tags {
- params[fmt.Sprintf("Tags.member.%d.Key", idx)] = k
- params[fmt.Sprintf("Tags.member.%d.Value", idx)] = v
- idx++
- }
- err := self.elbRequest("CreateLoadBalancer", params, ret)
- if err != nil {
- return nil, errors.Wrapf(err, "CreateLoadBalancer")
- }
- for i := range ret.LoadBalancers {
- ret.LoadBalancers[i].region = self
- return &ret.LoadBalancers[i], nil
- }
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created")
- }
- func (self *SElb) GetDescription() string {
- tags, _ := self.region.DescribeElbTags(self.LoadBalancerArn)
- if tags == nil {
- return ""
- }
- return tags.GetDescription()
- }
|