Skip to content

Commit

Permalink
Merge pull request #17671 from IRONICBo/feature/implement-vm-rescue
Browse files Browse the repository at this point in the history
[WIP]feat(region): add guest rescue api
  • Loading branch information
zexi authored Oct 29, 2023
2 parents ec7099c + 74b6a20 commit 3cb6a4d
Show file tree
Hide file tree
Showing 19 changed files with 854 additions and 1 deletion.
42 changes: 42 additions & 0 deletions cmd/climc/shell/compute/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -951,4 +951,46 @@ func init() {
}
return nil
})

// ServerStartRescueOptions is used to start a rescue os.
type ServerStartRescueOptions struct {
ID string `help:"ID of server" json:"-"`
QemuVersion string `help:"prefer qemu version" json:"qemu_version"`
}
R(&ServerStartRescueOptions{}, "server-start-rescue ", "Start rescu e a guest server", func(s *mcclient.ClientSession, opts *ServerStartRescueOptions) error {
params, err := baseoptions.StructToParams(opts)
if err != nil {
return err
}

result, err := modules.Servers.PerformAction(s, opts.ID, "start-rescue", params)
if err != nil {
return err
}

printObject(result)

return nil
})

// ServerStopRescueOptions is used to stop a rescue os.
type ServerStopRescueOptions struct {
ID string `help:"ID of server" json:"-"`
QemuVersion string `help:"prefer qemu version" json:"qemu_version"`
}
R(&ServerStopRescueOptions{}, "server-stop-rescue", "Stop rescue a guest server", func(s *mcclient.ClientSession, opts *ServerStopRescueOptions) error {
params, err := baseoptions.StructToParams(opts)
if err != nil {
return err
}

result, err := modules.Servers.PerformAction(s, opts.ID, "stop-rescue", params)
if err != nil {
return err
}

printObject(result)

return nil
})
}
6 changes: 6 additions & 0 deletions pkg/apis/compute/guest_const.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ const (
VM_DETACH_DISK = "detach_disk"
VM_UNSYNC = "unsync"

VM_START_RESCUE = "start_rescue"
VM_RESCUING = "rescuing"
VM_STOP_RESCUE = "stop_rescue"
VM_START_RESCUE_FAILED = "start_rescue_failed"
VM_STOP_RESCUE_FAILED = "stop_rescue_failed"

VM_BACKUP_STARTING = "backup_starting"
VM_BACKUP_STOPING = "backup_stopping"
VM_BACKUP_CREATING = "backup_creating"
Expand Down
28 changes: 28 additions & 0 deletions pkg/apis/compute/guest_rescue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 compute

// Rescue constants are used for rescue mode
const (
GUEST_RESCUE_RELATIVE_PATH = "rescue" // serverxxx/rescue

GUEST_RESCUE_INITRAMFS = "initramfs"
GUEST_RESCUE_KERNEL = "kernel"
GUEST_RESCUE_INITRAMFS_ARM64 = "initramfs_aarch64"
GUEST_RESCUE_KERNEL_ARM64 = "kernel_aarch64"

GUEST_RESCUE_SYS_DISK_NAME = "sys_img"
GUEST_RESCUE_SYS_DISK_SIZE = 500 // MB
)
2 changes: 2 additions & 0 deletions pkg/apis/compute/guests.go
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,8 @@ type GuestJsonDesc struct {
EncryptKeyId string `json:"encrypt_key_id,omitempty"`

IsDaemon bool `json:"is_daemon"`

RescueMode bool `json:"rescue_mode"`
}

type ServerSetBootIndexInput struct {
Expand Down
5 changes: 5 additions & 0 deletions pkg/cloudcommon/db/opslog_const.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,4 +322,9 @@ const (

ACT_BIND = "bind"
ACT_UNBIND = "unbind"

ACT_START_RESCUE = "start_rescue"
ACT_STOP_RESCUE = "stop_rescue"
ACT_START_RESCUE_FAILED = "start_rescue_failed"
ACT_STOP_RESCUE_FAILED = "stop_rescue_failed"
)
8 changes: 8 additions & 0 deletions pkg/compute/guestdrivers/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,11 @@ func (drv *SBaseGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task
func (drv *SBaseGuestDriver) SyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, g *models.SGuest, extVM cloudprovider.IOSInfo) error {
return nil
}

func (self *SBaseGuestDriver) RequestStartRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error {
return httperrors.ErrNotImplemented
}

func (self *SBaseGuestDriver) RequestStopRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error {
return httperrors.ErrNotImplemented
}
24 changes: 24 additions & 0 deletions pkg/compute/guestdrivers/kvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1173,3 +1173,27 @@ func (self *SKVMGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task
}
return nil
}

func (self *SKVMGuestDriver) RequestStartRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error {
header := self.getTaskRequestHeader(task)
client := httputils.GetDefaultClient()
url := fmt.Sprintf("%s/servers/%s/start-rescue", host.ManagerUri, guest.Id)
_, _, err := httputils.JSONRequest(client, ctx, "POST", url, header, body, false)
if err != nil {
return err
}

return nil
}

func (self *SKVMGuestDriver) RequestStopRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error {
header := self.getTaskRequestHeader(task)
client := httputils.GetDefaultClient()
url := fmt.Sprintf("%s/servers/%s/stop-rescue", host.ManagerUri, guest.Id)
_, _, err := httputils.JSONRequest(client, ctx, "POST", url, header, body, false)
if err != nil {
return err
}

return nil
}
148 changes: 148 additions & 0 deletions pkg/compute/models/guest_rescue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// 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 models

import (
"context"

"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"

api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)

func (self *SGuest) PerformStartRescue(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING}) {
return nil, httperrors.NewInvalidStatusError("guest status must be ready or running")
}

