mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #11166 from zexi/feat/migrate-forecast
feat(region): add server migration predication API
This commit is contained in:
@@ -59,6 +59,7 @@ func init() {
|
||||
cmd.BatchPerform("reset", new(options.ServerResetOptions))
|
||||
cmd.BatchPerform("restart", new(options.ServerRestartOptions))
|
||||
cmd.BatchPerform("purge", new(options.ServerIdsOptions))
|
||||
cmd.PrintObjectYAML().Perform("migrate-forecast", new(options.ServerMigrateForecastOptions))
|
||||
cmd.Perform("migrate", new(options.ServerMigrateOptions))
|
||||
cmd.Perform("live-migrate", new(options.ServerLiveMigrateOptions))
|
||||
cmd.Perform("modify-src-check", new(options.ServerModifySrcCheckOptions))
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/util/printutils"
|
||||
)
|
||||
|
||||
type ResourceCmd struct {
|
||||
@@ -32,12 +33,15 @@ type ResourceCmd struct {
|
||||
|
||||
keyword string
|
||||
prefix string
|
||||
|
||||
printObject func(jsonutils.JSONObject)
|
||||
}
|
||||
|
||||
func NewResourceCmd(manager modulebase.IBaseManager) *ResourceCmd {
|
||||
return &ResourceCmd{
|
||||
manager: manager,
|
||||
keyword: manager.GetKeyword(),
|
||||
manager: manager,
|
||||
keyword: manager.GetKeyword(),
|
||||
printObject: printObjectRecursive,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +66,30 @@ func (cmd *ResourceCmd) SetKeyword(keyword string) *ResourceCmd {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cmd *ResourceCmd) PrintObjectYAML() *ResourceCmd {
|
||||
cmd.printObject = func(obj jsonutils.JSONObject) {
|
||||
fmt.Print(obj.YAMLString())
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cmd *ResourceCmd) PrintObjectTable() *ResourceCmd {
|
||||
cmd.printObject = printutils.PrintJSONObject
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cmd *ResourceCmd) PrintObjectKV() *ResourceCmd {
|
||||
cmd.printObject = printObjectFmtKv
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cmd *ResourceCmd) PrintObjectFlattenKV() *ResourceCmd {
|
||||
cmd.printObject = func(obj jsonutils.JSONObject) {
|
||||
printObjectRecursiveEx(obj, printObjectFmtKv)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
type IOpt interface {
|
||||
Params() (jsonutils.JSONObject, error)
|
||||
}
|
||||
@@ -325,7 +353,7 @@ func (cmd ResourceCmd) PerformWithKeyword(keyword, action string, args IPerformO
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(ret)
|
||||
cmd.printObject(ret)
|
||||
return nil
|
||||
}
|
||||
cmd.Run(keyword, args, callback)
|
||||
@@ -342,7 +370,7 @@ func (cmd ResourceCmd) PerformClassWithKeyword(keyword, action string, args IOpt
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(ret)
|
||||
cmd.printObject(ret)
|
||||
return nil
|
||||
}
|
||||
cmd.Run(keyword, args, callback)
|
||||
@@ -363,7 +391,7 @@ func (cmd ResourceCmd) PerformClass(action string, args IOpt) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObjectRecursive(ret)
|
||||
cmd.printObject(ret)
|
||||
return nil
|
||||
}
|
||||
cmd.Run(action, args, callback)
|
||||
|
||||
@@ -532,3 +532,11 @@ type ServerDetachnetworkInput struct {
|
||||
// 通过Mac解绑网卡, 优先级低于ip_addr
|
||||
Mac string `json:"mac"`
|
||||
}
|
||||
|
||||
type ServerMigrateForecastInput struct {
|
||||
PreferHostId string `json:"prefer_host_id"`
|
||||
// Deprecated
|
||||
PreferHost string `json:"prefer_host" yunion-deprecated-by:"prefer_host_id"`
|
||||
LiveMigrate bool `json:"live_migrate"`
|
||||
SkipCpuCheck bool `josn:"skip_cpu_check"`
|
||||
}
|
||||
|
||||
@@ -358,31 +358,129 @@ func (self *SGuest) CheckQemuVersion(qemuVer, compareVer string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SGuest) validateMigrate(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
migrateInput *api.GuestMigrateInput,
|
||||
liveMigrateInput *api.GuestLiveMigrateInput,
|
||||
) error {
|
||||
isLiveMigrate := false
|
||||
if liveMigrateInput != nil {
|
||||
isLiveMigrate = true
|
||||
}
|
||||
|
||||
if isLiveMigrate {
|
||||
// do live migrate check
|
||||
if !self.GetDriver().IsSupportLiveMigrate() {
|
||||
return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
|
||||
}
|
||||
if err := self.GetDriver().CheckLiveMigrate(self, userCred, *liveMigrateInput); err != nil {
|
||||
return err
|
||||
}
|
||||
if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_SUSPEND}) {
|
||||
if len(liveMigrateInput.PreferHost) > 0 {
|
||||
iHost, _ := HostManager.FetchByIdOrName(userCred, liveMigrateInput.PreferHost)
|
||||
if iHost == nil {
|
||||
return httperrors.NewBadRequestError("Host %s not found", liveMigrateInput.PreferHost)
|
||||
}
|
||||
host := iHost.(*SHost)
|
||||
liveMigrateInput.PreferHost = host.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return httperrors.NewBadRequestError("Cannot live migrate in status %s", self.Status)
|
||||
} else {
|
||||
// do migrate check
|
||||
if !self.GetDriver().IsSupportMigrate() {
|
||||
return httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
|
||||
}
|
||||
if err := self.GetDriver().CheckMigrate(self, userCred, *migrateInput); err != nil {
|
||||
return err
|
||||
}
|
||||
if self.Status != api.VM_READY {
|
||||
return httperrors.NewServerStatusError("Cannot normal migrate guest in status %s, try rescue mode or server-live-migrate?", self.Status)
|
||||
}
|
||||
if len(migrateInput.PreferHost) > 0 {
|
||||
iHost, _ := HostManager.FetchByIdOrName(userCred, migrateInput.PreferHost)
|
||||
if iHost == nil {
|
||||
return httperrors.NewBadRequestError("Host %s not found", migrateInput.PreferHost)
|
||||
}
|
||||
host := iHost.(*SHost)
|
||||
migrateInput.PreferHost = host.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformMigrateForecast(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerMigrateForecastInput) (jsonutils.JSONObject, error) {
|
||||
var (
|
||||
mInput *api.GuestMigrateInput = nil
|
||||
lmInput *api.GuestLiveMigrateInput = nil
|
||||
)
|
||||
|
||||
if input.LiveMigrate {
|
||||
lmInput = &api.GuestLiveMigrateInput{
|
||||
PreferHost: input.PreferHostId,
|
||||
SkipCpuCheck: &input.SkipCpuCheck,
|
||||
}
|
||||
if err := self.validateMigrate(ctx, userCred, nil, lmInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input.PreferHostId = lmInput.PreferHost
|
||||
} else {
|
||||
mInput = &api.GuestMigrateInput{
|
||||
PreferHost: input.PreferHostId,
|
||||
}
|
||||
if err := self.validateMigrate(ctx, userCred, mInput, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input.PreferHostId = mInput.PreferHost
|
||||
}
|
||||
|
||||
schedParams := self.GetSchedMigrateParams(userCred, input)
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region, "")
|
||||
_, res, err := modules.SchedManager.DoScheduleForecast(s, schedParams, 1)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Do schedule migrate forecast")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (self *SGuest) GetSchedMigrateParams(
|
||||
userCred mcclient.TokenCredential,
|
||||
input *api.ServerMigrateForecastInput,
|
||||
) *schedapi.ScheduleInput {
|
||||
schedDesc := self.ToSchedDesc()
|
||||
if input.PreferHostId != "" {
|
||||
schedDesc.ServerConfig.PreferHost = input.PreferHostId
|
||||
}
|
||||
if input.LiveMigrate {
|
||||
schedDesc.LiveMigrate = input.LiveMigrate
|
||||
if self.GetMetadata("__cpu_mode", userCred) != api.CPU_MODE_QEMU {
|
||||
host := self.GetHost()
|
||||
schedDesc.CpuDesc = host.CpuDesc
|
||||
schedDesc.CpuMicrocode = host.CpuMicrocode
|
||||
schedDesc.CpuMode = api.CPU_MODE_HOST
|
||||
} else {
|
||||
schedDesc.CpuMode = api.CPU_MODE_QEMU
|
||||
}
|
||||
schedDesc.SkipCpuCheck = &input.SkipCpuCheck
|
||||
}
|
||||
schedDesc.ReuseNetwork = true
|
||||
return schedDesc
|
||||
}
|
||||
|
||||
func (self *SGuest) AllowPerformMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestMigrateInput) bool {
|
||||
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "migrate")
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestMigrateInput) (jsonutils.JSONObject, error) {
|
||||
if !self.GetDriver().IsSupportMigrate() {
|
||||
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
|
||||
}
|
||||
if err := self.GetDriver().CheckMigrate(self, userCred, input); err != nil {
|
||||
func (self *SGuest) PerformMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.GuestMigrateInput) (jsonutils.JSONObject, error) {
|
||||
if err := self.validateMigrate(ctx, userCred, input, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if self.Status != api.VM_READY {
|
||||
return nil, httperrors.NewServerStatusError("Cannot normal migrate guest in status %s, try rescue mode or server-live-migrate?", self.Status)
|
||||
}
|
||||
var preferHostId string
|
||||
if len(input.PreferHost) > 0 {
|
||||
iHost, _ := HostManager.FetchByIdOrName(userCred, input.PreferHost)
|
||||
if iHost == nil {
|
||||
return nil, httperrors.NewBadRequestError("Host %s not found", input.PreferHost)
|
||||
}
|
||||
host := iHost.(*SHost)
|
||||
preferHostId = host.Id
|
||||
}
|
||||
|
||||
return nil, self.StartMigrateTask(ctx, userCred, input.IsRescueMode, input.AutoStart, self.Status, preferHostId, "")
|
||||
return nil, self.StartMigrateTask(ctx, userCred, input.IsRescueMode, input.AutoStart, self.Status, input.PreferHost, "")
|
||||
}
|
||||
|
||||
func (self *SGuest) StartMigrateTask(
|
||||
@@ -418,27 +516,11 @@ func (self *SGuest) AllowPerformLiveMigrate(ctx context.Context, userCred mcclie
|
||||
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "live-migrate")
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformLiveMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestLiveMigrateInput) (jsonutils.JSONObject, error) {
|
||||
if !self.GetDriver().IsSupportLiveMigrate() {
|
||||
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.GetHypervisor())
|
||||
}
|
||||
if err := self.GetDriver().CheckLiveMigrate(self, userCred, input); err != nil {
|
||||
func (self *SGuest) PerformLiveMigrate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.GuestLiveMigrateInput) (jsonutils.JSONObject, error) {
|
||||
if err := self.validateMigrate(ctx, userCred, nil, input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_SUSPEND}) {
|
||||
var preferHostId string
|
||||
if len(input.PreferHost) > 0 {
|
||||
iHost, _ := HostManager.FetchByIdOrName(userCred, input.PreferHost)
|
||||
if iHost == nil {
|
||||
return nil, httperrors.NewBadRequestError("Host %s not found", input.PreferHost)
|
||||
}
|
||||
host := iHost.(*SHost)
|
||||
preferHostId = host.Id
|
||||
}
|
||||
err := self.StartGuestLiveMigrateTask(ctx, userCred, self.Status, preferHostId, input.SkipCpuCheck, "")
|
||||
return nil, err
|
||||
}
|
||||
return nil, httperrors.NewBadRequestError("Cannot live migrate in status %s", self.Status)
|
||||
return nil, self.StartGuestLiveMigrateTask(ctx, userCred, self.Status, input.PreferHost, input.SkipCpuCheck, "")
|
||||
}
|
||||
|
||||
func (self *SGuest) StartGuestLiveMigrateTask(ctx context.Context, userCred mcclient.TokenCredential, guestStatus, preferHostId string, skipCpuCheck *bool, parentTaskId string) error {
|
||||
|
||||
@@ -60,27 +60,18 @@ func (self *GuestMigrateTask) OnInit(ctx context.Context, obj db.IStandaloneMode
|
||||
func (self *GuestMigrateTask) GetSchedParams() (*schedapi.ScheduleInput, error) {
|
||||
obj := self.GetObject()
|
||||
guest := obj.(*models.SGuest)
|
||||
schedDesc := guest.ToSchedDesc()
|
||||
input := new(api.ServerMigrateForecastInput)
|
||||
if self.Params.Contains("prefer_host_id") {
|
||||
preferHostId, _ := self.Params.GetString("prefer_host_id")
|
||||
schedDesc.ServerConfig.PreferHost = preferHostId
|
||||
input.PreferHostId = preferHostId
|
||||
}
|
||||
guestStatus, _ := self.Params.GetString("guest_status")
|
||||
if !jsonutils.QueryBoolean(self.Params, "is_rescue_mode", false) && (guestStatus == api.VM_RUNNING || guestStatus == api.VM_SUSPEND) {
|
||||
schedDesc.LiveMigrate = true
|
||||
if guest.GetMetadata("__cpu_mode", self.UserCred) != api.CPU_MODE_QEMU {
|
||||
host := guest.GetHost()
|
||||
schedDesc.CpuDesc = host.CpuDesc
|
||||
schedDesc.CpuMicrocode = host.CpuMicrocode
|
||||
schedDesc.CpuMode = api.CPU_MODE_HOST
|
||||
} else {
|
||||
schedDesc.CpuMode = api.CPU_MODE_QEMU
|
||||
}
|
||||
input.LiveMigrate = true
|
||||
skipCpuCheck := jsonutils.QueryBoolean(self.Params, "skip_cpu_check", false)
|
||||
schedDesc.SkipCpuCheck = &skipCpuCheck
|
||||
input.SkipCpuCheck = skipCpuCheck
|
||||
}
|
||||
schedDesc.ReuseNetwork = true
|
||||
return schedDesc, nil
|
||||
return guest.GetSchedMigrateParams(self.GetUserCred(), input), nil
|
||||
}
|
||||
|
||||
func (self *GuestMigrateTask) OnStartSchedule(obj IScheduleModel) {
|
||||
|
||||
@@ -874,6 +874,21 @@ func (o *ServerRestartOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return StructToParams(o)
|
||||
}
|
||||
|
||||
type ServerMigrateForecastOptions struct {
|
||||
ID string `help:"ID of server" json:"-"`
|
||||
PreferHost string `help:"Server migration prefer host id or name" json:"prefer_host"`
|
||||
LiveMigrate *bool `help:"Use live migrate"`
|
||||
SkipCpuCheck *bool `help:"Skip check CPU mode of the target host" json:"skip_cpu_check"`
|
||||
}
|
||||
|
||||
func (o *ServerMigrateForecastOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *ServerMigrateForecastOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return StructToParams(o)
|
||||
}
|
||||
|
||||
type ServerMigrateOptions struct {
|
||||
ID string `help:"ID of server" json:"-"`
|
||||
PreferHost string `help:"Server migration prefer host id or name" json:"prefer_host"`
|
||||
|
||||
Reference in New Issue
Block a user