eip.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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 azure
  15. import (
  16. "fmt"
  17. "net/url"
  18. "strings"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/utils"
  24. billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
  25. api "yunion.io/x/cloudmux/pkg/apis/compute"
  26. "yunion.io/x/cloudmux/pkg/cloudprovider"
  27. "yunion.io/x/cloudmux/pkg/multicloud"
  28. )
  29. type PublicIPAddressSku struct {
  30. Name string
  31. }
  32. type IPConfigurationPropertiesFormat struct {
  33. PrivateIPAddress string
  34. }
  35. type IPConfiguration struct {
  36. Name string
  37. ID string
  38. }
  39. type PublicIPAddressPropertiesFormat struct {
  40. PublicIPAddressVersion string `json:"publicIPAddressVersion,omitempty"`
  41. IPAddress string `json:"ipAddress,omitempty"`
  42. PublicIPAllocationMethod string `json:"publicIPAllocationMethod,omitempty"`
  43. ProvisioningState string `json:"provisioningState,omitempty"`
  44. IPConfiguration IPConfiguration `json:"ipConfiguration,omitempty"`
  45. }
  46. type SEipAddress struct {
  47. region *SRegion
  48. multicloud.SEipBase
  49. AzureTags
  50. ID string
  51. Name string
  52. Location string
  53. Properties PublicIPAddressPropertiesFormat `json:"properties,omitempty"`
  54. Type string
  55. Sku *PublicIPAddressSku
  56. }
  57. func (self *SRegion) AllocateEIP(name, projectId string) (*SEipAddress, error) {
  58. params := map[string]interface{}{
  59. "Location": self.Name,
  60. "Name": name,
  61. "Properties": map[string]string{
  62. "PublicIPAddressVersion": "IPv4",
  63. "PublicIPAllocationMethod": "Static",
  64. },
  65. "Type": "Microsoft.Network/publicIPAddresses",
  66. }
  67. eip := &SEipAddress{region: self}
  68. err := self.create(projectId, jsonutils.Marshal(params), eip)
  69. if err != nil {
  70. return nil, err
  71. }
  72. return eip, cloudprovider.WaitStatus(eip, api.EIP_STATUS_READY, 10*time.Second, 300*time.Second)
  73. }
  74. func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
  75. return region.AllocateEIP(eip.Name, eip.ProjectId)
  76. }
  77. func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
  78. eip := SEipAddress{region: region}
  79. return &eip, region.get(eipId, url.Values{}, &eip)
  80. }
  81. func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
  82. return self.region.AssociateEip(self.ID, conf.InstanceId)
  83. }
  84. func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
  85. instance, err := region.GetInstance(instanceId)
  86. if err != nil {
  87. return err
  88. }
  89. if len(instance.Properties.NetworkProfile.NetworkInterfaces) > 0 {
  90. nic, err := region.GetNetworkInterface(instance.Properties.NetworkProfile.NetworkInterfaces[0].ID)
  91. if err != nil {
  92. return err
  93. }
  94. if len(nic.Properties.IPConfigurations) > 0 {
  95. nic.Properties.IPConfigurations[0].Properties.PublicIPAddress = &PublicIPAddress{ID: eipId}
  96. return region.update(jsonutils.Marshal(nic), nil)
  97. }
  98. return fmt.Errorf("network interface with no IPConfigurations")
  99. }
  100. return fmt.Errorf("Instance with no interface")
  101. }
  102. func (region *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) {
  103. return region.GetEip(eipId)
  104. }
  105. func (self *SEipAddress) ChangeBandwidth(bw int) error {
  106. return cloudprovider.ErrNotSupported
  107. }
  108. func (self *SEipAddress) Delete() error {
  109. self.Dissociate() //避免eip挂载在弹性网卡之上,导致删除失败
  110. return self.region.DeallocateEIP(self.ID)
  111. }
  112. func (region *SRegion) DeallocateEIP(eipId string) error {
  113. return cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) {
  114. err := region.del(eipId)
  115. if err == nil {
  116. return true, nil
  117. }
  118. // {"error":{"code":"PublicIPAddressCannotBeDeleted","details":[],"message":"Public IP address /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Network/publicIPAddresses/eip-for-test-wwl can not be deleted since it is still allocated to resource /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Network/networkInterfaces/test-wwl-ipconfig."}}
  119. // 刚解绑eip后可能数据未刷新,需要再次尝试
  120. if strings.Contains(err.Error(), "it is still allocated to resource") {
  121. return false, nil
  122. }
  123. return false, errors.Wrapf(err, "del(%s)", eipId)
  124. })
  125. }
  126. func (self *SEipAddress) Dissociate() error {
  127. return self.region.DissociateEip(self.ID)
  128. }
  129. func (region *SRegion) DissociateEip(eipId string) error {
  130. eip, err := region.GetEip(eipId)
  131. if err != nil {
  132. return errors.Wrapf(err, "GetEip(%s)", eipId)
  133. }
  134. interfaceId := eip.Properties.IPConfiguration.ID
  135. if strings.Index(interfaceId, "/ipConfigurations/") > 0 {
  136. interfaceId = strings.Split(interfaceId, "/ipConfigurations/")[0]
  137. }
  138. nic, err := region.GetNetworkInterface(interfaceId)
  139. if err != nil {
  140. return err
  141. }
  142. for i := 0; i < len(nic.Properties.IPConfigurations); i++ {
  143. if nic.Properties.IPConfigurations[i].Properties.PublicIPAddress != nil && nic.Properties.IPConfigurations[i].Properties.PublicIPAddress.ID == eipId {
  144. nic.Properties.IPConfigurations[i].Properties.PublicIPAddress = nil
  145. break
  146. }
  147. }
  148. return region.update(jsonutils.Marshal(nic), nil)
  149. }
  150. func (self *SEipAddress) GetAssociationExternalId() string {
  151. info := strings.Split(self.Properties.IPConfiguration.ID, "/")
  152. if len(info) > 2 {
  153. return strings.ToLower(strings.Join(info[:len(info)-2], "/"))
  154. }
  155. return ""
  156. }
  157. func (self *SEipAddress) GetAssociationType() string {
  158. if len(self.Properties.IPConfiguration.ID) == 0 {
  159. return ""
  160. }
  161. if info := strings.Split(self.Properties.IPConfiguration.ID, "/"); len(info) > 7 {
  162. resType := strings.ToLower(info[7])
  163. if utils.IsInStringArray(resType, []string{"networkinterfaces"}) {
  164. return api.EIP_ASSOCIATE_TYPE_SERVER
  165. }
  166. if utils.IsInStringArray(resType, []string{"loadbalancers", "applicationgateways"}) {
  167. return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
  168. }
  169. return resType
  170. }
  171. return ""
  172. }
  173. func (self *SEipAddress) GetBandwidth() int {
  174. return 0
  175. }
  176. func (self *SEipAddress) GetINetworkId() string {
  177. return ""
  178. }
  179. func (self *SEipAddress) GetGlobalId() string {
  180. return strings.ToLower(self.ID)
  181. }
  182. func (self *SEipAddress) GetId() string {
  183. return self.ID
  184. }
  185. func (self *SEipAddress) GetInternetChargeType() string {
  186. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  187. }
  188. func (self *SEipAddress) GetIpAddr() string {
  189. return self.Properties.IPAddress
  190. }
  191. func (self *SEipAddress) GetMode() string {
  192. if self.IsEmulated() {
  193. return api.EIP_MODE_INSTANCE_PUBLICIP
  194. }
  195. return api.EIP_MODE_STANDALONE_EIP
  196. }
  197. func (self *SEipAddress) GetName() string {
  198. return self.Name
  199. }
  200. func (self *SEipAddress) GetStatus() string {
  201. switch self.Properties.ProvisioningState {
  202. case "Succeeded", "":
  203. return api.EIP_STATUS_READY
  204. case "Updating":
  205. return api.EIP_STATUS_ALLOCATE
  206. default:
  207. log.Errorf("Unknown eip status: %s", self.Properties.ProvisioningState)
  208. return api.EIP_STATUS_UNKNOWN
  209. }
  210. }
  211. func (self *SEipAddress) IsEmulated() bool {
  212. if strings.ToLower(self.Properties.PublicIPAllocationMethod) == "Dynamic" || len(self.Properties.IPAddress) == 0 {
  213. return true
  214. }
  215. return false
  216. }
  217. func (self *SEipAddress) Refresh() error {
  218. eip, err := self.region.GetEip(self.ID)
  219. if err != nil {
  220. return err
  221. }
  222. return jsonutils.Update(self, eip)
  223. }
  224. func (self *SEipAddress) GetBillingType() string {
  225. return billing_api.BILLING_TYPE_POSTPAID
  226. }
  227. func (self *SEipAddress) GetCreatedAt() time.Time {
  228. return time.Time{}
  229. }
  230. func (self *SEipAddress) GetExpiredAt() time.Time {
  231. return time.Time{}
  232. }
  233. func (self *SEipAddress) GetProjectId() string {
  234. return getResourceGroup(self.ID)
  235. }