guest_batch_create_task.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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. "fmt"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. api "yunion.io/x/onecloud/pkg/apis/compute"
  21. schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
  22. "yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
  23. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  28. "yunion.io/x/onecloud/pkg/compute/models"
  29. taskutils "yunion.io/x/onecloud/pkg/compute/tasks/utils"
  30. "yunion.io/x/onecloud/pkg/util/conditionparser"
  31. "yunion.io/x/onecloud/pkg/util/logclient"
  32. )
  33. type GuestBatchCreateTask struct {
  34. taskutils.SSchedTask
  35. }
  36. func init() {
  37. taskman.RegisterTask(GuestBatchCreateTask{})
  38. }
  39. func (task *GuestBatchCreateTask) GetSchedParams() (*schedapi.ScheduleInput, error) {
  40. params := taskutils.GetBatchParamsAtIndex(task, 0)
  41. input, err := cmdline.FetchScheduleInputByJSON(params)
  42. if err != nil {
  43. return nil, fmt.Errorf("Unmarsh to schedule input: %v", err)
  44. }
  45. return input, err
  46. }
  47. func (task *GuestBatchCreateTask) GetDisks() ([]*api.DiskConfig, error) {
  48. input, err := task.GetSchedParams()
  49. if err != nil {
  50. return nil, err
  51. }
  52. return input.Disks, nil
  53. }
  54. func (task *GuestBatchCreateTask) GetFirstDisk() (*api.DiskConfig, error) {
  55. disks, err := task.GetDisks()
  56. if err != nil {
  57. return nil, err
  58. }
  59. if len(disks) == 0 {
  60. return nil, fmt.Errorf("Empty disks to schedule")
  61. }
  62. return disks[0], nil
  63. }
  64. func (task *GuestBatchCreateTask) GetCreateInput(data jsonutils.JSONObject) (*api.ServerCreateInput, error) {
  65. input := new(api.ServerCreateInput)
  66. err := data.Unmarshal(input)
  67. return input, err
  68. }
  69. func (task *GuestBatchCreateTask) clearPendingUsage(ctx context.Context, guest *models.SGuest) {
  70. taskutils.ClearTaskPendingUsage(ctx, task)
  71. taskutils.ClearTaskPendingRegionUsage(ctx, task)
  72. }
  73. func (task *GuestBatchCreateTask) OnInit(ctx context.Context, objs []db.IStandaloneModel, body jsonutils.JSONObject) {
  74. taskutils.StartScheduleObjects(ctx, task, objs)
  75. }
  76. func (task *GuestBatchCreateTask) OnScheduleFailCallback(ctx context.Context, obj taskutils.IScheduleModel, reason jsonutils.JSONObject, index int) {
  77. guest := obj.(*models.SGuest)
  78. if guest.DisableDelete.IsTrue() {
  79. guest.SetDisableDelete(task.UserCred, false)
  80. }
  81. task.clearPendingUsage(ctx, guest)
  82. task.SSchedTask.OnScheduleFailCallback(ctx, obj, reason, index)
  83. }
  84. func (task *GuestBatchCreateTask) SaveScheduleResultWithBackup(ctx context.Context, obj taskutils.IScheduleModel, master, slave *schedapi.CandidateResource, index int) {
  85. guest := obj.(*models.SGuest)
  86. guest.SetHostIdWithBackup(task.UserCred, master.HostId, slave.HostId)
  87. task.SaveScheduleResult(ctx, obj, master, index)
  88. }
  89. func (task *GuestBatchCreateTask) allocateGuestOnHost(ctx context.Context, guest *models.SGuest, candidate *schedapi.CandidateResource, data *jsonutils.JSONDict) error {
  90. pendingUsage := models.SQuota{}
  91. err := task.GetPendingUsage(&pendingUsage, 0)
  92. if err != nil {
  93. log.Errorf("GetPendingUsage fail %s", err)
  94. }
  95. if conditionparser.IsTemplate(guest.Name) {
  96. guestInfo := guest.GetShortDesc(ctx)
  97. generateName := guest.GetMetadata(ctx, "generate_name", task.UserCred)
  98. if len(generateName) == 0 {
  99. generateName = guest.Name
  100. }
  101. newGenName, err := conditionparser.EvalTemplate(generateName, guestInfo)
  102. if err == nil {
  103. func() {
  104. lockman.LockRawObject(ctx, models.GuestManager.Keyword(), "name")
  105. defer lockman.ReleaseRawObject(ctx, models.GuestManager.Keyword(), "name")
  106. newName, err := db.GenerateName2(ctx, models.GuestManager,
  107. guest.GetOwnerId(), newGenName, guest, 1)
  108. if err == nil {
  109. _, err = db.Update(guest, func() error {
  110. guest.Name = newName
  111. return nil
  112. })
  113. if err != nil {
  114. log.Errorf("guest update name fail %s", err)
  115. }
  116. } else {
  117. log.Errorf("db.GenerateName2 fail %s", err)
  118. }
  119. }()
  120. } else {
  121. log.Errorf("conditionparser.EvalTemplate fail %s", err)
  122. }
  123. }
  124. if len(candidate.CpuNumaPin) > 0 {
  125. if err := guest.SetCpuNumaPin(ctx, task.UserCred, candidate.CpuNumaPin, nil); err != nil {
  126. log.Errorf("SetCpuNumaPin fail %s", err)
  127. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  128. return err
  129. }
  130. }
  131. host, _ := guest.GetHost()
  132. quotaCpuMem := models.SQuota{Count: 1, Cpu: int(guest.VcpuCount), Memory: guest.VmemSize}
  133. keys, err := guest.GetQuotaKeys()
  134. if err != nil {
  135. log.Errorf("guest.GetQuotaKeys fail %s", err)
  136. }
  137. quotaCpuMem.SetKeys(keys)
  138. err = quotas.CancelPendingUsage(ctx, task.UserCred, &pendingUsage, &quotaCpuMem, true) // success
  139. task.SetPendingUsage(&pendingUsage, 0)
  140. input, err := task.GetCreateInput(data)
  141. if err != nil {
  142. log.Errorf("GetCreateInput fail %s", err)
  143. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  144. return err
  145. }
  146. if host.IsPrepaidRecycle() {
  147. input, err = host.SetGuestCreateNetworkAndDiskParams(ctx, task.UserCred, input)
  148. if err != nil {
  149. log.Errorf("host.SetGuestCreateNetworkAndDiskParams fail %s", err)
  150. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  151. return err
  152. }
  153. // params := input.JSON(input)
  154. // task.SaveParams(params)
  155. }
  156. /*input, err = task.GetCreateInput(data)
  157. if err != nil {
  158. guest.SetStatus(ctx,task.UserCred, api.VM_CREATE_FAILED, err.Error())
  159. return err
  160. }*/
  161. pendingRegionUsage := models.SRegionQuota{}
  162. task.GetPendingUsage(&pendingRegionUsage, 1)
  163. // allocate networks
  164. err = guest.CreateNetworksOnHost(ctx, task.UserCred, host, input.Networks, &pendingRegionUsage, &pendingUsage, candidate.Nets)
  165. task.SetPendingUsage(&pendingUsage, 0)
  166. task.SetPendingUsage(&pendingRegionUsage, 1)
  167. if err != nil {
  168. log.Errorf("Network failed: %s", err)
  169. guest.SetStatus(ctx, task.UserCred, api.VM_NETWORK_FAILED, err.Error())
  170. return err
  171. }
  172. if input.PublicIpBw > 0 {
  173. input.Eip, input.EipBw = "", 0
  174. }
  175. // allocate eips
  176. if input.EipBw > 0 {
  177. eip, err := models.ElasticipManager.NewEipForVMOnHost(ctx, task.UserCred, &models.NewEipForVMOnHostArgs{
  178. Bandwidth: input.EipBw,
  179. BgpType: input.EipBgpType,
  180. ChargeType: input.EipChargeType,
  181. AutoDellocate: input.EipAutoDellocate,
  182. Guest: guest,
  183. Host: host,
  184. PendingUsage: &pendingRegionUsage,
  185. })
  186. task.SetPendingUsage(&pendingRegionUsage, 1)
  187. if err != nil {
  188. log.Errorf("guest.CreateElasticipOnHost failed: %s", err)
  189. guest.SetStatus(ctx, task.UserCred, api.VM_NETWORK_FAILED, err.Error())
  190. return err
  191. }
  192. input.Eip = eip.Id
  193. }
  194. drv, err := guest.GetDriver()
  195. if err != nil {
  196. guest.SetStatus(ctx, task.UserCred, api.VM_DISK_FAILED, err.Error())
  197. return err
  198. }
  199. // allocate disks
  200. extraDisks, err := drv.PrepareDiskRaidConfig(task.UserCred, host, input.BaremetalDiskConfigs, input.Disks)
  201. if err != nil {
  202. log.Errorf("PrepareDiskRaidConfig fail: %s", err)
  203. guest.SetStatus(ctx, task.UserCred, api.VM_DISK_FAILED, err.Error())
  204. return err
  205. }
  206. if len(extraDisks) > 0 {
  207. input.Disks = append(input.Disks, extraDisks...)
  208. }
  209. var backupCandidateDisks []*schedapi.CandidateDisk
  210. if candidate.BackupCandidate != nil {
  211. backupCandidateDisks = candidate.BackupCandidate.Disks
  212. }
  213. // 纳管的云需要有关联关系后,在做deploy时才有磁盘的信息
  214. err = guest.CreateDisksOnHost(ctx, task.UserCred, host, input.Disks, &pendingUsage, true, true, candidate.Disks, backupCandidateDisks, true)
  215. task.SetPendingUsage(&pendingUsage, 0)
  216. if err != nil {
  217. log.Errorf("Disk create failed: %s", err)
  218. guest.SetStatus(ctx, task.UserCred, api.VM_DISK_FAILED, err.Error())
  219. return err
  220. }
  221. // allocate GPUs
  222. err = guest.CreateIsolatedDeviceOnHost(ctx, task.UserCred, host, input.IsolatedDevices, &pendingUsage)
  223. task.SetPendingUsage(&pendingUsage, 0)
  224. if err != nil {
  225. log.Errorf("IsolatedDevices create failed: %s", err)
  226. guest.SetStatus(ctx, task.UserCred, api.VM_DEVICE_FAILED, err.Error())
  227. return err
  228. }
  229. // join groups
  230. if input.InstanceGroupIds != nil && len(input.InstanceGroupIds) != 0 {
  231. err := guest.JoinGroups(ctx, task.UserCred, input.InstanceGroupIds)
  232. if err != nil {
  233. log.Errorf("Join Groups failed: %v", err)
  234. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  235. return err
  236. }
  237. }
  238. if guest.IsPrepaidRecycle() {
  239. err := host.RebuildRecycledGuest(ctx, task.UserCred, guest)
  240. if err != nil {
  241. log.Errorf("start guest create task fail %s", err)
  242. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  243. return err
  244. }
  245. autoStart := input.AutoStart
  246. resetPassword := true
  247. if input.ResetPassword != nil {
  248. resetPassword = *input.ResetPassword
  249. }
  250. deployInput := &api.ServerDeployInputBase{}
  251. deployInput.Password = input.Password
  252. deployInput.ResetPassword = resetPassword
  253. err = guest.StartRebuildRootTask(ctx, task.UserCred, "", false, autoStart, true, deployInput)
  254. if err != nil {
  255. log.Errorf("start guest create task fail %s", err)
  256. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  257. return err
  258. }
  259. return nil
  260. }
  261. err = guest.StartGuestCreateTask(ctx, task.UserCred, input, nil, task.GetId())
  262. if err != nil {
  263. log.Errorf("start guest create task fail %s", err)
  264. guest.SetStatus(ctx, task.UserCred, api.VM_CREATE_FAILED, err.Error())
  265. return err
  266. }
  267. return nil
  268. }
  269. func (task *GuestBatchCreateTask) SaveScheduleResult(ctx context.Context, obj taskutils.IScheduleModel, candidate *schedapi.CandidateResource, index int) {
  270. var err error
  271. hostId := candidate.HostId
  272. guest := obj.(*models.SGuest)
  273. if len(guest.HostId) == 0 {
  274. guest.OnScheduleToHost(ctx, task.UserCred, hostId)
  275. }
  276. data := taskutils.GetBatchParamsAtIndex(task, index)
  277. err = task.allocateGuestOnHost(ctx, guest, candidate, data)
  278. if err != nil {
  279. task.clearPendingUsage(ctx, guest)
  280. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, err, task.UserCred)
  281. logclient.AddActionLogWithStartable(task, obj, logclient.ACT_ALLOCATE, err, task.GetUserCred(), false)
  282. notifyclient.EventNotify(ctx, task.GetUserCred(), notifyclient.SEventNotifyParam{
  283. Obj: guest,
  284. Action: notifyclient.ActionCreateBackupServer,
  285. IsFail: true,
  286. })
  287. task.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
  288. }
  289. }
  290. func (task *GuestBatchCreateTask) OnScheduleComplete(ctx context.Context, items []db.IStandaloneModel, data *jsonutils.JSONDict) {
  291. task.SetStageComplete(ctx, nil)
  292. }