eip.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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 hcso
  15. import (
  16. "fmt"
  17. "time"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
  22. api "yunion.io/x/cloudmux/pkg/apis/compute"
  23. "yunion.io/x/cloudmux/pkg/cloudprovider"
  24. "yunion.io/x/cloudmux/pkg/multicloud"
  25. "yunion.io/x/cloudmux/pkg/multicloud/huawei"
  26. )
  27. type TInternetChargeType string
  28. const (
  29. InternetChargeByTraffic = TInternetChargeType("traffic")
  30. InternetChargeByBandwidth = TInternetChargeType("bandwidth")
  31. )
  32. type Bandwidth struct {
  33. ID string `json:"id"`
  34. Name string `json:"name"`
  35. Size int64 `json:"size"`
  36. ShareType string `json:"share_type"`
  37. PublicipInfo []PublicipInfo `json:"publicip_info"`
  38. TenantID string `json:"tenant_id"`
  39. BandwidthType string `json:"bandwidth_type"`
  40. ChargeMode string `json:"charge_mode"`
  41. BillingInfo string `json:"billing_info"`
  42. EnterpriseProjectID string `json:"enterprise_project_id"`
  43. }
  44. type PublicipInfo struct {
  45. PublicipID string `json:"publicip_id"`
  46. PublicipAddress string `json:"publicip_address"`
  47. PublicipType string `json:"publicip_type"`
  48. IPVersion int64 `json:"ip_version"`
  49. }
  50. type SProfile struct {
  51. UserID string `json:"user_id"`
  52. ProductID string `json:"product_id"`
  53. RegionID string `json:"region_id"`
  54. OrderID string `json:"order_id"`
  55. }
  56. // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html
  57. type SEipAddress struct {
  58. region *SRegion
  59. port *Port
  60. multicloud.SEipBase
  61. huawei.HuaweiTags
  62. ID string `json:"id"`
  63. Status string `json:"status"`
  64. Profile *SProfile `json:"profile,omitempty"`
  65. Type string `json:"type"`
  66. PublicIPAddress string `json:"public_ip_address"`
  67. PrivateIPAddress string `json:"private_ip_address"`
  68. TenantID string `json:"tenant_id"`
  69. CreateTime time.Time `json:"create_time"`
  70. BandwidthID string `json:"bandwidth_id"`
  71. BandwidthShareType string `json:"bandwidth_share_type"`
  72. BandwidthSize int64 `json:"bandwidth_size"`
  73. BandwidthName string `json:"bandwidth_name"`
  74. EnterpriseProjectID string `json:"enterprise_project_id"`
  75. IPVersion int64 `json:"ip_version"`
  76. PortId string `json:"port_id"`
  77. EnterpriseProjectId string
  78. }
  79. func (self *SEipAddress) GetId() string {
  80. return self.ID
  81. }
  82. func (self *SEipAddress) GetName() string {
  83. if len(self.BandwidthName) == 0 {
  84. return self.BandwidthName
  85. }
  86. return self.PublicIPAddress
  87. }
  88. func (self *SEipAddress) GetGlobalId() string {
  89. return self.ID
  90. }
  91. func (self *SEipAddress) GetStatus() string {
  92. // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html
  93. switch self.Status {
  94. case "ACTIVE", "DOWN", "ELB":
  95. return api.EIP_STATUS_READY
  96. case "PENDING_CREATE", "NOTIFYING":
  97. return api.EIP_STATUS_ALLOCATE
  98. case "BINDING":
  99. return api.EIP_STATUS_ALLOCATE
  100. case "BIND_ERROR":
  101. return api.EIP_STATUS_ALLOCATE_FAIL
  102. case "PENDING_DELETE", "NOTIFY_DELETE":
  103. return api.EIP_STATUS_DEALLOCATE
  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.ID)
  113. if err != nil {
  114. return err
  115. }
  116. return jsonutils.Update(self, new)
  117. }
  118. func (self *SEipAddress) IsEmulated() bool {
  119. return false
  120. }
  121. func (self *SEipAddress) GetIpAddr() string {
  122. return self.PublicIPAddress
  123. }
  124. func (self *SEipAddress) GetMode() string {
  125. return api.EIP_MODE_STANDALONE_EIP
  126. }
  127. func (self *SEipAddress) GetPort() *Port {
  128. if len(self.PortId) == 0 {
  129. return nil
  130. }
  131. if self.port != nil {
  132. return self.port
  133. }
  134. port, err := self.region.GetPort(self.PortId)
  135. if err != nil {
  136. return nil
  137. } else {
  138. self.port = &port
  139. }
  140. return self.port
  141. }
  142. func (self *SEipAddress) GetAssociationType() string {
  143. if len(self.PortId) == 0 {
  144. return ""
  145. }
  146. port, err := self.region.GetPort(self.PortId)
  147. if err != nil {
  148. log.Errorf("Get eip %s port %s error: %v", self.ID, self.PortId, err)
  149. return ""
  150. }
  151. switch port.DeviceOwner {
  152. case "neutron:LOADBALANCER", "neutron:LOADBALANCERV2":
  153. return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
  154. case "network:nat_gateway":
  155. return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
  156. default:
  157. return port.DeviceOwner
  158. }
  159. }
  160. func (self *SEipAddress) GetAssociationExternalId() string {
  161. // network/0273a359d61847fc83405926c958c746/ext-floatingips?tenantId=0273a359d61847fc83405926c958c746&limit=2000
  162. // 只能通过 port id 反查device id.
  163. if len(self.PortId) > 0 {
  164. port, _ := self.region.GetPort(self.PortId)
  165. return port.DeviceID
  166. }
  167. return ""
  168. }
  169. func (self *SEipAddress) GetBandwidth() int {
  170. return int(self.BandwidthSize) // Mb
  171. }
  172. func (self *SEipAddress) GetINetworkId() string {
  173. return ""
  174. }
  175. func (self *SEipAddress) GetInternetChargeType() string {
  176. // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html
  177. bandwidth, err := self.region.GetEipBandwidth(self.BandwidthID)
  178. if err != nil {
  179. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  180. }
  181. if bandwidth.ChargeMode != "traffic" {
  182. return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
  183. } else {
  184. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  185. }
  186. }
  187. func (self *SEipAddress) GetBillingType() string {
  188. if self.Profile == nil {
  189. return billing_api.BILLING_TYPE_POSTPAID
  190. } else {
  191. return billing_api.BILLING_TYPE_PREPAID
  192. }
  193. }
  194. func (self *SEipAddress) GetCreatedAt() time.Time {
  195. return self.CreateTime
  196. }
  197. func (self *SEipAddress) GetExpiredAt() time.Time {
  198. return time.Time{}
  199. }
  200. func (self *SEipAddress) Delete() error {
  201. return self.region.DeallocateEIP(self.ID)
  202. }
  203. func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
  204. portId, err := self.region.GetInstancePortId(conf.InstanceId)
  205. if err != nil {
  206. return err
  207. }
  208. if len(self.PortId) > 0 {
  209. if self.PortId == portId {
  210. return nil
  211. }
  212. return fmt.Errorf("eip %s aready associate with port %s", self.GetId(), self.PortId)
  213. }
  214. err = self.region.AssociateEipWithPortId(self.ID, portId)
  215. if err != nil {
  216. return err
  217. }
  218. err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 10*time.Second, 10*time.Second, 180*time.Second)
  219. return err
  220. }
  221. func (self *SEipAddress) Dissociate() error {
  222. if len(self.PortId) == 0 {
  223. return nil
  224. }
  225. port, err := self.region.GetPort(self.PortId)
  226. if err != nil {
  227. return errors.Wrapf(err, "GetPort(%s)", self.PortId)
  228. }
  229. err = self.region.DissociateEip(self.ID, port.DeviceID)
  230. if err != nil {
  231. return errors.Wrapf(err, "DissociateEip")
  232. }
  233. err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
  234. return err
  235. }
  236. func (self *SEipAddress) ChangeBandwidth(bw int) error {
  237. return self.region.UpdateEipBandwidth(self.BandwidthID, bw)
  238. }
  239. func (self *SRegion) GetInstancePortId(instanceId string) (string, error) {
  240. // 目前只绑定一个网卡
  241. // todo: 还需要按照ports状态进行过滤
  242. ports, err := self.GetPorts(instanceId)
  243. if err != nil {
  244. return "", err
  245. }
  246. if len(ports) == 0 {
  247. return "", fmt.Errorf("AssociateEip instance %s port is empty", instanceId)
  248. }
  249. return ports[0].ID, nil
  250. }
  251. // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html
  252. func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEipAddress, error) {
  253. paramsStr := `
  254. {
  255. "publicip": {
  256. "type": "%s",
  257. "ip_version": 4
  258. },
  259. "bandwidth": {
  260. "name": "%s",
  261. "size": %d,
  262. "share_type": "PER",
  263. "charge_mode": "%s"
  264. }
  265. }
  266. `
  267. if len(bgpType) == 0 {
  268. return nil, fmt.Errorf("AllocateEIP bgp type should not be empty")
  269. }
  270. paramsStr = fmt.Sprintf(paramsStr, bgpType, name, bwMbps, chargeType)
  271. _params, _ := jsonutils.ParseString(paramsStr)
  272. params := _params.(*jsonutils.JSONDict)
  273. if len(projectId) > 0 {
  274. params.Set("enterprise_project_id", jsonutils.NewString(projectId))
  275. }
  276. eip := SEipAddress{}
  277. err := DoCreate(self.ecsClient.Eips.Create, params, &eip)
  278. return &eip, err
  279. }
  280. func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) {
  281. var eip SEipAddress
  282. err := DoGet(self.ecsClient.Eips.Get, eipId, nil, &eip)
  283. eip.region = self
  284. return &eip, err
  285. }
  286. func (self *SRegion) DeallocateEIP(eipId string) error {
  287. _, err := self.ecsClient.Eips.Delete(eipId, nil)
  288. return err
  289. }
  290. func (self *SRegion) AssociateEip(eipId string, instanceId string) error {
  291. portId, err := self.GetInstancePortId(instanceId)
  292. if err != nil {
  293. return err
  294. }
  295. return self.AssociateEipWithPortId(eipId, portId)
  296. }
  297. func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error {
  298. params := jsonutils.NewDict()
  299. publicIPObj := jsonutils.NewDict()
  300. publicIPObj.Add(jsonutils.NewString(portId), "port_id")
  301. params.Add(publicIPObj, "publicip")
  302. _, err := self.ecsClient.Eips.Update(eipId, params)
  303. return err
  304. }
  305. func (self *SRegion) DissociateEip(eipId string, instanceId string) error {
  306. eip, err := self.GetEip(eipId)
  307. if err != nil {
  308. return err
  309. }
  310. // 已经是解绑状态
  311. if eip.Status == "DOWN" {
  312. return nil
  313. }
  314. remoteInstanceId := eip.GetAssociationExternalId()
  315. if remoteInstanceId != instanceId {
  316. return fmt.Errorf("eip %s associate with another instance %s", eipId, remoteInstanceId)
  317. }
  318. paramsStr := `{"publicip":{"port_id":null}}`
  319. params, _ := jsonutils.ParseString(paramsStr)
  320. _, err = self.ecsClient.Eips.Update(eipId, params)
  321. return err
  322. }
  323. func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error {
  324. paramStr := `{
  325. "bandwidth":
  326. {
  327. "size": %d
  328. }
  329. }`
  330. paramStr = fmt.Sprintf(paramStr, bw)
  331. params, _ := jsonutils.ParseString(paramStr)
  332. _, err := self.ecsClient.Bandwidths.Update(bandwidthId, params)
  333. return err
  334. }
  335. func (self *SRegion) GetEipBandwidth(bandwidthId string) (Bandwidth, error) {
  336. bandwidth := Bandwidth{}
  337. err := DoGet(self.ecsClient.Bandwidths.Get, bandwidthId, nil, &bandwidth)
  338. return bandwidth, err
  339. }
  340. func (self *SEipAddress) GetProjectId() string {
  341. return self.EnterpriseProjectId
  342. }