eip.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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 aliyun
  15. import (
  16. "fmt"
  17. "strings"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/utils"
  23. api "yunion.io/x/cloudmux/pkg/apis/compute"
  24. "yunion.io/x/cloudmux/pkg/cloudprovider"
  25. "yunion.io/x/cloudmux/pkg/multicloud"
  26. )
  27. type TInternetChargeType string
  28. const (
  29. InternetChargeByTraffic = TInternetChargeType("PayByTraffic")
  30. InternetChargeByBandwidth = TInternetChargeType("PayByBandwidth")
  31. )
  32. const (
  33. EIP_STATUS_ASSOCIATING = "Associating"
  34. EIP_STATUS_UNASSOCIATING = "Unassociating"
  35. EIP_STATUS_INUSE = "InUse"
  36. EIP_STATUS_AVAILABLE = "Available"
  37. EIP_OPERATION_LOCK_FINANCIAL = "financial"
  38. EIP_OPERATION_LOCK_SECURITY = "security"
  39. EIP_INSTANCE_TYPE_ECS = "EcsInstance" // (默认值):VPC类型的ECS实例
  40. EIP_INTANNCE_TYPE_SLB = "SlbInstance" // :VPC类型的SLB实例
  41. EIP_INSTANCE_TYPE_NAT = "Nat" // :NAT网关
  42. EIP_INSTANCE_TYPE_HAVIP = "HaVip" // :HAVIP
  43. )
  44. /*
  45. {
  46. "AllocationId":"eip-2zeddtan63ou44dtyt9s3",
  47. "AllocationTime":"2019-02-23T06:48:36Z",
  48. "Bandwidth":"100",
  49. "ChargeType":"PostPaid",
  50. "ExpiredTime":"",
  51. "InstanceId":"",
  52. "InstanceType":"",
  53. "InternetChargeType":"PayByTraffic",
  54. "IpAddress":"39.105.131.32",
  55. "OperationLocks":{"LockReason":[]},
  56. "RegionId":"cn-beijing",
  57. "Status":"Available"
  58. }
  59. */
  60. type SEipAddress struct {
  61. region *SRegion
  62. multicloud.SEipBase
  63. AliyunTags
  64. Name string
  65. AllocationId string
  66. InternetChargeType TInternetChargeType
  67. IpAddress string
  68. Status string
  69. InstanceType string
  70. InstanceId string
  71. Bandwidth int /* Mbps */
  72. BusinessStatus string
  73. AllocationTime time.Time
  74. DeletionProtection bool
  75. Descritpion string
  76. ISP string
  77. Mode string
  78. Netmode string
  79. OperationLocks string
  80. ChargeType TChargeType
  81. ExpiredTime time.Time
  82. ResourceGroupId string
  83. }
  84. func (self *SEipAddress) GetId() string {
  85. return self.AllocationId
  86. }
  87. func (self *SEipAddress) GetName() string {
  88. if len(self.Name) > 0 {
  89. return self.Name
  90. }
  91. return self.IpAddress
  92. }
  93. func (self *SEipAddress) GetGlobalId() string {
  94. return self.AllocationId
  95. }
  96. func (self *SEipAddress) GetStatus() string {
  97. switch self.Status {
  98. case EIP_STATUS_AVAILABLE, EIP_STATUS_INUSE:
  99. return api.EIP_STATUS_READY
  100. case EIP_STATUS_ASSOCIATING:
  101. return api.EIP_STATUS_ASSOCIATE
  102. case EIP_STATUS_UNASSOCIATING:
  103. return api.EIP_STATUS_DISSOCIATE
  104. default:
  105. return api.EIP_STATUS_UNKNOWN
  106. }
  107. }
  108. func (self *SEipAddress) Refresh() error {
  109. if self.IsEmulated() {
  110. return nil
  111. }
  112. new, err := self.region.GetEip(self.AllocationId)
  113. if err != nil {
  114. return err
  115. }
  116. return jsonutils.Update(self, new)
  117. }
  118. func (self *SEipAddress) IsEmulated() bool {
  119. if self.AllocationId == self.InstanceId {
  120. // fixed Public IP
  121. return true
  122. } else {
  123. return false
  124. }
  125. }
  126. func (self *SEipAddress) GetIpAddr() string {
  127. return self.IpAddress
  128. }
  129. func (self *SEipAddress) GetMode() string {
  130. if self.InstanceId == self.AllocationId {
  131. return api.EIP_MODE_INSTANCE_PUBLICIP
  132. } else {
  133. return api.EIP_MODE_STANDALONE_EIP
  134. }
  135. }
  136. func (self *SEipAddress) GetAssociationType() string {
  137. switch self.InstanceType {
  138. case EIP_INSTANCE_TYPE_ECS, "NetworkInterface":
  139. if strings.HasPrefix(self.Name, "CREATE_BY_ALB") || strings.HasPrefix(self.Name, "CREATE_BY_NLB") {
  140. return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
  141. }
  142. return api.EIP_ASSOCIATE_TYPE_SERVER
  143. case EIP_INSTANCE_TYPE_NAT:
  144. return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
  145. case EIP_INTANNCE_TYPE_SLB:
  146. return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
  147. default:
  148. return self.InstanceType
  149. }
  150. }
  151. func (self *SEipAddress) GetAssociationExternalId() string {
  152. return self.InstanceId
  153. }
  154. func (self *SEipAddress) GetBillingType() string {
  155. return convertChargeType(self.ChargeType)
  156. }
  157. func (self *SEipAddress) GetCreatedAt() time.Time {
  158. return self.AllocationTime
  159. }
  160. func (self *SEipAddress) GetExpiredAt() time.Time {
  161. return convertExpiredAt(self.ExpiredTime)
  162. }
  163. func (self *SEipAddress) Delete() error {
  164. return self.region.DeallocateEIP(self.AllocationId)
  165. }
  166. func (self *SEipAddress) GetBandwidth() int {
  167. return self.Bandwidth
  168. }
  169. func (self *SEipAddress) GetINetworkId() string {
  170. return ""
  171. }
  172. func (self *SEipAddress) GetInternetChargeType() string {
  173. switch self.InternetChargeType {
  174. case InternetChargeByTraffic:
  175. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  176. case InternetChargeByBandwidth:
  177. return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
  178. default:
  179. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  180. }
  181. }
  182. func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
  183. err := cloudprovider.Wait(20*time.Second, 60*time.Second, func() (bool, error) {
  184. err := self.region.AssociateEip(self.AllocationId, conf.InstanceId)
  185. if err != nil {
  186. if isError(err, "IncorrectInstanceStatus") {
  187. return false, nil
  188. }
  189. return false, errors.Wrap(err, "region.AssociateEip")
  190. }
  191. return true, nil
  192. })
  193. err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
  194. return err
  195. }
  196. func (self *SEipAddress) Dissociate() error {
  197. err := self.region.DissociateEip(self.AllocationId, self.InstanceId)
  198. if err != nil {
  199. return err
  200. }
  201. err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
  202. return err
  203. }
  204. func (self *SEipAddress) ChangeBandwidth(bw int) error {
  205. return self.region.UpdateEipBandwidth(self.AllocationId, bw)
  206. }
  207. func (region *SRegion) GetEips(eipId string, associatedId, addr string) ([]SEipAddress, error) {
  208. params := make(map[string]string)
  209. params["RegionId"] = region.RegionId
  210. params["PageSize"] = "100"
  211. if len(addr) > 0 {
  212. params["EipAddress"] = addr
  213. }
  214. if len(eipId) > 0 {
  215. params["AllocationId"] = eipId
  216. }
  217. if len(associatedId) > 0 {
  218. params["AssociatedInstanceId"] = associatedId
  219. for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "ngw-": "Nat", "lb-": "SlbInstance"} {
  220. if strings.HasPrefix(associatedId, prefix) {
  221. params["AssociatedInstanceType"] = instanceType
  222. }
  223. }
  224. }
  225. pageNumber := 1
  226. ret := []SEipAddress{}
  227. for {
  228. params["PageNumber"] = fmt.Sprintf("%d", pageNumber)
  229. body, err := region.vpcRequest("DescribeEipAddresses", params)
  230. if err != nil {
  231. log.Errorf("DescribeEipAddresses fail %s", err)
  232. return nil, err
  233. }
  234. part := struct {
  235. EipAddresses struct {
  236. EipAddress []SEipAddress
  237. } `json:"EipAddresses"`
  238. TotalCount int `json:"TotalCount"`
  239. }{}
  240. err = body.Unmarshal(&part)
  241. if err != nil {
  242. return nil, errors.Wrapf(err, "Unmarshal EipAddress details")
  243. }
  244. ret = append(ret, part.EipAddresses.EipAddress...)
  245. if len(ret) >= part.TotalCount {
  246. break
  247. }
  248. pageNumber++
  249. }
  250. return ret, nil
  251. }
  252. func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
  253. eips, err := region.GetEips(eipId, "", "")
  254. if err != nil {
  255. return nil, err
  256. }
  257. for i := range eips {
  258. if eips[i].AllocationId == eipId {
  259. eips[i].region = region
  260. return &eips[i], nil
  261. }
  262. }
  263. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", eipId)
  264. }
  265. func (region *SRegion) AllocateEIP(opts *cloudprovider.SEip) (*SEipAddress, error) {
  266. params := make(map[string]string)
  267. if len(opts.Name) > 0 {
  268. params["Name"] = opts.Name
  269. }
  270. params["RegionId"] = region.RegionId
  271. params["Bandwidth"] = fmt.Sprintf("%d", opts.BandwidthMbps)
  272. switch opts.ChargeType {
  273. case api.EIP_CHARGE_TYPE_BY_TRAFFIC:
  274. params["InternetChargeType"] = string(InternetChargeByTraffic)
  275. case api.EIP_CHARGE_TYPE_BY_BANDWIDTH:
  276. params["InternetChargeType"] = string(InternetChargeByBandwidth)
  277. }
  278. params["InstanceChargeType"] = "PostPaid"
  279. params["ClientToken"] = utils.GenRequestId(20)
  280. if len(opts.ProjectId) > 0 {
  281. params["ResourceGroupId"] = opts.ProjectId
  282. }
  283. params["ISP"] = "BGP"
  284. if opts.BGPType == "BGP_PRO" {
  285. params["ISP"] = "BGP_PRO"
  286. }
  287. body, err := region.vpcRequest("AllocateEipAddress", params)
  288. if err != nil {
  289. return nil, errors.Wrapf(err, "AllocateEipAddress")
  290. }
  291. eipId, err := body.GetString("AllocationId")
  292. if err != nil {
  293. return nil, errors.Wrapf(err, "get AllocationId after created")
  294. }
  295. eip, err := region.GetEip(eipId)
  296. if err != nil {
  297. return nil, err
  298. }
  299. if len(opts.Tags) == 0 {
  300. return eip, nil
  301. }
  302. cloudprovider.WaitStatus(eip, api.EIP_STATUS_READY, time.Second*5, time.Minute*1)
  303. eip.SetTags(opts.Tags, false)
  304. return eip, nil
  305. }
  306. func (region *SRegion) CreateEIP(opts *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
  307. return region.AllocateEIP(opts)
  308. }
  309. func (region *SRegion) DeallocateEIP(eipId string) error {
  310. params := make(map[string]string)
  311. params["AllocationId"] = eipId
  312. _, err := region.vpcRequest("ReleaseEipAddress", params)
  313. if err != nil {
  314. log.Errorf("ReleaseEipAddress fail %s", err)
  315. }
  316. return err
  317. }
  318. func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
  319. params := make(map[string]string)
  320. params["AllocationId"] = eipId
  321. params["InstanceId"] = instanceId
  322. for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} {
  323. if strings.HasPrefix(instanceId, prefix) {
  324. params["InstanceType"] = instanceType
  325. }
  326. }
  327. _, err := region.vpcRequest("AssociateEipAddress", params)
  328. return errors.Wrapf(err, "AssociateEipAddress")
  329. }
  330. func (region *SRegion) DissociateEip(eipId string, instanceId string) error {
  331. params := make(map[string]string)
  332. params["AllocationId"] = eipId
  333. params["InstanceId"] = instanceId
  334. for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} {
  335. if strings.HasPrefix(instanceId, prefix) {
  336. params["InstanceType"] = instanceType
  337. }
  338. }
  339. _, err := region.vpcRequest("UnassociateEipAddress", params)
  340. if err != nil {
  341. log.Errorf("UnassociateEipAddress fail %s", err)
  342. }
  343. return err
  344. }
  345. func (region *SRegion) UpdateEipBandwidth(eipId string, bw int) error {
  346. params := make(map[string]string)
  347. params["AllocationId"] = eipId
  348. params["Bandwidth"] = fmt.Sprintf("%d", bw)
  349. _, err := region.vpcRequest("ModifyEipAddressAttribute", params)
  350. if err != nil {
  351. log.Errorf("ModifyEipAddressAttribute fail %s", err)
  352. }
  353. return err
  354. }
  355. func (self *SEipAddress) GetProjectId() string {
  356. return self.ResourceGroupId
  357. }
  358. func (self *SEipAddress) SetTags(tags map[string]string, replace bool) error {
  359. return self.region.SetResourceTags(ALIYUN_SERVICE_VPC, "EIP", self.AllocationId, tags, replace)
  360. }