eip_allocate_task.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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 eip
  15. import (
  16. "context"
  17. "fmt"
  18. "time"
  19. "yunion.io/x/cloudmux/pkg/cloudprovider"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. api "yunion.io/x/onecloud/pkg/apis/compute"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  28. "yunion.io/x/onecloud/pkg/compute/models"
  29. "yunion.io/x/onecloud/pkg/util/logclient"
  30. )
  31. type EipAllocateTask struct {
  32. taskman.STask
  33. }
  34. func init() {
  35. taskman.RegisterTask(EipAllocateTask{})
  36. }
  37. func (self *EipAllocateTask) onFailed(ctx context.Context, eip *models.SElasticip, err error) {
  38. eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, err.Error())
  39. self.setGuestAllocateEipFailed(eip, jsonutils.NewString(err.Error()))
  40. notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
  41. Obj: eip,
  42. Action: notifyclient.ActionCreate,
  43. IsFail: true,
  44. })
  45. self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
  46. }
  47. func (self *EipAllocateTask) setGuestAllocateEipFailed(eip *models.SElasticip, reason jsonutils.JSONObject) {
  48. if eip != nil {
  49. db.OpsLog.LogEvent(eip, db.ACT_ALLOCATE_FAIL, reason, self.GetUserCred())
  50. logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, reason, self.UserCred, false)
  51. }
  52. if self.Params != nil && self.Params.Contains("instance_id") {
  53. instanceId, _ := self.Params.GetString("instance_id")
  54. instance, err := models.GuestManager.FetchById(instanceId)
  55. if err != nil {
  56. log.Errorf("failed to find guest by id: %s error: %v", instanceId, err)
  57. return
  58. }
  59. guest := instance.(*models.SGuest)
  60. guest.SetStatus(context.Background(), self.UserCred, api.INSTANCE_ASSOCIATE_EIP_FAILED, reason.String())
  61. }
  62. }
  63. func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  64. var (
  65. eip = obj.(*models.SElasticip)
  66. eipIsManaged = eip.IsManaged()
  67. )
  68. args := &cloudprovider.SEip{
  69. Name: eip.Name,
  70. BandwidthMbps: eip.Bandwidth,
  71. ChargeType: string(eip.ChargeType),
  72. BGPType: eip.BgpType,
  73. Ip: eip.IpAddr,
  74. }
  75. args.Tags, _ = eip.GetAllUserMetadata()
  76. if eip.NetworkId != "" {
  77. _network, err := models.NetworkManager.FetchById(eip.NetworkId)
  78. if err != nil {
  79. self.onFailed(ctx, eip, errors.Wrapf(err, "NetworkManager.FetchById(%s)", eip.NetworkId))
  80. return
  81. }
  82. network := _network.(*models.SNetwork)
  83. reqIp, _ := self.GetParams().GetString("ip_addr")
  84. if len(eip.IpAddr) == 0 && (reqIp != "" || !eipIsManaged) {
  85. lockman.LockObject(ctx, network)
  86. defer lockman.ReleaseObject(ctx, network)
  87. ipAddr, err := network.GetFreeIP(ctx, self.UserCred, nil, nil, reqIp, api.IPAllocationNone, false, api.AddressTypeIPv4)
  88. if err != nil {
  89. self.onFailed(ctx, eip, errors.Wrapf(err, "GetFreeIP(%s)", reqIp))
  90. return
  91. }
  92. if reqIp != "" && ipAddr != reqIp {
  93. self.onFailed(ctx, eip, fmt.Errorf("requested ip %s is occupied!", reqIp))
  94. return
  95. }
  96. _, err = db.Update(eip, func() error {
  97. eip.IpAddr = ipAddr
  98. eip.BgpType = network.BgpType
  99. return nil
  100. })
  101. if err != nil {
  102. self.onFailed(ctx, eip, errors.Wrapf(err, "db.Update"))
  103. return
  104. }
  105. }
  106. if !eipIsManaged {
  107. eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_READY, "allocated from network")
  108. }
  109. args.NetworkExternalId = network.ExternalId
  110. }
  111. if eipIsManaged {
  112. var err error
  113. _cloudprovider := eip.GetCloudprovider()
  114. args.ProjectId, err = _cloudprovider.SyncProject(ctx, self.GetUserCred(), eip.ProjectId)
  115. if err != nil {
  116. if errors.Cause(err) != cloudprovider.ErrNotSupported && errors.Cause(err) != cloudprovider.ErrNotImplemented {
  117. logclient.AddSimpleActionLog(eip, logclient.ACT_SYNC_CLOUD_PROJECT, err, self.UserCred, false)
  118. }
  119. }
  120. iregion, err := eip.GetIRegion(ctx)
  121. if err != nil {
  122. eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, "")
  123. self.onFailed(ctx, eip, errors.Wrapf(err, "eip.GetIRegion"))
  124. return
  125. }
  126. extEip, err := iregion.CreateEIP(args)
  127. if err != nil {
  128. eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, "")
  129. self.onFailed(ctx, eip, errors.Wrapf(err, "iregion.CreateEIP"))
  130. return
  131. }
  132. cloudprovider.WaitStatus(extEip, api.EIP_STATUS_READY, time.Second*5, time.Minute*3)
  133. if err := eip.SyncWithCloudEip(ctx, self.UserCred, eip.GetCloudprovider(), extEip, nil); err != nil {
  134. eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, "")
  135. self.onFailed(ctx, eip, errors.Wrapf(err, "ip.SyncWithCloudEip"))
  136. return
  137. }
  138. }
  139. if self.Params != nil && self.Params.Contains("instance_id") {
  140. self.SetStage("OnEipAssociateComplete", nil)
  141. if err := eip.StartEipAssociateTask(ctx, self.UserCred, self.Params, self.GetId()); err != nil {
  142. msg := fmt.Sprintf("start associate task fail %s", err)
  143. self.SetStageFailed(ctx, jsonutils.NewString(msg))
  144. }
  145. } else {
  146. logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
  147. notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
  148. Obj: eip,
  149. Action: notifyclient.ActionCreate,
  150. })
  151. self.SetStageComplete(ctx, nil)
  152. }
  153. }
  154. func (self *EipAllocateTask) OnEipAssociateComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  155. eip := obj.(*models.SElasticip)
  156. logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
  157. self.SetStageComplete(ctx, nil)
  158. }
  159. func (self *EipAllocateTask) OnEipAssociateCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  160. eip := obj.(*models.SElasticip)
  161. self.setGuestAllocateEipFailed(eip, data)
  162. self.SetStageFailed(ctx, data)
  163. }