mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
Merge pull request #13577 from zexi/qemu-mem-snapshot
feat(host): server instance snapshot with memory
This commit is contained in:
@@ -51,7 +51,7 @@ func init() {
|
||||
type InstanceSnapshotShowOptions struct {
|
||||
ID string `help:"ID or Name of snapshot"`
|
||||
}
|
||||
R(&InstanceSnapshotShowOptions{}, "snapshot-show", "Show snapshot details", func(s *mcclient.ClientSession, args *InstanceSnapshotShowOptions) error {
|
||||
R(&InstanceSnapshotShowOptions{}, "instance-snapshot-show", "Show snapshot details", func(s *mcclient.ClientSession, args *InstanceSnapshotShowOptions) error {
|
||||
result, err := modules.InstanceSnapshots.Get(s, args.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -722,8 +722,9 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
type ServerCreateSnapshot struct {
|
||||
ID string `help:"ID or name of VM" json:"-"`
|
||||
SNAPSHOT string `help:"Instance snapshot name" json:"name"`
|
||||
ID string `help:"ID or name of VM" json:"-"`
|
||||
SNAPSHOT string `help:"Instance snapshot name" json:"name"`
|
||||
WithMemory bool `help:"Save memory state" json:"with_memory"`
|
||||
}
|
||||
R(&ServerCreateSnapshot{}, "instance-snapshot-create", "create instance snapshot", func(s *mcclient.ClientSession, opts *ServerCreateSnapshot) error {
|
||||
params := jsonutils.Marshal(opts)
|
||||
@@ -773,6 +774,7 @@ func init() {
|
||||
type ServerRollBackSnapshot struct {
|
||||
ID string `help:"ID or name of VM" json:"-"`
|
||||
InstanceSnapshot string `help:"Instance snapshot id or name" json:"instance_snapshot"`
|
||||
WithMemory bool `help:"Memory restore" json:"with_memory"`
|
||||
AutoStart bool `help:"Auto start VM"`
|
||||
}
|
||||
R(&ServerRollBackSnapshot{}, "instance-snapshot-reset", "reset instance snapshot", func(s *mcclient.ClientSession, opts *ServerRollBackSnapshot) error {
|
||||
|
||||
@@ -484,6 +484,8 @@ type ServerResetInput struct {
|
||||
InstanceSnapshot string `json:"instance_snapshot"`
|
||||
// 自动启动
|
||||
AutoStart *bool `json:"auto_start"`
|
||||
// 恢复内存
|
||||
WithMemory bool `json:"with_memory"`
|
||||
}
|
||||
|
||||
type ServerStopInput struct {
|
||||
@@ -791,6 +793,7 @@ type ServerSnapshotAndCloneInput struct {
|
||||
|
||||
type ServerInstanceSnapshot struct {
|
||||
ServerCreateSnapshotParams
|
||||
WithMemory bool `json:"with_memory"`
|
||||
}
|
||||
|
||||
type ServerCreateSnapshotParams struct {
|
||||
|
||||
34
pkg/apis/host/memory_snapshot.go
Normal file
34
pkg/apis/host/memory_snapshot.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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 host
|
||||
|
||||
type GuestMemorySnapshotRequest struct {
|
||||
InstanceSnapshotId string `json:"instance_snapshot_id"`
|
||||
}
|
||||
|
||||
type GuestMemorySnapshotResponse struct {
|
||||
MemorySnapshotPath string `json:"memory_snapshot_path"`
|
||||
SizeMB int64 `json:"size_mb"`
|
||||
}
|
||||
|
||||
type GuestMemorySnapshotDeleteRequest struct {
|
||||
InstanceSnapshotId string `json:"instance_snapshot_id"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type GuestMemorySnapshotResetRequest struct {
|
||||
InstanceSnapshotId string `json:"instance_snapshot_id"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
@@ -190,11 +190,11 @@ func (self *SBaseGuestDriver) RequestDeleteDetachedDisk(ctx context.Context, dis
|
||||
return fmt.Errorf("Not Implement")
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
func (self *SBaseGuestDriver) RequestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement")
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RqeuestResumeOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
func (self *SBaseGuestDriver) RequestResumeOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement")
|
||||
}
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ func (self *SESXiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gues
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SESXiGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
func (self *SESXiGuestDriver) RequestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
host, _ := guest.GetHost()
|
||||
if host == nil {
|
||||
@@ -451,7 +451,7 @@ func (self *SESXiGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SESXiGuestDriver) RqeuestResumeOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
func (self *SESXiGuestDriver) RequestResumeOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
host, _ := guest.GetHost()
|
||||
if host == nil {
|
||||
|
||||
@@ -601,7 +601,7 @@ func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SKVMGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
func (self *SKVMGuestDriver) RequestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
host, _ := guest.GetHost()
|
||||
url := fmt.Sprintf("%s/servers/%s/suspend", host.ManagerUri, guest.Id)
|
||||
header := self.getTaskRequestHeader(task)
|
||||
@@ -609,6 +609,14 @@ func (self *SKVMGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *mo
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SKVMGuestDriver) RequestResumeOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
host, _ := guest.GetHost()
|
||||
url := fmt.Sprintf("%s/servers/%s/resume", host.ManagerUri, guest.Id)
|
||||
header := self.getTaskRequestHeader(task)
|
||||
_, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, nil, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SKVMGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
input := new(api.ServerCreateInput)
|
||||
task.GetParams().Unmarshal(input)
|
||||
|
||||
@@ -4712,7 +4712,12 @@ func (self *SGuest) PerformInstanceSnapshot(
|
||||
return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
|
||||
}
|
||||
input.ServerCreateSnapshotParams = params
|
||||
instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(ctx, userCred, self, input.Name, false)
|
||||
if input.WithMemory {
|
||||
if self.Status != api.VM_RUNNING {
|
||||
return nil, httperrors.NewUnsupportOperationError("Can't save memory state when guest status is %q", self.Status)
|
||||
}
|
||||
}
|
||||
instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(ctx, userCred, self, input.Name, false, input.WithMemory)
|
||||
if err != nil {
|
||||
quotas.CancelPendingUsage(
|
||||
ctx, userCred, pendingUsage, pendingUsage, false)
|
||||
@@ -4795,10 +4800,14 @@ func (self *SGuest) PerformInstanceSnapshotReset(ctx context.Context, userCred m
|
||||
}
|
||||
|
||||
if instanceSnapshot.Status != api.INSTANCE_SNAPSHOT_READY {
|
||||
return nil, httperrors.NewBadRequestError("Instance sanpshot not ready")
|
||||
return nil, httperrors.NewBadRequestError("Instance snapshot not ready")
|
||||
}
|
||||
|
||||
err = self.StartSnapshotResetTask(ctx, userCred, instanceSnapshot, input.AutoStart)
|
||||
if input.WithMemory && !instanceSnapshot.WithMemory {
|
||||
return nil, httperrors.NewBadRequestError("Instance snapshot not with memory statefile")
|
||||
}
|
||||
|
||||
err = self.StartSnapshotResetTask(ctx, userCred, instanceSnapshot, input.AutoStart, input.WithMemory)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("start snapshot reset failed %s", err)
|
||||
}
|
||||
@@ -4806,14 +4815,15 @@ func (self *SGuest) PerformInstanceSnapshotReset(ctx context.Context, userCred m
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SGuest) StartSnapshotResetTask(ctx context.Context, userCred mcclient.TokenCredential, instanceSnapshot *SInstanceSnapshot, autoStart *bool) error {
|
||||
|
||||
func (self *SGuest) StartSnapshotResetTask(ctx context.Context, userCred mcclient.TokenCredential, instanceSnapshot *SInstanceSnapshot, autoStart *bool, withMemory bool) error {
|
||||
data := jsonutils.NewDict()
|
||||
if autoStart != nil && *autoStart {
|
||||
data.Set("auto_start", jsonutils.JSONTrue)
|
||||
}
|
||||
data.Add(jsonutils.NewBool(withMemory), "with_memory")
|
||||
self.SetStatus(userCred, api.VM_START_SNAPSHOT_RESET, "start snapshot reset task")
|
||||
instanceSnapshot.SetStatus(userCred, api.INSTANCE_SNAPSHOT_RESET, "start snapshot reset task")
|
||||
log.Errorf("====data: %s", data)
|
||||
if task, err := taskman.TaskManager.NewTask(
|
||||
ctx, "InstanceSnapshotResetTask", instanceSnapshot, userCred, data, "", "", nil,
|
||||
); err != nil {
|
||||
@@ -4886,7 +4896,7 @@ func (self *SGuest) PerformSnapshotAndClone(
|
||||
}
|
||||
instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(
|
||||
ctx, userCred, self, instanceSnapshotName,
|
||||
input.AutoDeleteInstanceSnapshot != nil && *input.AutoDeleteInstanceSnapshot)
|
||||
input.AutoDeleteInstanceSnapshot != nil && *input.AutoDeleteInstanceSnapshot, false)
|
||||
if err != nil {
|
||||
quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
|
||||
quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
|
||||
|
||||
@@ -155,10 +155,10 @@ type IGuestDriver interface {
|
||||
StartGuestAttachDiskTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict, parentTaskId string) error
|
||||
|
||||
StartSuspendTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict, parentTaskId string) error
|
||||
RqeuestSuspendOnHost(ctx context.Context, guest *SGuest, task taskman.ITask) error
|
||||
RequestSuspendOnHost(ctx context.Context, guest *SGuest, task taskman.ITask) error
|
||||
|
||||
StartResumeTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict, parentTaskId string) error
|
||||
RqeuestResumeOnHost(ctx context.Context, guest *SGuest, task taskman.ITask) error
|
||||
RequestResumeOnHost(ctx context.Context, guest *SGuest, task taskman.ITask) error
|
||||
|
||||
AllowReconfigGuest() bool
|
||||
DoGuestCreateDisksTask(ctx context.Context, guest *SGuest, task taskman.ITask) error
|
||||
|
||||
@@ -79,6 +79,14 @@ type SInstanceSnapshot struct {
|
||||
SizeMb int `nullable:"false"`
|
||||
// 镜像ID
|
||||
ImageId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
|
||||
// 是否保存内存
|
||||
WithMemory bool `default:"false" list:"user"`
|
||||
// 内存文件大小
|
||||
MemorySizeMB int `nullable:"true" list:"user"`
|
||||
// 内存文件所在宿主机
|
||||
MemoryFileHostId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
|
||||
// 内存文件路径
|
||||
MemoryFilePath string `width:"512" charset:"utf8" nullable:"true" list:"user"`
|
||||
}
|
||||
|
||||
type SInstanceSnapshotManager struct {
|
||||
@@ -361,7 +369,7 @@ func (manager *SInstanceSnapshotManager) fillInstanceSnapshot(ctx context.Contex
|
||||
instanceSnapshot.ServerMetadata = serverMetadata
|
||||
}
|
||||
|
||||
func (manager *SInstanceSnapshotManager) CreateInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, name string, autoDelete bool) (*SInstanceSnapshot, error) {
|
||||
func (manager *SInstanceSnapshotManager) CreateInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, name string, autoDelete bool, withMemory bool) (*SInstanceSnapshot, error) {
|
||||
instanceSnapshot := &SInstanceSnapshot{}
|
||||
instanceSnapshot.SetModelManager(manager, instanceSnapshot)
|
||||
instanceSnapshot.Name = name
|
||||
@@ -369,6 +377,8 @@ func (manager *SInstanceSnapshotManager) CreateInstanceSnapshot(ctx context.Cont
|
||||
manager.fillInstanceSnapshot(ctx, userCred, guest, instanceSnapshot)
|
||||
// compute size of instanceSnapshot
|
||||
instanceSnapshot.SizeMb = guest.getDiskSize()
|
||||
instanceSnapshot.WithMemory = withMemory
|
||||
instanceSnapshot.MemoryFileHostId = guest.HostId
|
||||
err := manager.TableSpec().Insert(ctx, instanceSnapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
@@ -1008,9 +1009,25 @@ func (self *SKVMRegionDriver) RequestDeleteInstanceSnapshot(ctx context.Context,
|
||||
}
|
||||
if len(snapshots) == 0 {
|
||||
task.SetStage("OnInstanceSnapshotDelete", nil)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
})
|
||||
if isp.WithMemory && isp.MemoryFileHostId != "" && isp.MemoryFilePath != "" {
|
||||
// request delete memory snapshot
|
||||
host := models.HostManager.FetchHostById(isp.MemoryFileHostId)
|
||||
if host == nil {
|
||||
return errors.Errorf("Not found host by %q", isp.MemoryFileHostId)
|
||||
}
|
||||
header := task.GetTaskRequestHeader()
|
||||
url := fmt.Sprintf("%s/servers/memory-snapshot", host.ManagerUri)
|
||||
if _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "DELETE", url, header, jsonutils.Marshal(&hostapi.GuestMemorySnapshotDeleteRequest{
|
||||
InstanceSnapshotId: isp.GetId(),
|
||||
Path: isp.MemoryFilePath,
|
||||
}), false); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1055,9 +1072,26 @@ func (self *SKVMRegionDriver) RequestResetToInstanceSnapshot(ctx context.Context
|
||||
diskIndex := int(diskIndexI64)
|
||||
if diskIndex >= len(disks) {
|
||||
task.SetStage("OnInstanceSnapshotReset", nil)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
})
|
||||
withMem := jsonutils.QueryBoolean(params, "with_memory", false)
|
||||
if isp.WithMemory && withMem {
|
||||
// reset do memory snapshot
|
||||
host, err := guest.GetHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header := task.GetTaskRequestHeader()
|
||||
url := fmt.Sprintf("%s/servers/%s/memory-snapshot-reset", host.ManagerUri, guest.GetId())
|
||||
if _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, jsonutils.Marshal(&hostapi.GuestMemorySnapshotResetRequest{
|
||||
InstanceSnapshotId: isp.GetId(),
|
||||
Path: isp.MemoryFilePath,
|
||||
}), false); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1103,9 +1137,24 @@ func (self *SKVMRegionDriver) RequestCreateInstanceSnapshot(ctx context.Context,
|
||||
diskIndex := int(diskIndexI64)
|
||||
if diskIndex >= len(disks) {
|
||||
task.SetStage("OnInstanceSnapshot", nil)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
})
|
||||
if isp.WithMemory {
|
||||
// request do memory snapshot
|
||||
host, err := guest.GetHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header := task.GetTaskRequestHeader()
|
||||
url := fmt.Sprintf("%s/servers/%s/memory-snapshot", host.ManagerUri, guest.GetId())
|
||||
if _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, jsonutils.Marshal(&hostapi.GuestMemorySnapshotRequest{
|
||||
InstanceSnapshotId: isp.GetId(),
|
||||
}), false); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -278,6 +278,10 @@ func (self *GuestMigrateTask) OnUndeployTargetGuestSuccFailed(ctx context.Contex
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) OnMigrateConfAndDiskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
|
||||
if data.Contains("dest_prepared_memory_snapshots") {
|
||||
msData, _ := data.Get("dest_prepared_memory_snapshots")
|
||||
self.Params.Set("dest_prepared_memory_snapshots", msData)
|
||||
}
|
||||
guestStatus, _ := self.Params.GetString("guest_status")
|
||||
if !jsonutils.QueryBoolean(self.Params, "is_rescue_mode", false) && (guestStatus == api.VM_RUNNING || guestStatus == api.VM_SUSPEND) {
|
||||
// Live migrate
|
||||
@@ -324,6 +328,43 @@ func (self *GuestMigrateTask) OnGuestStartSuccFailed(ctx context.Context, guest
|
||||
self.TaskFailed(ctx, guest, data)
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) getInstanceSnapShotsWithMemory(guest *models.SGuest) ([]*models.SInstanceSnapshot, error) {
|
||||
isps, err := guest.GetInstanceSnapshots()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetInstanceSnapshots")
|
||||
}
|
||||
ret := make([]*models.SInstanceSnapshot, 0)
|
||||
for idx := range isps {
|
||||
if isps[idx].WithMemory {
|
||||
ret = append(ret, &isps[idx])
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) getInstanceSnapShotIdsWithMemory(guest *models.SGuest) (*jsonutils.JSONArray, error) {
|
||||
isps, err := self.getInstanceSnapShotsWithMemory(guest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getInstanceSnapshotsWithMemory")
|
||||
}
|
||||
ret := []string{}
|
||||
for _, isp := range isps {
|
||||
ret = append(ret, isp.GetId())
|
||||
}
|
||||
return jsonutils.Marshal(ret).(*jsonutils.JSONArray), nil
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) setBodyMemorySnapshotParams(guest *models.SGuest, srcHost *models.SHost, body *jsonutils.JSONDict) error {
|
||||
isps, err := self.getInstanceSnapShotIdsWithMemory(guest)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getInstanceSnapShotsWithMemory")
|
||||
}
|
||||
memSnapshotUri := fmt.Sprintf("%s/download/memory_snapshots", srcHost.ManagerUri)
|
||||
body.Set("memory_snapshots_uri", jsonutils.NewString(memSnapshotUri))
|
||||
body.Set("src_memory_snapshots", isps)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) sharedStorageMigrateConf(ctx context.Context, guest *models.SGuest, targetHost *models.SHost) (*jsonutils.JSONDict, error) {
|
||||
body := jsonutils.NewDict()
|
||||
body.Set("is_local_storage", jsonutils.JSONFalse)
|
||||
@@ -331,6 +372,11 @@ func (self *GuestMigrateTask) sharedStorageMigrateConf(ctx context.Context, gues
|
||||
body.Set("qemu_cmdline", jsonutils.NewString(guest.GetQemuCmdline(self.UserCred)))
|
||||
targetDesc := guest.GetJsonDescAtHypervisor(ctx, targetHost)
|
||||
body.Set("desc", jsonutils.Marshal(targetDesc))
|
||||
|
||||
sourceHost, _ := guest.GetHost()
|
||||
if err := self.setBodyMemorySnapshotParams(guest, sourceHost, body); err != nil {
|
||||
return nil, errors.Wrap(err, "setBodyMemorySnapshotParams")
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
@@ -362,6 +408,11 @@ func (self *GuestMigrateTask) localStorageMigrateConf(ctx context.Context,
|
||||
body.Set("server_url", jsonutils.NewString(serverUrl))
|
||||
body.Set("qemu_version", jsonutils.NewString(guest.GetQemuVersion(self.UserCred)))
|
||||
body.Set("qemu_cmdline", jsonutils.NewString(guest.GetQemuCmdline(self.UserCred)))
|
||||
|
||||
if err := self.setBodyMemorySnapshotParams(guest, sourceHost, body); err != nil {
|
||||
return nil, errors.Wrap(err, "setBodyMemorySnapshotParams")
|
||||
}
|
||||
|
||||
targetDesc := guest.GetJsonDescAtHypervisor(ctx, targetHost)
|
||||
if len(targetDesc.Disks) == 0 {
|
||||
return nil, errors.Errorf("Get disksDesc error")
|
||||
@@ -530,7 +581,39 @@ func (self *GuestLiveMigrateTask) OnGuestSyncStatus(ctx context.Context, guest *
|
||||
self.TaskComplete(ctx, guest)
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) updateInstanceSnapshotMemory(ctx context.Context, guest *models.SGuest) error {
|
||||
if !self.Params.Contains("dest_prepared_memory_snapshots") {
|
||||
return nil
|
||||
}
|
||||
ms, err := self.Params.Get("dest_prepared_memory_snapshots")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get dest_prepared_memory_snapshots from params")
|
||||
}
|
||||
isps, err := self.getInstanceSnapShotsWithMemory(guest)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getInstanceSnapShotsWithMemory")
|
||||
}
|
||||
for _, isp := range isps {
|
||||
msPath, err := ms.GetString(isp.GetId())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "get instance snapshot %s memory path from dest prepared", isp.GetId())
|
||||
}
|
||||
if _, err := db.Update(isp, func() error {
|
||||
isp.MemoryFilePath = msPath
|
||||
isp.MemoryFileHostId = guest.HostId
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrapf(err, "update instance snapshot %q memory_filie_path", isp.GetId())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) TaskComplete(ctx context.Context, guest *models.SGuest) {
|
||||
if err := self.updateInstanceSnapshotMemory(ctx, guest); err != nil {
|
||||
self.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
self.SetStageComplete(ctx, nil)
|
||||
db.OpsLog.LogEvent(guest, db.ACT_MIGRATE, "Migrate success", self.UserCred)
|
||||
logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_MIGRATE, self.Params, self.UserCred, true)
|
||||
|
||||
@@ -37,7 +37,7 @@ func (self *GuestResumeTask) OnInit(ctx context.Context, obj db.IStandaloneModel
|
||||
guest := obj.(*models.SGuest)
|
||||
db.OpsLog.LogEvent(guest, db.ACT_RESUMING, "", self.UserCred)
|
||||
self.SetStage("OnResumeComplete", nil)
|
||||
err := guest.GetDriver().RqeuestResumeOnHost(ctx, guest, self)
|
||||
err := guest.GetDriver().RequestResumeOnHost(ctx, guest, self)
|
||||
if err != nil {
|
||||
self.OnResumeGuestFail(guest, err.Error())
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type GuestSuspendTask struct {
|
||||
@@ -37,8 +38,8 @@ func (self *GuestSuspendTask) OnInit(ctx context.Context, obj db.IStandaloneMode
|
||||
guest := obj.(*models.SGuest)
|
||||
db.OpsLog.LogEvent(guest, db.ACT_STOPPING, "", self.UserCred)
|
||||
guest.SetStatus(self.UserCred, api.VM_SUSPENDING, "GuestSusPendTask")
|
||||
self.SetStage("on_suspend_complete", nil)
|
||||
err := guest.GetDriver().RqeuestSuspendOnHost(ctx, guest, self)
|
||||
self.SetStage("OnSuspendComplete", nil)
|
||||
err := guest.GetDriver().RequestSuspendOnHost(ctx, guest, self)
|
||||
if err != nil {
|
||||
self.OnSuspendGuestFail(guest, err.Error())
|
||||
}
|
||||
@@ -48,6 +49,7 @@ func (self *GuestSuspendTask) OnSuspendComplete(ctx context.Context, obj db.ISta
|
||||
guest := obj.(*models.SGuest)
|
||||
guest.SetStatus(self.UserCred, api.VM_SUSPEND, "")
|
||||
db.OpsLog.LogEvent(guest, db.ACT_STOP, "", self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_SUSPEND, "success", self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -55,6 +57,7 @@ func (self *GuestSuspendTask) OnSuspendCompleteFailed(ctx context.Context, obj d
|
||||
guest := obj.(*models.SGuest)
|
||||
guest.SetStatus(self.UserCred, api.VM_RUNNING, "")
|
||||
db.OpsLog.LogEvent(guest, db.ACT_STOP_FAIL, err.String(), self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_SUSPEND, err.String(), self.UserCred, false)
|
||||
self.SetStageFailed(ctx, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
@@ -123,6 +124,21 @@ func (self *InstanceSnapshotCreateTask) OnKvmDiskSnapshotFailed(
|
||||
|
||||
func (self *InstanceSnapshotCreateTask) OnInstanceSnapshot(ctx context.Context, isp *models.SInstanceSnapshot, data jsonutils.JSONObject) {
|
||||
guest, _ := isp.GetGuest()
|
||||
if isp.WithMemory {
|
||||
resp := new(hostapi.GuestMemorySnapshotResponse)
|
||||
if err := data.Unmarshal(resp); err != nil {
|
||||
self.taskFail(ctx, isp, guest, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
if _, err := db.Update(isp, func() error {
|
||||
isp.MemorySizeMB = int(resp.SizeMB)
|
||||
isp.MemoryFilePath = resp.MemorySnapshotPath
|
||||
return nil
|
||||
}); err != nil {
|
||||
self.taskFail(ctx, isp, guest, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
self.taskComplete(ctx, isp, guest, data)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ func (self *InstanceSnapshotResetTask) OnInit(
|
||||
self.SetStage("OnInstanceSnapshotReset", nil)
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("disk_index", jsonutils.NewInt(0))
|
||||
withMem := jsonutils.QueryBoolean(self.Params, "with_memory", false)
|
||||
params.Set("with_memory", jsonutils.NewBool(withMem))
|
||||
if err := isp.GetRegionDriver().RequestResetToInstanceSnapshot(ctx, guest, isp, self, params); err != nil {
|
||||
self.taskFail(ctx, isp, guest, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
@@ -91,6 +93,8 @@ func (self *InstanceSnapshotResetTask) OnKvmDiskReset(
|
||||
}
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("disk_index", jsonutils.NewInt(diskIndex+1))
|
||||
withMem := jsonutils.QueryBoolean(self.Params, "with_memory", false)
|
||||
params.Set("with_memory", jsonutils.NewBool(withMem))
|
||||
if err := isp.GetRegionDriver().RequestResetToInstanceSnapshot(ctx, guest, isp, self, params); err != nil {
|
||||
self.taskFail(ctx, isp, guest, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
||||
@@ -55,6 +56,12 @@ func AddDownloadHandler(prefix string, app *appsrv.Application) {
|
||||
nil, "snapshot_download", nil)
|
||||
customizeHandlerInfo(hi)
|
||||
|
||||
hi = app.AddHandler2("GET", fmt.Sprintf(
|
||||
"%s/%s/memory_snapshots/<serverId>/<instanceSnapshotId>",
|
||||
prefix, kerword), auth.Authenticate(memorySnapshotDownload),
|
||||
nil, "memory_snapshot_download", nil)
|
||||
customizeHandlerInfo(hi)
|
||||
|
||||
hi = app.AddHandler2("HEAD", fmt.Sprintf("%s/%s/disks/<storageId>/<diskId>",
|
||||
prefix, kerword), auth.Authenticate(diskHead),
|
||||
nil, "head_disk_download", nil)
|
||||
@@ -70,6 +77,11 @@ func AddDownloadHandler(prefix string, app *appsrv.Application) {
|
||||
fmt.Sprintf("%s/%s/images/<id>", prefix, kerword), auth.Authenticate(imageCacheHead),
|
||||
nil, "head_image", nil)
|
||||
customizeHandlerInfo(hi)
|
||||
|
||||
hi = app.AddHandler2("HEAD",
|
||||
fmt.Sprintf("%s/%s/memory_snapshots/<instance_snapshot_id>", prefix, kerword), auth.Authenticate(memorySnapshotHead),
|
||||
nil, "head_memory_snapshot", nil)
|
||||
customizeHandlerInfo(hi)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,3 +242,35 @@ func imageCacheHead(ctx context.Context, w http.ResponseWriter, r *http.Request)
|
||||
hostutils.Response(ctx, w, err)
|
||||
}
|
||||
}
|
||||
|
||||
func getInstanceSnapShotPath(ctx context.Context, w http.ResponseWriter, r *http.Request) string {
|
||||
var (
|
||||
params, _, _ = appsrv.FetchEnv(ctx, w, r)
|
||||
serverId = params["<serverId>"]
|
||||
instanceSnapshotId = params["<instanceSnapshotId>"]
|
||||
)
|
||||
msPath := guestman.GetMemorySnapshotPath(serverId, instanceSnapshotId)
|
||||
return msPath
|
||||
}
|
||||
|
||||
func memorySnapshotDownload(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
msPath := getInstanceSnapShotPath(ctx, w, r)
|
||||
var compress = isCompress(r)
|
||||
var sparse = isSparse(r)
|
||||
hand := NewSnapshotDownloadProvider(w,
|
||||
compress, sparse, options.HostOptions.BandwidthLimit, msPath)
|
||||
if err := hand.Start(); err != nil {
|
||||
hostutils.Response(ctx, w, err)
|
||||
}
|
||||
}
|
||||
|
||||
func memorySnapshotHead(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
msPath := getInstanceSnapShotPath(ctx, w, r)
|
||||
var compress = isCompress(r)
|
||||
var sparse = isSparse(r)
|
||||
hand := NewSnapshotDownloadProvider(w,
|
||||
compress, sparse, options.HostOptions.BandwidthLimit, msPath)
|
||||
if err := hand.HandlerHead(); err != nil {
|
||||
hostutils.Response(ctx, w, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
@@ -59,39 +60,45 @@ func AddGuestTaskHandler(prefix string, app *appsrv.Application) {
|
||||
auth.Authenticate(deleteGuest))
|
||||
|
||||
for action, f := range map[string]actionFunc{
|
||||
"create": guestCreate,
|
||||
"deploy": guestDeploy,
|
||||
"rebuild": guestRebuild,
|
||||
"start": guestStart,
|
||||
"stop": guestStop,
|
||||
"monitor": guestMonitor,
|
||||
"sync": guestSync,
|
||||
"suspend": guestSuspend,
|
||||
"io-throttle": guestIoThrottle,
|
||||
"snapshot": guestSnapshot,
|
||||
"delete-snapshot": guestDeleteSnapshot,
|
||||
"reload-disk-snapshot": guestReloadDiskSnapshot,
|
||||
"src-prepare-migrate": guestSrcPrepareMigrate,
|
||||
"dest-prepare-migrate": guestDestPrepareMigrate,
|
||||
"live-migrate": guestLiveMigrate,
|
||||
"resume": guestResume,
|
||||
"drive-mirror": guestDriveMirror,
|
||||
"hotplug-cpu-mem": guestHotplugCpuMem,
|
||||
"cancel-block-jobs": guestCancelBlockJobs,
|
||||
"create-from-libvirt": guestCreateFromLibvirt,
|
||||
"create-form-esxi": guestCreateFromEsxi,
|
||||
"open-forward": guestOpenForward,
|
||||
"list-forward": guestListForward,
|
||||
"close-forward": guestCloseForward,
|
||||
"storage-clone-disk": guestStorageCloneDisk,
|
||||
"cpuset": guestCPUSet,
|
||||
"cpuset-remove": guestCPUSetRemove,
|
||||
"create": guestCreate,
|
||||
"deploy": guestDeploy,
|
||||
"rebuild": guestRebuild,
|
||||
"start": guestStart,
|
||||
"stop": guestStop,
|
||||
"monitor": guestMonitor,
|
||||
"sync": guestSync,
|
||||
"suspend": guestSuspend,
|
||||
"io-throttle": guestIoThrottle,
|
||||
"snapshot": guestSnapshot,
|
||||
"delete-snapshot": guestDeleteSnapshot,
|
||||
"reload-disk-snapshot": guestReloadDiskSnapshot,
|
||||
"src-prepare-migrate": guestSrcPrepareMigrate,
|
||||
"dest-prepare-migrate": guestDestPrepareMigrate,
|
||||
"live-migrate": guestLiveMigrate,
|
||||
"resume": guestResume,
|
||||
"drive-mirror": guestDriveMirror,
|
||||
"hotplug-cpu-mem": guestHotplugCpuMem,
|
||||
"cancel-block-jobs": guestCancelBlockJobs,
|
||||
"create-from-libvirt": guestCreateFromLibvirt,
|
||||
"create-form-esxi": guestCreateFromEsxi,
|
||||
"open-forward": guestOpenForward,
|
||||
"list-forward": guestListForward,
|
||||
"close-forward": guestCloseForward,
|
||||
"storage-clone-disk": guestStorageCloneDisk,
|
||||
"cpuset": guestCPUSet,
|
||||
"cpuset-remove": guestCPUSetRemove,
|
||||
"memory-snapshot": guestMemorySnapshot,
|
||||
"memory-snapshot-reset": guestMemorySnapshotReset,
|
||||
} {
|
||||
app.AddHandler("POST",
|
||||
fmt.Sprintf("%s/%s/<sid>/%s", prefix, keyWord, action),
|
||||
auth.Authenticate(guestActions(f)),
|
||||
)
|
||||
}
|
||||
|
||||
app.AddHandler("DELETE",
|
||||
fmt.Sprintf("%s/%s/memory-snapshot", prefix, keyWord),
|
||||
auth.Authenticate(guestMemorySnapshotDelete))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,6 +381,15 @@ func guestDestPrepareMigrate(ctx context.Context, sid string, body jsonutils.JSO
|
||||
}
|
||||
params.RebaseDisks = jsonutils.QueryBoolean(body, "rebase_disks", false)
|
||||
}
|
||||
|
||||
msUri, err := body.GetString("memory_snapshots_uri")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("memory_snapshots_uri")
|
||||
}
|
||||
params.MemorySnapshotsUri = msUri
|
||||
msIds, _ := jsonutils.GetStringArray(body, "src_memory_snapshots")
|
||||
params.SrcMemorySnapshots = msIds
|
||||
|
||||
hostutils.DelayTask(ctx, guestman.GetGuestManager().DestPrepareMigrate, params)
|
||||
return nil, nil
|
||||
}
|
||||
@@ -644,3 +660,56 @@ func guestCPUSetRemove(ctx context.Context, sid string, body jsonutils.JSONObjec
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func guestMemorySnapshot(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) {
|
||||
input := new(hostapi.GuestMemorySnapshotRequest)
|
||||
if err := body.Unmarshal(input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gm := guestman.GetGuestManager()
|
||||
hostutils.DelayTaskWithoutReqctx(ctx, gm.DoMemorySnapshot, &guestman.SMemorySnapshot{
|
||||
GuestMemorySnapshotRequest: input,
|
||||
Sid: sid,
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func guestMemorySnapshotReset(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) {
|
||||
input := new(hostapi.GuestMemorySnapshotResetRequest)
|
||||
if err := body.Unmarshal(input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.InstanceSnapshotId == "" {
|
||||
return nil, httperrors.NewMissingParameterError("instance_snapshot_id")
|
||||
}
|
||||
if input.Path == "" {
|
||||
return nil, httperrors.NewMissingParameterError("path")
|
||||
}
|
||||
gm := guestman.GetGuestManager()
|
||||
hostutils.DelayTaskWithoutReqctx(ctx, gm.DoResetMemorySnapshot, &guestman.SMemorySnapshotReset{
|
||||
GuestMemorySnapshotResetRequest: input,
|
||||
Sid: sid,
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func guestMemorySnapshotDelete(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, r)
|
||||
input := new(hostapi.GuestMemorySnapshotDeleteRequest)
|
||||
if err := body.Unmarshal(input); err != nil {
|
||||
hostutils.Response(ctx, w, err)
|
||||
return
|
||||
}
|
||||
if input.InstanceSnapshotId == "" {
|
||||
hostutils.Response(ctx, w, httperrors.NewMissingParameterError("instance_snapshot_id"))
|
||||
return
|
||||
}
|
||||
if input.Path == "" {
|
||||
hostutils.Response(ctx, w, httperrors.NewMissingParameterError("path"))
|
||||
return
|
||||
}
|
||||
gm := guestman.GetGuestManager()
|
||||
hostutils.DelayTask(ctx, gm.DoDeleteMemorySnapshot, &guestman.SMemorySnapshotDelete{
|
||||
GuestMemorySnapshotDeleteRequest: input,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package guestman
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/esxi/vcenter"
|
||||
)
|
||||
@@ -55,6 +56,9 @@ type SDestPrepareMigrate struct {
|
||||
Desc jsonutils.JSONObject
|
||||
DisksBackingFile jsonutils.JSONObject
|
||||
SrcSnapshots jsonutils.JSONObject
|
||||
|
||||
MemorySnapshotsUri string
|
||||
SrcMemorySnapshots []string
|
||||
}
|
||||
|
||||
type SLiveMigrate struct {
|
||||
@@ -88,6 +92,20 @@ type SDiskSnapshot struct {
|
||||
Disk storageman.IDisk
|
||||
}
|
||||
|
||||
type SMemorySnapshot struct {
|
||||
*hostapi.GuestMemorySnapshotRequest
|
||||
Sid string
|
||||
}
|
||||
|
||||
type SMemorySnapshotReset struct {
|
||||
*hostapi.GuestMemorySnapshotResetRequest
|
||||
Sid string
|
||||
}
|
||||
|
||||
type SMemorySnapshotDelete struct {
|
||||
*hostapi.GuestMemorySnapshotDeleteRequest
|
||||
}
|
||||
|
||||
type SDiskBackup struct {
|
||||
Sid string
|
||||
SnapshotId string
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -41,11 +42,13 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/remotefile"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/cgrouputils"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/netutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/timeutils2"
|
||||
)
|
||||
|
||||
@@ -843,6 +846,16 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
|
||||
|
||||
}
|
||||
|
||||
body := jsonutils.NewDict()
|
||||
|
||||
if len(migParams.SrcMemorySnapshots) > 0 {
|
||||
preparedMs, err := m.destinationPrepareMigrateMemorySnapshots(ctx, migParams.Sid, migParams.MemorySnapshotsUri, migParams.SrcMemorySnapshots)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "destination prepare migrate memory snapshots")
|
||||
}
|
||||
body.Add(jsonutils.Marshal(preparedMs), "dest_prepared_memory_snapshots")
|
||||
}
|
||||
|
||||
if migParams.LiveMigrate {
|
||||
startParams := jsonutils.NewDict()
|
||||
startParams.Set("qemu_version", jsonutils.NewString(migParams.QemuVersion))
|
||||
@@ -859,7 +872,26 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
|
||||
hostutils.UpdateServerProgress(context.Background(), migParams.Sid, 100.0, 0)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (m *SGuestManager) destinationPrepareMigrateMemorySnapshots(ctx context.Context, serverId string, uri string, ids []string) (map[string]string, error) {
|
||||
ret := make(map[string]string, 0)
|
||||
for _, id := range ids {
|
||||
url := fmt.Sprintf("%s/%s/%s", uri, serverId, id)
|
||||
msPath := GetMemorySnapshotPath(serverId, id)
|
||||
dir := filepath.Dir(msPath)
|
||||
if err := procutils.NewRemoteCommandAsFarAsPossible("mkdir", "-p", dir).Run(); err != nil {
|
||||
return nil, errors.Wrapf(err, "mkdir -p %q", dir)
|
||||
}
|
||||
remotefile := remotefile.NewRemoteFile(ctx, url, msPath, false, "", -1, nil, "", "")
|
||||
if err := remotefile.Fetch(nil); err != nil {
|
||||
return nil, errors.Wrapf(err, "fetch memory snapshot file %s", url)
|
||||
} else {
|
||||
ret[id] = msPath
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (m *SGuestManager) LiveMigrate(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
@@ -963,6 +995,41 @@ func (m *SGuestManager) DeleteSnapshot(ctx context.Context, params interface{})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SGuestManager) DoMemorySnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
input, ok := params.(*SMemorySnapshot)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
|
||||
guest, _ := m.GetServer(input.Sid)
|
||||
return guest.ExecMemorySnapshotTask(ctx, input.GuestMemorySnapshotRequest)
|
||||
}
|
||||
|
||||
func (m *SGuestManager) DoResetMemorySnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
input, ok := params.(*SMemorySnapshotReset)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
|
||||
guest, _ := m.GetServer(input.Sid)
|
||||
return guest.ExecMemorySnapshotResetTask(ctx, input.GuestMemorySnapshotResetRequest)
|
||||
}
|
||||
|
||||
func (m *SGuestManager) DoDeleteMemorySnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
input, ok := params.(*SMemorySnapshotDelete)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
|
||||
if err := procutils.NewRemoteCommandAsFarAsPossible("rm", input.Path).Run(); err != nil {
|
||||
if !strings.Contains(strings.ToLower(err.Error()), "No such file or directory") {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
log.Infof("Memory snapshot file %q removed", input.Path)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *SGuestManager) Resume(ctx context.Context, sid string, isLiveMigrate bool, cleanTLS bool) (jsonutils.JSONObject, error) {
|
||||
guest, _ := m.GetServer(sid)
|
||||
if guest.IsStopping() || guest.IsStopped() {
|
||||
|
||||
@@ -96,6 +96,74 @@ func (s *SGuestStopTask) CheckGuestRunningLater() {
|
||||
s.checkGuestRunning()
|
||||
}
|
||||
|
||||
type SGuestSuspendTask struct {
|
||||
*SKVMGuestInstance
|
||||
ctx context.Context
|
||||
onFinishCallback func(*SGuestSuspendTask, string)
|
||||
}
|
||||
|
||||
func NewGuestSuspendTask(
|
||||
guest *SKVMGuestInstance,
|
||||
ctx context.Context,
|
||||
onFinishCallback func(*SGuestSuspendTask, string),
|
||||
) *SGuestSuspendTask {
|
||||
t := &SGuestSuspendTask{
|
||||
SKVMGuestInstance: guest,
|
||||
ctx: ctx,
|
||||
}
|
||||
if onFinishCallback == nil {
|
||||
onFinishCallback = t.onSaveMemStateComplete
|
||||
}
|
||||
t.onFinishCallback = onFinishCallback
|
||||
return t
|
||||
}
|
||||
|
||||
func (s *SGuestSuspendTask) Start() {
|
||||
s.Monitor.SimpleCommand("stop", s.onSuspendGuest)
|
||||
}
|
||||
|
||||
func (s *SGuestSuspendTask) GetStateFilePath() string {
|
||||
return s.SKVMGuestInstance.GetStateFilePath("")
|
||||
}
|
||||
|
||||
func (s *SGuestSuspendTask) onSuspendGuest(results string) {
|
||||
if strings.Contains(strings.ToLower(results), "error") {
|
||||
hostutils.TaskFailed(s.ctx, fmt.Sprintf("Suspend error: %s", results))
|
||||
return
|
||||
}
|
||||
statFile := s.GetStateFilePath()
|
||||
s.Monitor.SaveState(statFile, s.onSaveMemStateWait)
|
||||
}
|
||||
|
||||
func (s *SGuestSuspendTask) onSaveMemStateWait(results string) {
|
||||
if strings.Contains(strings.ToLower(results), "error") {
|
||||
hostutils.TaskFailed(s.ctx, fmt.Sprintf("Save memory state error: %s", results))
|
||||
// TODO: send cont command
|
||||
return
|
||||
}
|
||||
s.Monitor.GetMigrateStatus(s.onSaveMemStateCheck)
|
||||
}
|
||||
|
||||
func (s *SGuestSuspendTask) onSaveMemStateCheck(status string) {
|
||||
if status == "failed" {
|
||||
hostutils.TaskFailed(s.ctx, fmt.Sprintf("Save memory state failed"))
|
||||
// TODO: send cont command
|
||||
return
|
||||
} else if status != "completed" {
|
||||
time.Sleep(time.Second * 3)
|
||||
log.Infof("Server %s saving memory state status %q", s.GetName(), status)
|
||||
s.onSaveMemStateWait("")
|
||||
} else {
|
||||
log.Infof("Server %s save memory completed", s.GetName())
|
||||
s.onFinishCallback(s, s.GetStateFilePath())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SGuestSuspendTask) onSaveMemStateComplete(_ *SGuestSuspendTask, _ string) {
|
||||
log.Infof("Server %s memory state saved, stopping server", s.GetName())
|
||||
s.ExecStopTask(s.ctx, int64(3))
|
||||
}
|
||||
|
||||
/**
|
||||
* GuestSyncConfigTaskExecutor
|
||||
**/
|
||||
@@ -782,6 +850,8 @@ type SGuestResumeTask struct {
|
||||
|
||||
isTimeout bool
|
||||
cleanTLS bool
|
||||
|
||||
getTaskData func() (jsonutils.JSONObject, error)
|
||||
}
|
||||
|
||||
func NewGuestResumeTask(ctx context.Context, s *SKVMGuestInstance, isTimeout bool, cleanTLS bool) *SGuestResumeTask {
|
||||
@@ -790,6 +860,7 @@ func NewGuestResumeTask(ctx context.Context, s *SKVMGuestInstance, isTimeout boo
|
||||
ctx: ctx,
|
||||
isTimeout: isTimeout,
|
||||
cleanTLS: cleanTLS,
|
||||
getTaskData: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,6 +881,10 @@ func (s *SGuestResumeTask) Start() {
|
||||
s.confirmRunning()
|
||||
}
|
||||
|
||||
func (s *SGuestResumeTask) GetStateFilePath() string {
|
||||
return s.SKVMGuestInstance.GetStateFilePath("")
|
||||
}
|
||||
|
||||
func (s *SGuestResumeTask) Stop() {
|
||||
// TODO
|
||||
// stop stream disk
|
||||
@@ -879,9 +954,25 @@ func (s *SGuestResumeTask) onResumeSucc(res string) {
|
||||
s.confirmRunning()
|
||||
}
|
||||
|
||||
func (s *SGuestResumeTask) SetGetTaskData(f func() (jsonutils.JSONObject, error)) {
|
||||
s.getTaskData = f
|
||||
}
|
||||
|
||||
func (s *SGuestResumeTask) onStartRunning() {
|
||||
s.removeStatefile()
|
||||
if s.ctx != nil && len(appctx.AppContextTaskId(s.ctx)) > 0 {
|
||||
hostutils.TaskComplete(s.ctx, nil)
|
||||
var (
|
||||
data jsonutils.JSONObject
|
||||
err error
|
||||
)
|
||||
if s.getTaskData != nil {
|
||||
data, err = s.getTaskData()
|
||||
if err != nil {
|
||||
s.taskFailed(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
hostutils.TaskComplete(s.ctx, data)
|
||||
}
|
||||
if options.HostOptions.SetVncPassword {
|
||||
s.SetVncPassword()
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -33,6 +34,7 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/appctx"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
|
||||
@@ -102,6 +104,14 @@ func (s *SKVMGuestInstance) getStateFilePathRootPrefix() string {
|
||||
return path.Join(s.HomeDir(), STATE_FILE_PREFIX)
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) GetStateFilePath(version string) string {
|
||||
p := s.getStateFilePathRootPrefix()
|
||||
if version != "" {
|
||||
p = fmt.Sprintf("%s_%s", p, version)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getQemuLogPath() string {
|
||||
return path.Join(s.HomeDir(), "qemu.log")
|
||||
}
|
||||
@@ -1038,6 +1048,13 @@ func (s *SKVMGuestInstance) delTmpDisks(ctx context.Context, migrated bool) erro
|
||||
return err
|
||||
}
|
||||
}
|
||||
if migrated {
|
||||
// remove memory snapshot files
|
||||
dir := GetMemorySnapshotPath(s.GetId(), "")
|
||||
if err := procutils.NewRemoteCommandAsFarAsPossible("rm", "-rf", dir).Run(); err != nil {
|
||||
return errors.Wrapf(err, "remove dir %q", dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -1115,7 +1132,7 @@ func (s *SKVMGuestInstance) ExecStopTask(ctx context.Context, params interface{}
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) ExecSuspendTask(ctx context.Context) {
|
||||
// TODO
|
||||
NewGuestSuspendTask(s, ctx, nil).Start()
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) GetNicDescMatch(mac, ip, port, bridge string) jsonutils.JSONObject {
|
||||
@@ -1667,9 +1684,9 @@ func (s *SKVMGuestInstance) ListStateFilePaths() []string {
|
||||
return ret
|
||||
}
|
||||
|
||||
// 好像不用了
|
||||
func (s *SKVMGuestInstance) CleanStatefiles() {
|
||||
for _, stateFile := range s.ListStateFilePaths() {
|
||||
log.Infof("Server %s remove statefile %q", s.GetName(), stateFile)
|
||||
if _, err := procutils.NewCommand("mountpoint", stateFile).Output(); err == nil {
|
||||
if output, err := procutils.NewCommand("umount", stateFile).Output(); err != nil {
|
||||
log.Errorf("umount %s failed: %s, %s", stateFile, err, output)
|
||||
@@ -1781,6 +1798,58 @@ func (s *SKVMGuestInstance) deleteStaticSnapshotFile(
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func GetMemorySnapshotPath(serverId, instanceSnapshotId string) string {
|
||||
dir := options.HostOptions.MemorySnapshotsPath
|
||||
memSnapPath := filepath.Join(dir, serverId, instanceSnapshotId)
|
||||
return memSnapPath
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) ExecMemorySnapshotTask(ctx context.Context, input *hostapi.GuestMemorySnapshotRequest) (jsonutils.JSONObject, error) {
|
||||
if !s.IsRunning() {
|
||||
return nil, errors.Errorf("Server is not running status")
|
||||
}
|
||||
if s.IsSuspend() {
|
||||
return nil, errors.Errorf("Server is suspend status")
|
||||
}
|
||||
memSnapPath := GetMemorySnapshotPath(s.GetId(), input.InstanceSnapshotId)
|
||||
dir := filepath.Dir(memSnapPath)
|
||||
if err := procutils.NewRemoteCommandAsFarAsPossible("mkdir", "-p", dir).Run(); err != nil {
|
||||
return nil, errors.Wrapf(err, "mkdir -p %q", dir)
|
||||
}
|
||||
NewGuestSuspendTask(s, ctx, func(_ *SGuestSuspendTask, memStatPath string) {
|
||||
log.Infof("Memory state file %q saved, move it to %q", memStatPath, memSnapPath)
|
||||
sizeBytes := fileutils2.FileSize(memStatPath)
|
||||
sizeMB := sizeBytes / 1024
|
||||
if err := procutils.NewRemoteCommandAsFarAsPossible("mv", memStatPath, memSnapPath).Run(); err != nil {
|
||||
hostutils.TaskFailed(ctx, fmt.Sprintf("move statefile %q to memory snapshot %q: %v", memStatPath, memSnapPath, err))
|
||||
return
|
||||
}
|
||||
resumeTask := NewGuestResumeTask(ctx, s, false, false)
|
||||
resumeTask.SetGetTaskData(func() (jsonutils.JSONObject, error) {
|
||||
resp := &hostapi.GuestMemorySnapshotResponse{
|
||||
MemorySnapshotPath: memSnapPath,
|
||||
SizeMB: sizeMB,
|
||||
}
|
||||
return jsonutils.Marshal(resp), nil
|
||||
})
|
||||
resumeTask.Start()
|
||||
}).Start()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) ExecMemorySnapshotResetTask(ctx context.Context, input *hostapi.GuestMemorySnapshotResetRequest) (jsonutils.JSONObject, error) {
|
||||
if !s.IsStopped() {
|
||||
return nil, errors.Errorf("Server is not stopped status")
|
||||
}
|
||||
memStatPath := s.GetStateFilePath("")
|
||||
if err := procutils.NewRemoteCommandAsFarAsPossible("ln", "-s", input.Path, memStatPath).Run(); err != nil {
|
||||
hostutils.TaskFailed(ctx, fmt.Sprintf("move %q to %q: %v", input.Path, memStatPath, err))
|
||||
return nil, err
|
||||
}
|
||||
hostutils.TaskComplete(ctx, nil)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) PrepareDisksMigrate(liveMigrage bool) (*jsonutils.JSONDict, error) {
|
||||
disksBackFile := jsonutils.NewDict()
|
||||
disks, _ := s.Desc.GetArray("disks")
|
||||
|
||||
@@ -307,7 +307,7 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (strin
|
||||
}
|
||||
cmd += diskScripts
|
||||
|
||||
// cmd += fmt.Sprintf("STATE_FILE=`ls -d %s* | head -n 1`\n", s.getStateFilePathRootPrefix())
|
||||
cmd += fmt.Sprintf("STATE_FILE=`ls -d %s* | head -n 1`\n", s.getStateFilePathRootPrefix())
|
||||
cmd += fmt.Sprintf("PID_FILE=%s\n", input.PidFilePath)
|
||||
|
||||
var qemuCmd = qemutils.GetQemu(string(input.QemuVersion))
|
||||
@@ -316,21 +316,25 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (strin
|
||||
}
|
||||
|
||||
cmd += fmt.Sprintf("DEFAULT_QEMU_CMD='%s'\n", qemuCmd)
|
||||
// cmd += "if [ -n \"$STATE_FILE\" ]; then\n"
|
||||
// cmd += " QEMU_VER=`echo $STATE_FILE" +
|
||||
// ` | grep -o '_[[:digit:]]\+\.[[:digit:]]\+.*'` + "`\n"
|
||||
// cmd += " QEMU_CMD=\"qemu-system-x86_64\"\n"
|
||||
// cmd += " QEMU_LOCAL_PATH=\"/usr/local/bin/$QEMU_CMD\"\n"
|
||||
// cmd += " QEMU_LOCAL_PATH_VER=\"/usr/local/qemu-$QEMU_VER/bin/$QEMU_CMD\"\n"
|
||||
// cmd += " QEMU_BIN_PATH=\"/usr/bin/$QEMU_CMD\"\n"
|
||||
// cmd += " if [ -f \"$QEMU_LOCAL_PATH_VER\" ]; then\n"
|
||||
// cmd += " QEMU_CMD=$QEMU_LOCAL_PATH_VER\n"
|
||||
// cmd += " elif [ -f \"$QEMU_LOCAL_PATH\" ]; then\n"
|
||||
// cmd += " QEMU_CMD=$QEMU_LOCAL_PATH\n"
|
||||
// cmd += " elif [ -f \"$QEMU_BIN_PATH\" ]; then\n"
|
||||
// cmd += " QEMU_CMD=$QEMU_BIN_PATH\n"
|
||||
// cmd += " fi\n"
|
||||
// cmd += "else\n"
|
||||
/*
|
||||
* cmd += "if [ -n \"$STATE_FILE\" ]; then\n"
|
||||
* cmd += " QEMU_VER=`echo $STATE_FILE" +
|
||||
* ` | grep -o '_[[:digit:]]\+\.[[:digit:]]\+.*'` + "`\n"
|
||||
* cmd += " QEMU_CMD=\"qemu-system-x86_64\"\n"
|
||||
* cmd += " QEMU_LOCAL_PATH=\"/usr/local/bin/$QEMU_CMD\"\n"
|
||||
* cmd += " QEMU_LOCAL_PATH_VER=\"/usr/local/qemu-$QEMU_VER/bin/$QEMU_CMD\"\n"
|
||||
* cmd += " QEMU_BIN_PATH=\"/usr/bin/$QEMU_CMD\"\n"
|
||||
* cmd += " if [ -f \"$QEMU_LOCAL_PATH_VER\" ]; then\n"
|
||||
* cmd += " QEMU_CMD=$QEMU_LOCAL_PATH_VER\n"
|
||||
* cmd += " elif [ -f \"$QEMU_LOCAL_PATH\" ]; then\n"
|
||||
* cmd += " QEMU_CMD=$QEMU_LOCAL_PATH\n"
|
||||
* cmd += " elif [ -f \"$QEMU_BIN_PATH\" ]; then\n"
|
||||
* cmd += " QEMU_CMD=$QEMU_BIN_PATH\n"
|
||||
* cmd += " fi\n"
|
||||
* cmd += "else\n"
|
||||
* cmd += " QEMU_CMD=$DEFAULT_QEMU_CMD\n"
|
||||
* cmd += "fi\n"
|
||||
*/
|
||||
cmd += "QEMU_CMD=$DEFAULT_QEMU_CMD\n"
|
||||
if s.IsKvmSupport() && !options.HostOptions.DisableKVM {
|
||||
cmd += "QEMU_CMD_KVM_ARG=-enable-kvm\n"
|
||||
@@ -522,15 +526,15 @@ function nic_mtu() {
|
||||
return "", errors.Wrap(err, "GenerateStartCommand")
|
||||
}
|
||||
cmd = fmt.Sprintf("%s %s", cmd, qemuOpts)
|
||||
|
||||
cmd += "\"\n"
|
||||
// cmd += "if [ ! -z \"$STATE_FILE\" ] && [ -d \"$STATE_FILE\" ] && [ -f \"$STATE_FILE/content\" ]; then\n"
|
||||
// cmd += " $CMD --incoming \"exec: cat $STATE_FILE/content\"\n"
|
||||
// cmd += "elif [ ! -z \"$STATE_FILE\" ] && [ -f $STATE_FILE ]; then\n"
|
||||
// cmd += " $CMD --incoming \"exec: cat $STATE_FILE\"\n"
|
||||
// cmd += "else\n"
|
||||
cmd += "eval $CMD\n"
|
||||
// cmd += "fi\n"
|
||||
|
||||
cmd += `
|
||||
if [ ! -z "$STATE_FILE" ] && [ -d "$STATE_FILE" ] && [ -f "$STATE_FILE/content" ]; then
|
||||
CMD="$CMD --incoming \"exec: cat $STATE_FILE/content\""
|
||||
elif [ ! -z "$STATE_FILE" ] && [ -f "$STATE_FILE" ]; then
|
||||
CMD="$CMD --incoming \"exec: cat $STATE_FILE\""
|
||||
fi
|
||||
eval $CMD`
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
@@ -333,12 +333,17 @@ func (h *SHostInfo) prepareEnv() error {
|
||||
return fmt.Errorf("Option report_interval must no longer than 5 min")
|
||||
}
|
||||
|
||||
output, err := procutils.NewCommand("mkdir", "-p", options.HostOptions.ServersPath).Output()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create path %s: %s", options.HostOptions.ServersPath, output)
|
||||
for _, dirPath := range []string{
|
||||
options.HostOptions.ServersPath,
|
||||
options.HostOptions.MemorySnapshotsPath,
|
||||
} {
|
||||
output, err := procutils.NewCommand("mkdir", "-p", dirPath).Output()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create path %s: %s", dirPath, output)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = procutils.NewCommand("ethtool", "-h").Output()
|
||||
_, err := procutils.NewCommand("ethtool", "-h").Output()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Execute 'ethtool -h'")
|
||||
}
|
||||
@@ -386,7 +391,7 @@ func (h *SHostInfo) prepareEnv() error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to activate tun/tap device")
|
||||
}
|
||||
output, err = procutils.NewRemoteCommandAsFarAsPossible("modprobe", "vhost_net").Output()
|
||||
output, err := procutils.NewRemoteCommandAsFarAsPossible("modprobe", "vhost_net").Output()
|
||||
if err != nil {
|
||||
log.Warningf("modprobe vhost_net error: %s", output)
|
||||
}
|
||||
|
||||
@@ -501,3 +501,8 @@ func (m *HmpMonitor) NetdevDel(id string, callback StringCallback) {
|
||||
cmd := fmt.Sprintf("netdev_del %s", id)
|
||||
m.Query(cmd, callback)
|
||||
}
|
||||
|
||||
func (m *HmpMonitor) SaveState(stateFilePath string, callback StringCallback) {
|
||||
cmd := fmt.Sprintf(`migrate -d "%s"`, getSaveStatefileUri(stateFilePath))
|
||||
m.Query(cmd, callback)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package monitor
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -191,6 +192,8 @@ type Monitor interface {
|
||||
|
||||
NetdevAdd(id, netType string, params map[string]string, callback StringCallback)
|
||||
NetdevDel(id string, callback StringCallback)
|
||||
|
||||
SaveState(statFilePath string, callback StringCallback)
|
||||
}
|
||||
|
||||
type MonitorErrorFunc func(error)
|
||||
@@ -283,3 +286,10 @@ func (m *SBaseMonitor) checkWriting() bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getSaveStatefileUri(stateFilePath string) string {
|
||||
if strings.HasSuffix(stateFilePath, ".gz") {
|
||||
return fmt.Sprintf("exec:gzip -c > %s", stateFilePath)
|
||||
}
|
||||
return fmt.Sprintf("exec:cat > %s", stateFilePath)
|
||||
}
|
||||
|
||||
@@ -972,3 +972,18 @@ func (m *QmpMonitor) NetdevDel(id string, callback StringCallback) {
|
||||
cmd := fmt.Sprintf("netdev_del %s", id)
|
||||
m.HumanMonitorCommand(cmd, callback)
|
||||
}
|
||||
|
||||
func (m *QmpMonitor) SaveState(stateFilePath string, callback StringCallback) {
|
||||
var (
|
||||
cb = func(res *Response) {
|
||||
callback(m.actionResult(res))
|
||||
}
|
||||
cmd = &Command{
|
||||
Execute: "migrate",
|
||||
Args: map[string]interface{}{
|
||||
"uri": getSaveStatefileUri(stateFilePath),
|
||||
},
|
||||
}
|
||||
)
|
||||
m.Query(cmd, cb)
|
||||
}
|
||||
|
||||
@@ -33,8 +33,9 @@ type SHostOptions struct {
|
||||
Slots string `help:"Slots of host (optional)"`
|
||||
Hostname string `help:"Customized host name"`
|
||||
|
||||
ServersPath string `help:"Path for virtual server configuration files" default:"/opt/cloud/workspace/servers"`
|
||||
ImageCachePath string `help:"Path for storing image caches" default:"/opt/cloud/workspace/disks/image_cache"`
|
||||
ServersPath string `help:"Path for virtual server configuration files" default:"/opt/cloud/workspace/servers"`
|
||||
ImageCachePath string `help:"Path for storing image caches" default:"/opt/cloud/workspace/disks/image_cache"`
|
||||
MemorySnapshotsPath string `help:"Path for memory snapshot stat files" default:"/opt/cloud/workspace/memory_snapshots"`
|
||||
// ImageCacheLimit int `help:"Maximal storage space for image caching, in GB" default:"20"`
|
||||
AgentTempPath string `help:"Path for ESXi agent"`
|
||||
AgentTempLimit int `help:"Maximal storage space for ESXi agent, in GB" default:"10"`
|
||||
|
||||
@@ -59,6 +59,7 @@ const (
|
||||
ACT_VM_SRC_CHECK = "vm_src_check"
|
||||
ACT_VM_START = "vm_start"
|
||||
ACT_VM_STOP = "vm_stop"
|
||||
ACT_VM_SUSPEND = "vm_suspend"
|
||||
ACT_VM_RESTART = "vm_restart"
|
||||
ACT_VM_RESUME = "vm_resume"
|
||||
ACT_VM_SYNC_CONF = "vm_sync_conf"
|
||||
|
||||
Reference in New Issue
Block a user