Files
cloudpods/pkg/compute/tasks/eip_allocate_task.go
T

174 lines
5.5 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"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
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/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
)
type EipAllocateTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(EipAllocateTask{})
}
func (self *EipAllocateTask) onFailed(ctx context.Context, eip *models.SElasticip, reason string) {
self.finalReleasePendingUsage(ctx, eip)
self.setGuestAllocateEipFailed(eip, reason)
self.SetStageFailed(ctx, reason)
}
func (self *EipAllocateTask) setGuestAllocateEipFailed(eip *models.SElasticip, reason string) {
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(self.UserCred, api.VM_ASSOCIATE_EIP_FAILED, reason)
}
}
func (self *EipAllocateTask) finalReleasePendingUsage(ctx context.Context, eip *models.SElasticip) {
pendingUsage := models.SQuota{}
if err := self.GetPendingUsage(&pendingUsage); err == nil && !pendingUsage.IsEmpty() {
quotaPlatform := eip.GetQuotaPlatformID()
if err := models.QuotaManager.CancelPendingUsage(ctx, self.UserCred, rbacutils.ScopeProject, self.UserCred, quotaPlatform, nil, &pendingUsage); err != nil {
log.Errorf("CancelPendingUsage error: %v", err)
}
}
}
func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
eip := obj.(*models.SElasticip)
iregion, err := eip.GetIRegion()
if err != nil {
msg := fmt.Sprintf("fail to find iregion for eip %s", err)
eip.SetStatus(self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, msg)
self.onFailed(ctx, eip, msg)
return
}
args := &cloudprovider.SEip{
Name: eip.Name,
BandwidthMbps: eip.Bandwidth,
ChargeType: eip.ChargeType,
BGPType: eip.BgpType,
IP: eip.IpAddr,
}
if len(eip.NetworkId) > 0 {
_network, err := models.NetworkManager.FetchById(eip.NetworkId)
if err != nil {
msg := fmt.Sprintf("failed to found network %s error: %v", eip.NetworkId, err)
self.onFailed(ctx, eip, msg)
return
}
network := _network.(*models.SNetwork)
ip, _ := self.GetParams().GetString("ip")
if len(ip) > 0 {
lockman.LockObject(ctx, network)
defer lockman.ReleaseObject(ctx, network)
addrTable := network.GetUsedAddresses()
ipAddr, err := network.GetFreeIP(ctx, self.UserCred, addrTable, nil, ip, models.IPAllocationNone, false)
if err != nil {
self.onFailed(ctx, eip, err.Error())
return
}
if ipAddr != ip {
msg := fmt.Sprintf("candidate ip %s is occupoed!", ip)
self.onFailed(ctx, eip, msg)
return
}
_, err = db.Update(eip, func() error {
eip.IpAddr = ipAddr
return nil
})
if err != nil {
self.onFailed(ctx, eip, err.Error())
return
}
}
args.NetworkExternalId = network.ExternalId
}
extEip, err := iregion.CreateEIP(args)
if err != nil {
msg := fmt.Sprintf("create eip fail %s", err)
eip.SetStatus(self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, msg)
self.onFailed(ctx, eip, msg)
return
}
err = eip.SyncWithCloudEip(ctx, self.UserCred, eip.GetCloudprovider(), extEip, nil)
if err != nil {
msg := fmt.Sprintf("sync eip fail %s", err)
eip.SetStatus(self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, msg)
self.onFailed(ctx, eip, msg)
return
}
self.finalReleasePendingUsage(ctx, eip)
if self.Params != nil && self.Params.Contains("instance_id") {
self.SetStage("on_eip_associate_complete", nil)
err = eip.StartEipAssociateTask(ctx, self.UserCred, self.Params, "")
if err != nil {
msg := fmt.Sprintf("start associate task fail %s", err)
self.SetStageFailed(ctx, msg)
}
} else {
logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
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.String())
self.SetStageFailed(ctx, data.String())
}