guest_start_task.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package guest
  15. import (
  16. "context"
  17. "yunion.io/x/jsonutils"
  18. "yunion.io/x/log"
  19. "yunion.io/x/pkg/errors"
  20. api "yunion.io/x/onecloud/pkg/apis/compute"
  21. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  22. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  23. "yunion.io/x/onecloud/pkg/compute/models"
  24. "yunion.io/x/onecloud/pkg/compute/options"
  25. "yunion.io/x/onecloud/pkg/mcclient/auth"
  26. "yunion.io/x/onecloud/pkg/mcclient/modules/vpcagent"
  27. "yunion.io/x/onecloud/pkg/util/logclient"
  28. )
  29. type GuestStartTask struct {
  30. SGuestBaseTask
  31. }
  32. func init() {
  33. taskman.RegisterTask(GuestStartTask{})
  34. taskman.RegisterTask(GuestSchedStartTask{})
  35. }
  36. func (self *GuestStartTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  37. guest := obj.(*models.SGuest)
  38. self.AttachReleasedDevices(ctx, guest)
  39. }
  40. func (self *GuestStartTask) attachReleasedDevices(ctx context.Context, guest *models.SGuest) error {
  41. devs, err := guest.GetReleasedIsolatedDevices(ctx, self.GetUserCred())
  42. if err != nil {
  43. return errors.Wrap(err, "GetReleasedIsolatedDevices")
  44. }
  45. if len(devs) == 0 {
  46. return self.ScheduleRun(nil)
  47. }
  48. attachReq := make(map[string]int)
  49. for _, dev := range devs {
  50. count, ok := attachReq[dev.Model]
  51. if !ok {
  52. attachReq[dev.Model] = 1
  53. } else {
  54. attachReq[dev.Model] = count + 1
  55. }
  56. }
  57. if err := guest.AttachIsolatedDevices(ctx, self.GetUserCred(), attachReq); err != nil {
  58. return errors.Wrap(err, "attach isolated devices")
  59. }
  60. return guest.StartIsolatedDevicesSyncTask(ctx, self.GetUserCred(), false, self.GetTaskId())
  61. }
  62. func (self *GuestStartTask) AttachReleasedDevices(ctx context.Context, guest *models.SGuest) {
  63. self.SetStage("OnReleasedDevicesAttached", nil)
  64. if guest.ShutdownBehavior != api.SHUTDOWN_STOP_RELEASE_GPU {
  65. self.ScheduleRun(nil)
  66. return
  67. }
  68. if err := self.attachReleasedDevices(ctx, guest); err != nil {
  69. self.OnStartCompleteFailed(ctx, guest, jsonutils.NewString(errors.Wrap(err, "attach released devices").Error()))
  70. return
  71. }
  72. }
  73. func (self *GuestStartTask) OnReleasedDevicesAttached(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  74. db.OpsLog.LogEvent(guest, db.ACT_STARTING, nil, self.UserCred)
  75. self.RequestStart(ctx, guest)
  76. }
  77. func (self *GuestStartTask) OnReleasedDevicesFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  78. self.OnStartCompleteFailed(ctx, guest, data)
  79. }
  80. func (self *GuestStartTask) RequestStart(ctx context.Context, guest *models.SGuest) {
  81. self.SetStage("OnStartComplete", nil)
  82. host, _ := guest.GetHost()
  83. guest.SetStatus(ctx, self.UserCred, api.VM_STARTING, "")
  84. drv, err := guest.GetDriver()
  85. if err != nil {
  86. self.OnStartCompleteFailed(ctx, guest, jsonutils.NewString(err.Error()))
  87. return
  88. }
  89. err = drv.RequestStartOnHost(ctx, guest, host, self.UserCred, self)
  90. if err != nil {
  91. self.OnStartCompleteFailed(ctx, guest, jsonutils.NewString(err.Error()))
  92. return
  93. }
  94. }
  95. func (task *GuestStartTask) OnStartComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  96. guest := obj.(*models.SGuest)
  97. // save start mem and cpu
  98. guest.SetMetadata(ctx, api.VM_METADATA_START_VCPU_COUNT, guest.VcpuCount, task.UserCred)
  99. guest.SetMetadata(ctx, api.VM_METADATA_START_VMEM_MB, guest.VmemSize, task.UserCred)
  100. // save start time
  101. guest.SaveLastStartAt()
  102. if data.Contains("cpu_numa_pin") {
  103. cpuNumaPin := make([]api.SCpuNumaPin, 0)
  104. err := data.Unmarshal(&cpuNumaPin, "cpu_numa_pin")
  105. if err != nil {
  106. log.Errorf("failed unmarshal cpu numa pin %s", err)
  107. } else {
  108. if err = guest.SetCpuNumaPin(ctx, task.UserCred, nil, cpuNumaPin); err != nil {
  109. log.Errorf("failed set cpu numa pin %s", err)
  110. }
  111. }
  112. }
  113. // sync Vpc Topology
  114. isVpc, err := guest.IsOneCloudVpcNetwork()
  115. if err != nil {
  116. log.Errorf("IsOneCloudVpcNetwork fail: %s", err)
  117. } else if isVpc {
  118. // force update VPC topo
  119. err := vpcagent.VpcAgent.DoSync(auth.GetAdminSession(ctx, options.Options.Region))
  120. if err != nil {
  121. log.Errorf("vpcagent.VpcAgent.DoSync fail %s", err)
  122. }
  123. }
  124. // log
  125. db.OpsLog.LogEvent(guest, db.ACT_START, guest.GetShortDesc(ctx), task.UserCred)
  126. logclient.AddActionLogWithStartable(task, guest, logclient.ACT_VM_START, guest.GetShortDesc(ctx), task.UserCred, true)
  127. task.taskComplete(ctx, guest)
  128. }
  129. func (self *GuestStartTask) OnStartCompleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) {
  130. guest := obj.(*models.SGuest)
  131. guest.SetStatus(ctx, self.UserCred, api.VM_START_FAILED, err.String())
  132. db.OpsLog.LogEvent(guest, db.ACT_START_FAIL, err, self.UserCred)
  133. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_START, err, self.UserCred, false)
  134. self.SetStageFailed(ctx, err)
  135. }
  136. func (self *GuestStartTask) taskComplete(ctx context.Context, guest *models.SGuest) {
  137. models.HostManager.ClearSchedDescCache(guest.HostId)
  138. self.SetStageComplete(ctx, nil)
  139. }
  140. type GuestSchedStartTask struct {
  141. SGuestBaseTask
  142. }
  143. func (self *GuestSchedStartTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  144. guest := obj.(*models.SGuest)
  145. self.StartScheduler(ctx, guest)
  146. }
  147. func (self *GuestSchedStartTask) StartScheduler(ctx context.Context, guest *models.SGuest) {
  148. host, _ := guest.GetHost()
  149. if host.EnableNumaAllocate {
  150. self.ScheduleFailed(ctx, guest, true)
  151. return
  152. }
  153. if request := host.GetRunningGuestResourceUsage(); request == nil {
  154. self.TaskFailed(ctx, guest, jsonutils.NewString("Guest Start Failed: Can't Get Host Guests CPU Memory Usage"))
  155. } else {
  156. if float32(request.GuestVmemSize+guest.VmemSize) > host.GetVirtualMemorySize() {
  157. log.Infof("host memory not enough to start guest")
  158. self.ScheduleFailed(ctx, guest, false)
  159. } else if request.GuestVcpuCount+guest.VcpuCount > int(host.GetVirtualCPUCount()) {
  160. log.Infof("host cpu not enough to start guest")
  161. self.ScheduleFailed(ctx, guest, false)
  162. } else {
  163. self.ScheduleSucc(ctx, guest)
  164. }
  165. }
  166. }
  167. func (self *GuestSchedStartTask) ScheduleFailed(ctx context.Context, guest *models.SGuest, resetCpuNumaPin bool) {
  168. self.SetStage("OnGuestMigrate", nil)
  169. preferHostId := ""
  170. if resetCpuNumaPin {
  171. preferHostId = guest.HostId
  172. }
  173. guest.StartMigrateTask(ctx, self.UserCred, false, false, guest.Status, preferHostId, self.GetId())
  174. }
  175. func (self *GuestSchedStartTask) OnGuestMigrate(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  176. self.SetStage("OnGuestStarted", nil)
  177. guest.GuestNonSchedStartTask(ctx, self.UserCred, self.Params, self.GetTaskId())
  178. }
  179. func (self *GuestSchedStartTask) OnGuestMigrateFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  180. self.TaskFailed(ctx, guest, data)
  181. }
  182. func (self *GuestSchedStartTask) OnGuestStarted(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  183. self.SetStageComplete(ctx, nil)
  184. }
  185. func (self *GuestSchedStartTask) OnGuestStartedFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  186. self.TaskFailed(ctx, guest, data)
  187. }
  188. func (self *GuestSchedStartTask) ScheduleSucc(ctx context.Context, guest *models.SGuest) {
  189. self.SetStage("OnGuestStarted", nil)
  190. guest.GuestNonSchedStartTask(ctx, self.UserCred, self.Params, self.GetTaskId())
  191. }
  192. func (self *GuestSchedStartTask) TaskFailed(ctx context.Context, guest *models.SGuest, reason jsonutils.JSONObject) {
  193. self.SetStageFailed(ctx, reason)
  194. guest.SetStatus(ctx, self.UserCred, api.VM_START_FAILED, reason.String())
  195. db.OpsLog.LogEvent(guest, db.ACT_START_FAIL, reason, self.UserCred)
  196. logclient.AddActionLogWithStartable(
  197. self, guest, logclient.ACT_VM_START, reason, self.UserCred, false)
  198. }