Files
cloudpods/pkg/compute/tasks/eip_allocate_task.go
2024-02-01 20:44:40 +08:00

183 lines
6.1 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tasks
import (
"context"
"fmt"
"time"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type EipAllocateTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(EipAllocateTask{})
}
func (self *EipAllocateTask) onFailed(ctx context.Context, eip *models.SElasticip, err error) {
eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, err.Error())
self.setGuestAllocateEipFailed(eip, jsonutils.NewString(err.Error()))
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
Obj: eip,
Action: notifyclient.ActionCreate,
IsFail: true,
})
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *EipAllocateTask) setGuestAllocateEipFailed(eip *models.SElasticip, reason jsonutils.JSONObject) {
if eip != nil {
db.OpsLog.LogEvent(eip, db.ACT_ALLOCATE_FAIL, reason, self.GetUserCred())
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, reason, self.UserCred, false)
}
if self.Params != nil && self.Params.Contains("instance_id") {
instanceId, _ := self.Params.GetString("instance_id")
instance, err := models.GuestManager.FetchById(instanceId)
if err != nil {
log.Errorf("failed to find guest by id: %s error: %v", instanceId, err)
return
}
guest := instance.(*models.SGuest)
guest.SetStatus(context.Background(), self.UserCred, api.INSTANCE_ASSOCIATE_EIP_FAILED, reason.String())
}
}
func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
var (
eip = obj.(*models.SElasticip)
eipIsManaged = eip.IsManaged()
)
args := &cloudprovider.SEip{
Name: eip.Name,
BandwidthMbps: eip.Bandwidth,
ChargeType: eip.ChargeType,
BGPType: eip.BgpType,
Ip: eip.IpAddr,
}
args.Tags, _ = eip.GetAllUserMetadata()
if eip.NetworkId != "" {
_network, err := models.NetworkManager.FetchById(eip.NetworkId)
if err != nil {
self.onFailed(ctx, eip, errors.Wrapf(err, "NetworkManager.FetchById(%s)", eip.NetworkId))
return
}
network := _network.(*models.SNetwork)
reqIp, _ := self.GetParams().GetString("ip_addr")
if len(eip.IpAddr) == 0 && (reqIp != "" || !eipIsManaged) {
lockman.LockObject(ctx, network)
defer lockman.ReleaseObject(ctx, network)
ipAddr, err := network.GetFreeIP(ctx, self.UserCred, nil, nil, reqIp, api.IPAllocationNone, false, api.AddressTypeIPv4)
if err != nil {
self.onFailed(ctx, eip, errors.Wrapf(err, "GetFreeIP(%s)", reqIp))
return
}
if reqIp != "" && ipAddr != reqIp {
self.onFailed(ctx, eip, fmt.Errorf("requested ip %s is occupied!", reqIp))
return
}
_, err = db.Update(eip, func() error {
eip.IpAddr = ipAddr
eip.BgpType = network.BgpType
return nil
})
if err != nil {
self.onFailed(ctx, eip, errors.Wrapf(err, "db.Update"))
return
}
}
if !eipIsManaged {
eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_READY, "allocated from network")
}
args.NetworkExternalId = network.ExternalId
}
if eipIsManaged {
var err error
_cloudprovider := eip.GetCloudprovider()
args.ProjectId, err = _cloudprovider.SyncProject(ctx, self.GetUserCred(), eip.ProjectId)
if err != nil {
logclient.AddSimpleActionLog(eip, logclient.ACT_SYNC_CLOUD_PROJECT, err, self.UserCred, false)
}
iregion, err := eip.GetIRegion(ctx)
if err != nil {
eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, "")
self.onFailed(ctx, eip, errors.Wrapf(err, "eip.GetIRegion"))
return
}
extEip, err := iregion.CreateEIP(args)
if err != nil {
eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, "")
self.onFailed(ctx, eip, errors.Wrapf(err, "iregion.CreateEIP"))
return
}
cloudprovider.WaitStatus(extEip, api.EIP_STATUS_READY, time.Second*5, time.Minute*3)
if err := eip.SyncWithCloudEip(ctx, self.UserCred, eip.GetCloudprovider(), extEip, nil); err != nil {
eip.SetStatus(ctx, self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, "")
self.onFailed(ctx, eip, errors.Wrapf(err, "ip.SyncWithCloudEip"))
return
}
}
if self.Params != nil && self.Params.Contains("instance_id") {
self.SetStage("OnEipAssociateComplete", nil)
if err := eip.StartEipAssociateTask(ctx, self.UserCred, self.Params, self.GetId()); err != nil {
msg := fmt.Sprintf("start associate task fail %s", err)
self.SetStageFailed(ctx, jsonutils.NewString(msg))
}
} else {
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
Obj: eip,
Action: notifyclient.ActionCreate,
})
self.SetStageComplete(ctx, nil)
}
}
func (self *EipAllocateTask) OnEipAssociateComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
eip := obj.(*models.SElasticip)
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
self.SetStageComplete(ctx, nil)
}
func (self *EipAllocateTask) OnEipAssociateCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
eip := obj.(*models.SElasticip)
self.setGuestAllocateEipFailed(eip, data)
self.SetStageFailed(ctx, data)
}