mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-10 15:53:11 +08:00
268 lines
8.9 KiB
Go
268 lines
8.9 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/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/util/stringutils"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis/compute"
|
|
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"
|
|
)
|
|
|
|
func init() {
|
|
taskman.RegisterTask(HostImportLibvirtServersTask{})
|
|
taskman.RegisterTask(CreateImportedLibvirtGuestTask{})
|
|
}
|
|
|
|
type HostImportLibvirtServersTask struct {
|
|
taskman.STask
|
|
}
|
|
|
|
func (self *HostImportLibvirtServersTask) OnInit(
|
|
ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject,
|
|
) {
|
|
host := obj.(*models.SHost)
|
|
self.SetStage("OnRequestHostPrepareImport", nil)
|
|
self.RequestHostPrepareImport(ctx, host)
|
|
}
|
|
|
|
func (self *HostImportLibvirtServersTask) RequestHostPrepareImport(
|
|
ctx context.Context, host *models.SHost,
|
|
) {
|
|
header := self.GetTaskRequestHeader()
|
|
if _, err := host.Request(ctx, self.UserCred, "POST",
|
|
"/servers/prepare-import-from-libvirt", header, self.Params); err != nil {
|
|
self.TaskFailed(ctx, host, jsonutils.NewString(err.Error()))
|
|
}
|
|
}
|
|
|
|
func (self *HostImportLibvirtServersTask) OnRequestHostPrepareImport(
|
|
ctx context.Context, host *models.SHost, body jsonutils.JSONObject,
|
|
) {
|
|
serversNotMatch, _ := body.GetArray("servers_not_match")
|
|
if len(serversNotMatch) > 0 {
|
|
db.OpsLog.LogEvent(host, db.ACT_HOST_IMPORT_LIBVIRT_SERVERS,
|
|
fmt.Sprintf("Servers %s not match host xml description", serversNotMatch), self.UserCred)
|
|
logclient.AddActionLogWithContext(ctx, host, logclient.ACT_HOST_IMPORT_LIBVIRT_SERVERS,
|
|
fmt.Sprintf("Servers %s not match host xml description", serversNotMatch), self.UserCred, false)
|
|
}
|
|
|
|
serversMatched, _ := body.Get("servers_matched")
|
|
if serversMatched == nil {
|
|
self.TaskFailed(ctx, host, jsonutils.NewString("No matched server found"))
|
|
return
|
|
}
|
|
|
|
guestsDesc := []compute.SImportGuestDesc{}
|
|
if err := serversMatched.Unmarshal(&guestsDesc); err != nil {
|
|
self.TaskFailed(ctx, host, jsonutils.NewString(fmt.Sprintf("Unmarshal matched servers failed: %s", err)))
|
|
return
|
|
}
|
|
self.StartImportServers(ctx, host, guestsDesc)
|
|
}
|
|
|
|
func (self *HostImportLibvirtServersTask) StartImportServers(
|
|
ctx context.Context, host *models.SHost, guestsDesc []compute.SImportGuestDesc,
|
|
) {
|
|
var (
|
|
note string
|
|
success bool
|
|
)
|
|
for i := 0; i < len(guestsDesc); i++ {
|
|
var (
|
|
guest *models.SGuest = nil
|
|
originId string = guestsDesc[i].Id
|
|
)
|
|
|
|
func() {
|
|
lockman.LockRawObject(ctx, models.GuestManager.Keyword(), "name")
|
|
defer lockman.ReleaseRawObject(ctx, models.GuestManager.Keyword(), "name")
|
|
|
|
err := self.FillLibvirtGuestDesc(ctx, host, &guestsDesc[i])
|
|
if err != nil {
|
|
note = fmt.Sprintf("Guest %s desc fill failed: %s", guestsDesc[i].Id, err)
|
|
success = false
|
|
} else {
|
|
guest, err = models.GuestManager.DoImport(ctx, self.UserCred, &guestsDesc[i])
|
|
if err != nil {
|
|
note = fmt.Sprintf("Guest %s import failed: %s", guestsDesc[i].Id, err)
|
|
success = false
|
|
} else {
|
|
meta := map[string]interface{}{
|
|
"__is_import": "true",
|
|
"__origin_id": originId,
|
|
"__monitor_path": guestsDesc[i].MonitorPath,
|
|
}
|
|
guest.SetAllMetadata(ctx, meta, self.UserCred)
|
|
if err := self.CreateImportedLibvirtGuestOnHost(ctx, host, guest, &guestsDesc[i]); err != nil {
|
|
note = fmt.Sprintf("Guest %s create on host failed: %s", guestsDesc[i].Id, err)
|
|
success = false
|
|
} else {
|
|
note = fmt.Sprintf("Guest %s import success, started create on host", guestsDesc[i].Id)
|
|
success = true
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
if success {
|
|
db.OpsLog.LogEvent(host, db.ACT_HOST_IMPORT_LIBVIRT_SERVERS, note, self.UserCred)
|
|
} else {
|
|
log.Errorln(note)
|
|
if guest != nil {
|
|
guest.SetStatus(self.UserCred, compute.VM_IMPORT_FAILED, note)
|
|
}
|
|
db.OpsLog.LogEvent(host, db.ACT_HOST_IMPORT_LIBVIRT_SERVERS_FAIL, note, self.UserCred)
|
|
}
|
|
logclient.AddActionLogWithContext(ctx, host,
|
|
logclient.ACT_HOST_IMPORT_LIBVIRT_SERVERS, note, self.UserCred, success)
|
|
}
|
|
self.SetStageComplete(ctx, nil)
|
|
}
|
|
|
|
func (self *HostImportLibvirtServersTask) FillLibvirtGuestDesc(
|
|
ctx context.Context, host *models.SHost, guestDesc *compute.SImportGuestDesc,
|
|
) error {
|
|
// Generate new uuid for guest to prevent duplicate
|
|
guestDesc.Id = stringutils.UUID4()
|
|
guestDesc.HostId = host.Id
|
|
newName, err := db.GenerateName(ctx, models.GuestManager, self.UserCred, guestDesc.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
guestDesc.Name = newName
|
|
for i := 0; i < len(guestDesc.Disks); i++ {
|
|
guestDesc.Disks[i].DiskId = stringutils.UUID4()
|
|
if len(guestDesc.Disks[i].Backend) == 0 {
|
|
guestDesc.Disks[i].Backend = api.STORAGE_LOCAL
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Create sub task to create guest on host, and feedback disk real access path
|
|
func (self *HostImportLibvirtServersTask) CreateImportedLibvirtGuestOnHost(
|
|
ctx context.Context, host *models.SHost, guest *models.SGuest, guestDesc *compute.SImportGuestDesc,
|
|
) error {
|
|
task, err := taskman.TaskManager.NewTask(
|
|
ctx, "CreateImportedLibvirtGuestTask", guest, self.UserCred, nil, "", "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
disksPath := jsonutils.NewDict()
|
|
for _, disk := range guestDesc.Disks {
|
|
disksPath.Set(disk.DiskId, jsonutils.NewString(disk.AccessPath))
|
|
}
|
|
body := jsonutils.NewDict()
|
|
desc := guest.GetJsonDescAtHypervisor(ctx, host)
|
|
body.Set("desc", jsonutils.Marshal(desc))
|
|
body.Set("disks_path", disksPath)
|
|
if len(guestDesc.MonitorPath) > 0 {
|
|
body.Set("monitor_path", jsonutils.NewString(guestDesc.MonitorPath))
|
|
}
|
|
|
|
_, err = host.Request(ctx, self.UserCred, "POST",
|
|
fmt.Sprintf("/servers/%s/create-from-libvirt", guest.Id),
|
|
task.GetTaskRequestHeader(), body)
|
|
return err
|
|
}
|
|
|
|
func (self *HostImportLibvirtServersTask) TaskFailed(
|
|
ctx context.Context, host *models.SHost, reason jsonutils.JSONObject,
|
|
) {
|
|
self.SetStageFailed(ctx, reason)
|
|
db.OpsLog.LogEvent(host, db.ACT_HOST_IMPORT_LIBVIRT_SERVERS_FAIL, reason, self.UserCred)
|
|
logclient.AddActionLogWithContext(ctx, host,
|
|
logclient.ACT_HOST_IMPORT_LIBVIRT_SERVERS, reason, self.UserCred, false)
|
|
}
|
|
|
|
type CreateImportedLibvirtGuestTask struct {
|
|
taskman.STask
|
|
}
|
|
|
|
func (self *CreateImportedLibvirtGuestTask) OnInit(
|
|
ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject,
|
|
) {
|
|
guest := obj.(*models.SGuest)
|
|
disksPath, err := body.GetMap("disks_path")
|
|
if err != nil {
|
|
self.TaskFailed(ctx, guest, jsonutils.NewString("guest create on host no disk access path feedback"))
|
|
}
|
|
guestDisks, _ := guest.GetGuestDisks()
|
|
for i := 0; i < len(guestDisks); i++ {
|
|
disk := guestDisks[i].GetDisk()
|
|
if accessPath, ok := disksPath[disk.Id]; !ok {
|
|
self.TaskFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("Guest missing disk %s access path", disk.Id)))
|
|
return
|
|
} else {
|
|
_, err := db.Update(disk, func() error {
|
|
disk.AccessPath = accessPath.String()
|
|
if guestDisks[i].Index == 0 {
|
|
disk.DiskType = api.DISK_TYPE_SYS
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
self.TaskFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("Guest set disk access path error %s", err)))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
self.SetStage("OnGuestSync", nil)
|
|
guest.StartSyncTask(ctx, self.UserCred, false, self.GetTaskId())
|
|
}
|
|
|
|
func (self *CreateImportedLibvirtGuestTask) OnGuestSync(
|
|
ctx context.Context, guest *models.SGuest, body jsonutils.JSONObject,
|
|
) {
|
|
db.OpsLog.LogEvent(guest, db.ACT_GUEST_CREATE_FROM_IMPORT_SUCC, "", self.UserCred)
|
|
logclient.AddActionLogWithContext(ctx, guest,
|
|
logclient.ACT_GUEST_CREATE_FROM_IMPORT, "", self.UserCred, true)
|
|
self.SetStageComplete(ctx, nil)
|
|
}
|
|
|
|
func (self *CreateImportedLibvirtGuestTask) OnGuestSyncFailed(
|
|
ctx context.Context, guest *models.SGuest, body jsonutils.JSONObject,
|
|
) {
|
|
self.TaskFailed(ctx, guest, body)
|
|
}
|
|
|
|
func (self *CreateImportedLibvirtGuestTask) OnInitFailed(
|
|
ctx context.Context, guest *models.SGuest, body jsonutils.JSONObject,
|
|
) {
|
|
self.TaskFailed(ctx, guest, body)
|
|
}
|
|
|
|
func (self *CreateImportedLibvirtGuestTask) TaskFailed(
|
|
ctx context.Context, guest *models.SGuest, reason jsonutils.JSONObject,
|
|
) {
|
|
guest.SetStatus(self.UserCred, compute.VM_IMPORT_FAILED, reason.String())
|
|
db.OpsLog.LogEvent(guest, db.ACT_GUEST_CREATE_FROM_IMPORT_FAIL, reason, self.UserCred)
|
|
logclient.AddActionLogWithContext(ctx, guest,
|
|
logclient.ACT_GUEST_CREATE_FROM_IMPORT, reason, self.UserCred, false)
|
|
self.SetStageFailed(ctx, reason)
|
|
}
|