// Check vmem size, need to be greater than 2G
if self.VmemSize < 2048 {
return nil, httperrors.NewInvalidStatusError("vmem size must be greater than 2G")
}

// Reset index
disks, err := self.GetGuestDisks()
if err != nil || len(disks) < 1 {
return nil, httperrors.NewInvalidStatusError("guest.GetGuestDisks: %s", err.Error())
}
for i := 0; i < len(disks); i++ {
if disks[i].BootIndex >= 0 {
// Move to next index, and easy to rollback
err = disks[i].SetBootIndex(disks[i].BootIndex + 1)
if err != nil {
return nil, httperrors.NewInvalidStatusError("guest.SetBootIndex: %s", err.Error())
}
}
}

// Get baremetal agent
host, err := self.GetHost()
if err != nil {
return nil, httperrors.NewInvalidStatusError("guest.GetHost: %s", err.Error())
}
bmAgent := BaremetalagentManager.GetAgent(api.AgentTypeBaremetal, host.ZoneId)
if bmAgent == nil {
return nil, httperrors.NewInvalidStatusError("BaremetalagentManager.GetAgent: %s", "Baremetal agent not found")
}

// Set available baremetal agent managerURi to data
dataDict := data.(*jsonutils.JSONDict)
dataDict.Add(jsonutils.NewString(bmAgent.ManagerUri), "manager_uri")

// Start rescue vm task
err = self.StartRescueTask(ctx, userCred, dataDict, "")
if err != nil {
return nil, httperrors.NewInvalidStatusError("guest.StartGuestRescueTask: %s", err.Error())
}

// Now it only support kvm guest os rescue
return nil, nil
}

func (self *SGuest) PerformStopRescue(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !self.RescueMode {
return nil, httperrors.NewInvalidStatusError("guest is not in rescue mode")
}

// Recover index
disks, err := self.GetGuestDisks()
if err != nil || len(disks) < 1 {
return nil, httperrors.NewInvalidStatusError("guest.GetGuestDisks: %s", err.Error())
}
for i := 0; i < len(disks); i++ {
if disks[i].BootIndex >= 0 {
// Rollback index
err = disks[i].SetBootIndex(disks[i].BootIndex - 1)
if err != nil {
return nil, httperrors.NewInvalidStatusError("guest.SetBootIndex: %s", err.Error())
}
}
}

// Start rescue vm task
err = self.StopRescueTask(ctx, userCred, data.(*jsonutils.JSONDict), "")
if err != nil {
return nil, httperrors.NewInvalidStatusError("guest.StopGuestRescueTask: %s", err.Error())
}

// Now it only support kvm guest os rescue
return nil, nil
}

func (self *SGuest) UpdateRescueMode(mode bool) error {
_, err := db.Update(self, func() error {
self.RescueMode = mode
return nil
})
if err != nil {
return errors.Wrap(err, "Update RescueMode")
}
return nil
}

func (self *SGuest) StartRescueTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error {
// Now only support KVM
taskName := "StartRescueTask"
task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
if err != nil {
return err
}
err = task.ScheduleRun(nil)
if err != nil {
return err
}
return nil
}

func (self *SGuest) StopRescueTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error {
taskName := "StopRescueTask"
task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
if err != nil {
return err
}
err = task.ScheduleRun(nil)
if err != nil {
return err
}
return nil
}
3 changes: 3 additions & 0 deletions pkg/compute/models/guestdrivers.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ type IGuestDriver interface {
RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *SHost, guest *SGuest, input *api.ServerNicTrafficLimit) error

SyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, g *SGuest, extVM cloudprovider.IOSInfo) error

RequestStartRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *SHost, guest *SGuest) error
RequestStopRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *SHost, guest *SGuest) error
}

var guestDrivers map[string]IGuestDriver
Expand Down
4 changes: 4 additions & 0 deletions pkg/compute/models/guests.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ type SGuest struct {
QgaStatus string `width:"36" charset:"ascii" nullable:"false" default:"unknown" list:"user" create:"optional"`
// power_states limit in [on, off, unknown]
PowerStates string `width:"36" charset:"ascii" nullable:"false" default:"unknown" list:"user" create:"optional"`
// Used for guest rescue
RescueMode bool `nullable:"false" default:"false" list:"user" create:"optional"`
}

func (manager *SGuestManager) GetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*apis.StatusStatistic, error) {
Expand Down Expand Up @@ -4917,6 +4919,8 @@ func (self *SGuest) GetJsonDescAtHypervisor(ctx context.Context, host *SHost) *a
EncryptKeyId: self.EncryptKeyId,

IsDaemon: self.IsDaemon.Bool(),

RescueMode: self.RescueMode,
}

if len(self.BackupHostId) > 0 {
Expand Down
Loading

0 comments on commit 3cb6a4d

Please sign in to comment.