loadbalancer.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package aws
  15. import (
  16. "context"
  17. "fmt"
  18. "sort"
  19. "strconv"
  20. "strings"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/utils"
  24. api "yunion.io/x/cloudmux/pkg/apis/compute"
  25. "yunion.io/x/cloudmux/pkg/cloudprovider"
  26. "yunion.io/x/cloudmux/pkg/multicloud"
  27. )
  28. /*
  29. https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/Welcome.html
  30. */
  31. type SElbs struct {
  32. LoadBalancers []SElb `xml:"LoadBalancers>member"`
  33. NextMarker string `xml:"NextMarker"`
  34. }
  35. type SElb struct {
  36. multicloud.SLoadbalancerBase
  37. region *SRegion
  38. AwsTags
  39. Type string `xml:"Type"`
  40. Scheme string `xml:"Scheme"`
  41. IPAddressType string `xml:"IpAddressType"`
  42. VpcId string `xml:"VpcId"`
  43. AvailabilityZones []AvailabilityZone `xml:"AvailabilityZones>member"`
  44. CreatedTime string `xml:"CreatedTime"`
  45. CanonicalHostedZoneID string `xml:"CanonicalHostedZoneId"`
  46. DNSName string `xml:"DNSName"`
  47. SecurityGroups []string `xml:"SecurityGroups>member"`
  48. LoadBalancerName string `xml:"LoadBalancerName"`
  49. State State `xml:"State"`
  50. LoadBalancerArn string `xml:"LoadBalancerArn"`
  51. }
  52. type AvailabilityZone struct {
  53. LoadBalancerAddresses []LoadBalancerAddress `xml:"LoadBalancerAddresses>member"`
  54. ZoneName string `xml:"ZoneName"`
  55. SubnetId string `xml:"SubnetId"`
  56. }
  57. type LoadBalancerAddress struct {
  58. IPAddress string `xml:"IpAddress"`
  59. AllocationID string `xml:"AllocationId"`
  60. }
  61. type State struct {
  62. Code string `xml:"Code"`
  63. }
  64. func (self *SElb) GetId() string {
  65. return self.LoadBalancerArn
  66. }
  67. func (self *SElb) GetName() string {
  68. return self.LoadBalancerName
  69. }
  70. func (self *SElb) GetGlobalId() string {
  71. return self.GetId()
  72. }
  73. func (self *SElb) GetStatus() string {
  74. switch self.State.Code {
  75. case "provisioning":
  76. return api.LB_STATUS_INIT
  77. case "active":
  78. return api.LB_STATUS_ENABLED
  79. case "failed":
  80. return api.LB_STATUS_START_FAILED
  81. default:
  82. return api.LB_STATUS_UNKNOWN
  83. }
  84. }
  85. func (self *SElb) Refresh() error {
  86. lb, err := self.region.GetLoadBalancer(self.GetId())
  87. if err != nil {
  88. return err
  89. }
  90. return jsonutils.Update(self, lb)
  91. }
  92. func (self *SElb) GetSysTags() map[string]string {
  93. data := map[string]string{}
  94. data["loadbalance_type"] = self.Type
  95. attrs, err := self.region.GetElbAttributes(self.GetId())
  96. if err != nil {
  97. return data
  98. }
  99. for k, v := range attrs {
  100. data[k] = v
  101. }
  102. return data
  103. }
  104. func (self *SElb) GetTags() (map[string]string, error) {
  105. tagBase, err := self.region.DescribeElbTags(self.LoadBalancerArn)
  106. if err != nil {
  107. return nil, errors.Wrap(err, "DescribeElbTags")
  108. }
  109. return tagBase.GetTags()
  110. }
  111. func (self *SElb) GetAddress() string {
  112. return self.DNSName
  113. }
  114. func (lb *SElb) GetSecurityGroupIds() ([]string, error) {
  115. return lb.SecurityGroups, nil
  116. }
  117. func (self *SElb) GetAddressType() string {
  118. switch self.Scheme {
  119. case "internal":
  120. return api.LB_ADDR_TYPE_INTRANET
  121. case "internet-facing":
  122. return api.LB_ADDR_TYPE_INTERNET
  123. default:
  124. return api.LB_ADDR_TYPE_INTRANET
  125. }
  126. }
  127. func (self *SElb) GetNetworkType() string {
  128. return api.LB_NETWORK_TYPE_VPC
  129. }
  130. func (self *SElb) GetNetworkIds() []string {
  131. ret := []string{}
  132. for i := range self.AvailabilityZones {
  133. ret = append(ret, self.AvailabilityZones[i].SubnetId)
  134. }
  135. return ret
  136. }
  137. func (self *SElb) GetVpcId() string {
  138. return self.VpcId
  139. }
  140. func (self *SElb) GetZoneId() string {
  141. zoneNames := []string{}
  142. for i := range self.AvailabilityZones {
  143. zoneNames = append(zoneNames, self.AvailabilityZones[i].ZoneName)
  144. }
  145. sort.Strings(zoneNames)
  146. for i := range zoneNames {
  147. return zoneNames[i]
  148. }
  149. return ""
  150. }
  151. func (self *SElb) GetZone1Id() string {
  152. zoneNames := []string{}
  153. for i := range self.AvailabilityZones {
  154. zoneNames = append(zoneNames, self.AvailabilityZones[i].ZoneName)
  155. }
  156. sort.Strings(zoneNames)
  157. for i := range zoneNames {
  158. if i != 0 {
  159. return zoneNames[i]
  160. }
  161. }
  162. return ""
  163. }
  164. func (self *SElb) GetLoadbalancerSpec() string {
  165. return self.Type
  166. }
  167. func (self *SElb) GetChargeType() string {
  168. return api.LB_CHARGE_TYPE_BY_TRAFFIC
  169. }
  170. func (self *SElb) GetEgressMbps() int {
  171. return 0
  172. }
  173. func (self *SElb) Delete(ctx context.Context) error {
  174. return self.region.DeleteElb(self.GetId())
  175. }
  176. func (self *SElb) Start() error {
  177. return nil
  178. }
  179. func (self *SElb) Stop() error {
  180. return cloudprovider.ErrNotSupported
  181. }
  182. func (self *SElb) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
  183. ret := []cloudprovider.ICloudLoadbalancerListener{}
  184. marker := ""
  185. for {
  186. part, marker, err := self.region.GetElbListeners(self.LoadBalancerArn, "", marker)
  187. if err != nil {
  188. return nil, err
  189. }
  190. for i := range part {
  191. part[i].lb = self
  192. ret = append(ret, &part[i])
  193. }
  194. if len(marker) == 0 || len(part) == 0 {
  195. break
  196. }
  197. }
  198. return ret, nil
  199. }
  200. func (self *SElb) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  201. groups, err := self.region.GetElbBackendgroups(self.LoadBalancerArn, "")
  202. if err != nil {
  203. return nil, errors.Wrapf(err, "GetElbBackendgroups")
  204. }
  205. ret := []cloudprovider.ICloudLoadbalancerBackendGroup{}
  206. for i := range groups {
  207. groups[i].lb = self
  208. ret = append(ret, &groups[i])
  209. }
  210. return ret, nil
  211. }
  212. func (self *SElb) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  213. backendgroup, err := self.region.CreateElbBackendgroup(group)
  214. if err != nil {
  215. return nil, errors.Wrap(err, "CreateElbBackendgroup")
  216. }
  217. backendgroup.lb = self
  218. return backendgroup, nil
  219. }
  220. func (self *SElb) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
  221. lbbg, err := self.region.GetElbBackendgroup(groupId)
  222. if err != nil {
  223. return nil, err
  224. }
  225. lbbg.lb = self
  226. return lbbg, nil
  227. }
  228. func (self *SElb) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListenerCreateOptions) (cloudprovider.ICloudLoadbalancerListener, error) {
  229. ret, err := self.region.CreateElbListener(self.LoadBalancerArn, listener)
  230. if err != nil {
  231. return nil, errors.Wrap(err, "CreateElbListener")
  232. }
  233. ret.lb = self
  234. return ret, nil
  235. }
  236. func (self *SElb) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
  237. lis, err := self.region.GetElbListener(listenerId)
  238. if err != nil {
  239. return nil, err
  240. }
  241. lis.lb = self
  242. return lis, nil
  243. }
  244. func (self *SElb) GetIEIPs() ([]cloudprovider.ICloudEIP, error) {
  245. ret := []cloudprovider.ICloudEIP{}
  246. for _, zone := range self.AvailabilityZones {
  247. for _, addr := range zone.LoadBalancerAddresses {
  248. if len(addr.IPAddress) > 0 && strings.Contains(addr.AllocationID, "eip") {
  249. eip, err := self.region.GetEipByIpAddress(addr.IPAddress)
  250. if err != nil {
  251. return nil, err
  252. }
  253. ret = append(ret, eip)
  254. }
  255. }
  256. }
  257. return ret, nil
  258. }
  259. func (self *SRegion) DeleteElb(id string) error {
  260. params := map[string]string{"LoadBalancerArn": id}
  261. return self.elbRequest("DeleteLoadBalancer", params, nil)
  262. }
  263. func (self *SRegion) GetElbBackendgroups(elbId, id string) ([]SElbBackendGroup, error) {
  264. params := map[string]string{}
  265. if len(elbId) > 0 {
  266. params["LoadBalancerArn"] = elbId
  267. }
  268. if len(id) > 0 {
  269. params["TargetGroupArns.member.1"] = id
  270. }
  271. ret := []SElbBackendGroup{}
  272. for {
  273. part := struct {
  274. NextMarker string `xml:"NextMarker"`
  275. TargetGroups []SElbBackendGroup `xml:"TargetGroups>member"`
  276. }{}
  277. err := self.elbRequest("DescribeTargetGroups", params, &part)
  278. if err != nil {
  279. return nil, errors.Wrapf(err, "DescribeTargetGroups")
  280. }
  281. ret = append(ret, part.TargetGroups...)
  282. if len(part.NextMarker) == 0 || len(part.TargetGroups) == 0 {
  283. break
  284. }
  285. params["Marker"] = part.NextMarker
  286. }
  287. return ret, nil
  288. }
  289. func (self *SRegion) GetElbBackendgroup(id string) (*SElbBackendGroup, error) {
  290. groups, err := self.GetElbBackendgroups("", id)
  291. if err != nil {
  292. return nil, err
  293. }
  294. for i := range groups {
  295. if groups[i].TargetGroupArn == id {
  296. return &groups[i], nil
  297. }
  298. }
  299. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", id)
  300. }
  301. func ToAwsHealthCode(s string) string {
  302. ret := []string{}
  303. segs := strings.Split(s, ",")
  304. for _, seg := range segs {
  305. if seg == api.LB_HEALTH_CHECK_HTTP_CODE_4xx && !utils.IsInStringArray("400-499", ret) {
  306. ret = append(ret, "400-499")
  307. } else if seg == api.LB_HEALTH_CHECK_HTTP_CODE_3xx && !utils.IsInStringArray("300-399", ret) {
  308. ret = append(ret, "300-399")
  309. } else if seg == api.LB_HEALTH_CHECK_HTTP_CODE_2xx && !utils.IsInStringArray("200-299", ret) {
  310. ret = append(ret, "200-299")
  311. }
  312. }
  313. return strings.Join(ret, ",")
  314. }
  315. func ToOnecloudHealthCode(s string) string {
  316. ret := []string{}
  317. segs := strings.Split(s, ",")
  318. for _, seg := range segs {
  319. codes := strings.Split(seg, "-")
  320. for _, code := range codes {
  321. c, _ := strconv.Atoi(code)
  322. if c >= 400 && !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_4xx, ret) {
  323. ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_4xx)
  324. } else if c >= 300 && !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_3xx, ret) {
  325. ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_3xx)
  326. } else if c >= 200 && !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_2xx, ret) {
  327. ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_2xx)
  328. }
  329. }
  330. if len(codes) == 2 {
  331. min, _ := strconv.Atoi(codes[0])
  332. max, _ := strconv.Atoi(codes[1])
  333. if min >= 200 && max >= 400 {
  334. if !utils.IsInStringArray(api.LB_HEALTH_CHECK_HTTP_CODE_3xx, ret) {
  335. ret = append(ret, api.LB_HEALTH_CHECK_HTTP_CODE_3xx)
  336. }
  337. }
  338. }
  339. }
  340. return strings.Join(ret, ",")
  341. }
  342. // 目前只支持target type :instance
  343. func (self *SRegion) CreateElbBackendgroup(opts *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) {
  344. params := map[string]string{
  345. "Protocol": strings.ToUpper(opts.Protocol),
  346. "Name": opts.Name,
  347. "Port": fmt.Sprintf("%d", opts.ListenPort),
  348. "TargetType": "instance",
  349. "VpcId": opts.VpcId,
  350. }
  351. ret := struct {
  352. TargetGroups []SElbBackendGroup `xml:"TargetGroups>member"`
  353. }{}
  354. err := self.elbRequest("CreateTargetGroup", params, &ret)
  355. if err != nil {
  356. return nil, err
  357. }
  358. for i := range ret.TargetGroups {
  359. return &ret.TargetGroups[i], nil
  360. }
  361. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created")
  362. }
  363. func (self *SElb) SetTags(tags map[string]string, replace bool) error {
  364. return self.region.setElbTags(self.LoadBalancerArn, tags, replace)
  365. }
  366. func (self *SRegion) setElbTags(arn string, tags map[string]string, replace bool) error {
  367. tagBase, err := self.DescribeElbTags(arn)
  368. if err != nil {
  369. return errors.Wrapf(err, "DescribeElbTags")
  370. }
  371. oldTags, err := tagBase.GetTags()
  372. if err != nil {
  373. return errors.Wrap(err, "get tags")
  374. }
  375. added, removed := map[string]string{}, map[string]string{}
  376. for k, v := range tags {
  377. oldValue, ok := oldTags[k]
  378. if !ok {
  379. added[k] = v
  380. } else if oldValue != v {
  381. removed[k] = oldValue
  382. added[k] = v
  383. }
  384. }
  385. if replace {
  386. for k, v := range oldTags {
  387. newValue, ok := tags[k]
  388. if !ok {
  389. removed[k] = v
  390. } else if v != newValue {
  391. added[k] = newValue
  392. removed[k] = v
  393. }
  394. }
  395. }
  396. if len(removed) > 0 {
  397. err = self.RemoveElbTags(arn, removed)
  398. if err != nil {
  399. return errors.Wrapf(err, "RemoveElbTags %s", removed)
  400. }
  401. }
  402. if len(added) > 0 {
  403. return self.AddElbTags(arn, added)
  404. }
  405. return nil
  406. }
  407. func (self *SRegion) AddElbTags(arn string, tags map[string]string) error {
  408. params := map[string]string{
  409. "ResourceArns.member.1": arn,
  410. }
  411. idx := 1
  412. for k, v := range tags {
  413. params[fmt.Sprintf("Tags.member.%d.Key", idx)] = k
  414. params[fmt.Sprintf("Tags.member.%d.Value", idx)] = v
  415. idx++
  416. }
  417. ret := struct {
  418. }{}
  419. return self.elbRequest("AddTags", params, &ret)
  420. }
  421. func (self *SRegion) RemoveElbTags(arn string, tags map[string]string) error {
  422. params := map[string]string{
  423. "ResourceArns.member.1": arn,
  424. }
  425. idx := 1
  426. for k := range tags {
  427. params[fmt.Sprintf("TagKeys.member.%d", idx)] = k
  428. idx++
  429. }
  430. ret := struct {
  431. }{}
  432. return self.elbRequest("RemoveTags", params, &ret)
  433. }
  434. func (self *SRegion) DescribeElbTags(arn string) (*AwsTags, error) {
  435. ret := struct {
  436. TagDescriptions []struct {
  437. ResourceArn string `xml:"ResourceArn"`
  438. AwsTags
  439. } `xml:"TagDescriptions>member"`
  440. }{}
  441. err := self.elbRequest("DescribeTags", map[string]string{"ResourceArns.member.1": arn}, &ret)
  442. if err != nil {
  443. return nil, errors.Wrapf(err, "DescribeTags")
  444. }
  445. for _, res := range ret.TagDescriptions {
  446. if res.ResourceArn == arn {
  447. return &res.AwsTags, nil
  448. }
  449. }
  450. return nil, cloudprovider.ErrNotFound
  451. }
  452. func (self *SRegion) GetLoadbalancers(id, marker string) ([]SElb, string, error) {
  453. ret := &SElbs{}
  454. params := map[string]string{}
  455. if len(id) > 0 {
  456. params["LoadBalancerArns.member.1"] = id
  457. }
  458. if len(marker) > 0 {
  459. params["Marker"] = marker
  460. }
  461. err := self.elbRequest("DescribeLoadBalancers", params, ret)
  462. if err != nil {
  463. return nil, "", errors.Wrapf(err, "DescribeLoadBalancers")
  464. }
  465. return ret.LoadBalancers, ret.NextMarker, nil
  466. }
  467. func (self *SRegion) CreateLoadbalancer(opts *cloudprovider.SLoadbalancerCreateOptions) (*SElb, error) {
  468. ret := &SElbs{}
  469. params := map[string]string{
  470. "Name": opts.Name,
  471. "Type": opts.LoadbalancerSpec,
  472. "Scheme": "internal",
  473. "IpAddressType": "ipv4",
  474. }
  475. if opts.AddressType == api.LB_ADDR_TYPE_INTERNET {
  476. params["Scheme"] = "internet-facing"
  477. }
  478. if opts.LoadbalancerSpec == api.LB_AWS_SPEC_APPLICATION && len(opts.NetworkIds) == 1 {
  479. nets, err := self.GetNetwroks(nil, opts.ZoneId, opts.VpcId)
  480. if err != nil {
  481. return nil, errors.Wrapf(err, "GetNetworks(%s)", opts.VpcId)
  482. }
  483. for i := range nets {
  484. if !utils.IsInStringArray(nets[i].SubnetId, opts.NetworkIds) {
  485. opts.NetworkIds = append(opts.NetworkIds, nets[i].SubnetId)
  486. break
  487. }
  488. }
  489. }
  490. for i, net := range opts.NetworkIds {
  491. params[fmt.Sprintf("Subnets.member.%d", i+1)] = net
  492. }
  493. idx := 1
  494. for k, v := range opts.Tags {
  495. params[fmt.Sprintf("Tags.member.%d.Key", idx)] = k
  496. params[fmt.Sprintf("Tags.member.%d.Value", idx)] = v
  497. idx++
  498. }
  499. err := self.elbRequest("CreateLoadBalancer", params, ret)
  500. if err != nil {
  501. return nil, errors.Wrapf(err, "CreateLoadBalancer")
  502. }
  503. for i := range ret.LoadBalancers {
  504. ret.LoadBalancers[i].region = self
  505. return &ret.LoadBalancers[i], nil
  506. }
  507. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created")
  508. }
  509. func (self *SElb) GetDescription() string {
  510. tags, _ := self.region.DescribeElbTags(self.LoadBalancerArn)
  511. if tags == nil {
  512. return ""
  513. }
  514. return tags.GetDescription()
  515. }