diff --git a/cmd/climc/climc.go b/cmd/climc/climc.go index e5983068a6..d7a8da1372 100644 --- a/cmd/climc/climc.go +++ b/cmd/climc/climc.go @@ -181,7 +181,7 @@ func main() { fmt.Print(parser.HelpString()) } else if options.Version { fmt.Printf("Yunion API client version:\n %s\n", version.GetJsonString()) - } else if len(options.SUBCOMMAND) == 0 { + } else if len(os.Args) <= 1 || (options.ApiVersion == "v2" && len(os.Args) <= 3) { session, e := newClientSession(options) if e != nil { showErrorAndExit(e) diff --git a/cmd/climc/shell/servers.go b/cmd/climc/shell/servers.go index 7845391197..6381d8d0f1 100644 --- a/cmd/climc/shell/servers.go +++ b/cmd/climc/shell/servers.go @@ -339,8 +339,16 @@ func init() { return nil }) - R(&ServerOpsOptions{}, "server-reset", "Reset servers", func(s *mcclient.ClientSession, args *ServerOpsOptions) error { - ret := modules.Servers.BatchPerformAction(s, args.ID, "reset", nil) + type ServerResetOptions struct { + ServerOpsOptions + Hard bool `help:"Hard reset or not; default soft"` + } + R(&ServerResetOptions{}, "server-reset", "Reset servers", func(s *mcclient.ClientSession, args *ServerResetOptions) error { + params := jsonutils.NewDict() + if args.Hard { + params.Add(jsonutils.JSONTrue, "is_hard") + } + ret := modules.Servers.BatchPerformAction(s, args.ID, "reset", params) printBatchResults(ret, modules.Servers.GetColumns(s)) return nil }) diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index b73a757637..a63aa1470c 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -102,6 +102,14 @@ func (self *SBaseGuestDriver) RqeuestSuspendOnHost(ctx context.Context, guest *m return fmt.Errorf("Not Implement") } +func (self *SBaseGuestDriver) StartGuestResetTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, isHard bool, parentTaskId string) error { + return fmt.Errorf("Not Implement") +} + +func (self *SBaseGuestDriver) RequestSoftReset(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + return fmt.Errorf("Not Implement") +} + func (self *SBaseGuestDriver) AllowReconfigGuest() bool { return true } diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index 2a04d43eda..d98ed72b15 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -206,6 +206,11 @@ func (self *SKVMGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *m return nil } +func (self *SKVMGuestDriver) RequestSoftReset(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + _, err := guest.SendMonitorCommand(ctx, task.GetUserCred(), "system_reset") + return err +} + func (self *SKVMGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId()) } diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index c6311bd5f3..7efdd6d475 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -95,6 +95,19 @@ func (self *SVirtualizedGuestDriver) StartGuestStopTask(guest *models.SGuest, ct return nil } +func (self *SVirtualizedGuestDriver) StartGuestResetTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, isHard bool, parentTaskId string) error { + var taskName = "GuestSoftResetTask" + if isHard { + taskName = "GuestHardResetTask" + } + task, err := taskman.TaskManager.NewTask(ctx, taskName, guest, userCred, nil, parentTaskId, "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) + return nil +} + func (self *SVirtualizedGuestDriver) OnGuestDeployTaskComplete(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { if jsonutils.QueryBoolean(task.GetParams(), "restart", false) { task.SetStage("OnDeployStartGuestComplete", nil) diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 9e9ce6c70c..48406df24f 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -39,6 +39,9 @@ type IGuestDriver interface { RequestGuestCreateInsertIso(ctx context.Context, imageId string, guest *SGuest, task taskman.ITask) error StartGuestStopTask(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error + StartGuestResetTask(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, isHard bool, parentTaskId string) error + + RequestSoftReset(ctx context.Context, guest *SGuest, task taskman.ITask) error RequestDeployGuestOnHost(ctx context.Context, guest *SGuest, host *SHost, task taskman.ITask) error diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index c12f0e4221..047a8659ad 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -3103,6 +3103,23 @@ func (self *SGuest) PerformStart(ctx context.Context, userCred mcclient.TokenCre } } +func (self *SGuest) AllowPerformReset(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return self.IsOwner(userCred) +} + +func (self *SGuest) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, + data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + isHard := jsonutils.QueryBoolean(data, "is_hard", false) + if self.Status == VM_RUNNING || self.Status == VM_STOP_FAILED { + self.GetDriver().StartGuestResetTask(self, ctx, userCred, isHard, "") + return nil, nil + } + return nil, httperrors.NewInvalidStatusError("Cannot reset VM in status %s", self.Status) +} + func (self *SGuest) AllowPerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, diff --git a/pkg/compute/tasks/guest_reset_task.go b/pkg/compute/tasks/guest_reset_task.go new file mode 100644 index 0000000000..72a5273e2d --- /dev/null +++ b/pkg/compute/tasks/guest_reset_task.go @@ -0,0 +1,58 @@ +package tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +func init() { + taskman.RegisterTask(GuestSoftResetTask{}) + taskman.RegisterTask(GuestHardResetTask{}) +} + +type GuestSoftResetTask struct { + SGuestBaseTask +} + +func (self *GuestSoftResetTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + err := guest.GetDriver().RequestSoftReset(ctx, guest, self) + if err == nil { + self.SetStageComplete(ctx, nil) + } else { + self.SetStageFailed(ctx, err.Error()) + } +} + +type GuestHardResetTask struct { + SGuestBaseTask +} + +func (self *GuestHardResetTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + self.StopServer(ctx, guest) +} + +func (self *GuestHardResetTask) StopServer(ctx context.Context, guest *models.SGuest) { + guest.SetStatus(self.UserCred, models.VM_STOPPING, "") + self.SetStage("OnServerStopComplete", nil) + guest.StartGuestStopTask(ctx, self.UserCred, false, self.GetTaskId()) +} + +func (self *GuestHardResetTask) OnServerStopComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + self.StartServer(ctx, guest) +} + +func (self *GuestHardResetTask) StartServer(ctx context.Context, guest *models.SGuest) { + self.SetStage("OnServerStartComplete", nil) + guest.StartGueststartTask(ctx, self.UserCred, nil, self.GetTaskId()) +} + +func (self *GuestHardResetTask) OnServerStartComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +}