guest_create_task.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/billing"
  23. api "yunion.io/x/onecloud/pkg/apis/compute"
  24. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  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. "yunion.io/x/onecloud/pkg/util/logclient"
  30. )
  31. type GuestCreateTask struct {
  32. SGuestBaseTask
  33. }
  34. func init() {
  35. taskman.RegisterTask(GuestCreateTask{})
  36. }
  37. func (self *GuestCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
  38. guest := obj.(*models.SGuest)
  39. guest.SetStatus(ctx, self.UserCred, api.VM_CREATE_NETWORK, "")
  40. self.SetStage("OnWaitGuestNetworksReady", nil)
  41. self.OnWaitGuestNetworksReady(ctx, obj, nil)
  42. }
  43. func (self *GuestCreateTask) OnWaitGuestNetworksReady(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  44. guest := obj.(*models.SGuest)
  45. if !guest.IsNetworkAllocated() {
  46. log.Infof("Guest %s network not ready!!", guest.Name)
  47. time.Sleep(time.Second * 2)
  48. self.ScheduleRun(nil)
  49. } else {
  50. self.OnGuestNetworkReady(ctx, guest)
  51. }
  52. }
  53. func (self *GuestCreateTask) OnGuestNetworkReady(ctx context.Context, guest *models.SGuest) {
  54. guest.SetStatus(ctx, self.UserCred, api.VM_CREATE_DISK, "")
  55. self.SetStage("OnDiskPrepared", nil)
  56. drv, err := guest.GetDriver()
  57. if err != nil {
  58. self.OnDiskPreparedFailed(ctx, guest, jsonutils.NewString(err.Error()))
  59. return
  60. }
  61. err = drv.RequestGuestCreateAllDisks(ctx, guest, self)
  62. if err != nil {
  63. msg := fmt.Sprintf("unable to RequestGuestCreateAllDisks: %v", err)
  64. self.OnDiskPreparedFailed(ctx, guest, jsonutils.NewString(msg))
  65. }
  66. }
  67. func (self *GuestCreateTask) OnDiskPreparedFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  68. guest.SetStatus(ctx, self.UserCred, api.VM_DISK_FAILED, "allocation failed")
  69. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
  70. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false)
  71. notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
  72. Obj: guest,
  73. Action: notifyclient.ActionCreate,
  74. IsFail: true,
  75. })
  76. self.SetStageFailed(ctx, data)
  77. }
  78. func (self *GuestCreateTask) OnDiskPrepared(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  79. secgroups, err := guest.GetSecgroups()
  80. if err != nil {
  81. self.OnSecurityGroupPreparedFailed(ctx, guest, jsonutils.NewString(errors.Wrapf(err, "GetSecgroups").Error()))
  82. return
  83. }
  84. if len(secgroups) == 0 {
  85. self.OnSecurityGroupPrepared(ctx, guest, nil)
  86. return
  87. }
  88. vpc, err := guest.GetVpc()
  89. if err != nil {
  90. self.OnSecurityGroupPreparedFailed(ctx, guest, jsonutils.NewString(errors.Wrapf(err, "GetVpc").Error()))
  91. return
  92. }
  93. region, err := vpc.GetRegion()
  94. if err != nil {
  95. self.OnSecurityGroupPreparedFailed(ctx, guest, jsonutils.NewString(errors.Wrapf(err, "GetRegion").Error()))
  96. return
  97. }
  98. self.SetStage("OnSecurityGroupPrepared", nil)
  99. err = region.GetDriver().RequestPrepareSecurityGroups(ctx, self.UserCred, guest.GetOwnerId(), secgroups, vpc, func(ids []string) error {
  100. return guest.SaveSecgroups(ctx, self.UserCred, ids)
  101. }, self)
  102. if err != nil {
  103. self.OnSecurityGroupPreparedFailed(ctx, guest, jsonutils.NewString(err.Error()))
  104. return
  105. }
  106. }
  107. func (self *GuestCreateTask) OnSecurityGroupPreparedFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  108. guest.SetStatus(ctx, self.UserCred, api.VM_SECURITY_GROUP_FAILED, "prepare security group failed")
  109. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
  110. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false)
  111. notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
  112. Obj: guest,
  113. Action: notifyclient.ActionCreate,
  114. IsFail: true,
  115. })
  116. self.SetStageFailed(ctx, data)
  117. }
  118. func (self *GuestCreateTask) OnSecurityGroupPrepared(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  119. cdrom, _ := self.Params.GetString("cdrom")
  120. var bootIndex *int8
  121. if self.Params.Contains("cdrom_boot_index") {
  122. bd, _ := self.Params.Int("cdrom_boot_index")
  123. bd8 := int8(bd)
  124. bootIndex = &bd8
  125. }
  126. if len(cdrom) > 0 {
  127. image, err := models.CachedimageManager.GetImageInfo(ctx, self.UserCred, cdrom, false)
  128. if err != nil {
  129. log.Errorf("failed get image %s info: %s", cdrom, err)
  130. } else {
  131. imagePros := map[string]interface{}{}
  132. for _, k := range []string{imageapi.IMAGE_OS_ARCH, imageapi.IMAGE_OS_DISTRO, imageapi.IMAGE_OS_VERSION, imageapi.IMAGE_OS_TYPE} {
  133. if v, ok := image.Properties[k]; ok {
  134. imagePros[k] = v
  135. }
  136. }
  137. guest.SetAllMetadata(ctx, imagePros, self.UserCred)
  138. }
  139. self.SetStage("OnCdromPrepared", nil)
  140. drv, err := guest.GetDriver()
  141. if err != nil {
  142. self.OnCdromPreparedFailed(ctx, guest, jsonutils.NewString(err.Error()))
  143. return
  144. }
  145. drv.RequestGuestCreateInsertIso(ctx, cdrom, bootIndex, self, guest)
  146. } else {
  147. self.OnCdromPrepared(ctx, guest, data)
  148. }
  149. }
  150. func (self *GuestCreateTask) OnCdromPrepared(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
  151. log.Infof("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
  152. log.Infof("DEPLOY GUEST %s", guest.Name)
  153. log.Infof("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
  154. guest.SetStatus(ctx, self.UserCred, api.VM_DEPLOYING, "")
  155. self.StartDeployGuest(ctx, guest)
  156. }
  157. func (self *GuestCreateTask) OnCdromPreparedFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  158. guest := obj.(*models.SGuest)
  159. guest.SetStatus(ctx, self.UserCred, api.VM_DISK_FAILED, "")
  160. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
  161. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false)
  162. notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
  163. Obj: guest,
  164. Action: notifyclient.ActionCreate,
  165. IsFail: true,
  166. })
  167. self.SetStageFailed(ctx, data)
  168. }
  169. func (self *GuestCreateTask) StartDeployGuest(ctx context.Context, guest *models.SGuest) {
  170. self.SetStage("OnDeployGuestDescComplete", nil)
  171. guest.StartGuestDeployTask(ctx, self.UserCred, self.Params, "create", self.GetId())
  172. }
  173. func (self *GuestCreateTask) OnDeployGuestDescComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  174. guest := obj.(*models.SGuest)
  175. // bind eip
  176. {
  177. eipId, _ := self.Params.GetString("eip")
  178. if len(eipId) > 0 {
  179. var err error
  180. self.SetStage("OnDeployEipComplete", nil)
  181. eipObj, err := models.ElasticipManager.FetchById(eipId)
  182. if err != nil {
  183. msg := fmt.Sprintf("fail to get eip %s %s", eipId, err)
  184. self.OnDeployEipCompleteFailed(ctx, obj, jsonutils.NewString(msg))
  185. return
  186. }
  187. eip := eipObj.(*models.SElasticip)
  188. input := api.ElasticipAssociateInput{
  189. InstanceId: guest.Id,
  190. InstanceExternalId: guest.ExternalId,
  191. InstanceType: api.EIP_ASSOCIATE_TYPE_SERVER,
  192. }
  193. eipBw, _ := self.Params.Int("eip_bw")
  194. if eipBw > 0 {
  195. // newly allocated eip, need allocation and associate
  196. err = eip.AllocateAndAssociateInstance(ctx, self.UserCred, guest, input, self.GetId())
  197. err = errors.Wrapf(err, "eip.AllocateAndAssociateInstance")
  198. } else {
  199. // existing eip, association only
  200. err = eip.StartEipAssociateInstanceTask(ctx, self.UserCred, input, self.GetId())
  201. err = errors.Wrapf(err, "eip.StartEipAssociateInstanceTask")
  202. }
  203. if err != nil {
  204. self.OnDeployEipCompleteFailed(ctx, obj, jsonutils.NewString(err.Error()))
  205. return
  206. }
  207. return
  208. }
  209. }
  210. self.OnDeployEipComplete(ctx, guest, nil)
  211. }
  212. func (self *GuestCreateTask) notifyServerCreated(ctx context.Context, guest *models.SGuest) {
  213. guest.EventNotify(ctx, self.UserCred, notifyclient.ActionCreate)
  214. }
  215. func (self *GuestCreateTask) OnDeployGuestDescCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  216. guest := obj.(*models.SGuest)
  217. guest.SetStatus(ctx, self.UserCred, api.VM_DEPLOY_FAILED, "deploy_failed")
  218. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred)
  219. notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
  220. Obj: guest,
  221. Action: notifyclient.ActionCreate,
  222. IsFail: true,
  223. })
  224. self.SetStageFailed(ctx, data)
  225. }
  226. func (self *GuestCreateTask) OnDeployEipComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  227. guest := obj.(*models.SGuest)
  228. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE, nil, self.UserCred)
  229. if !guest.IsSystem {
  230. self.notifyServerCreated(ctx, guest)
  231. }
  232. // Guest Create Complete
  233. duration, _ := self.GetParams().GetString("duration")
  234. if len(duration) > 0 {
  235. bc, err := billing.ParseBillingCycle(duration)
  236. if err == nil && guest.ExpiredAt.IsZero() {
  237. models.SaveRenewInfo(ctx, self.GetUserCred(), guest, &bc, nil, "")
  238. }
  239. if jsonutils.QueryBoolean(self.GetParams(), "auto_prepaid_recycle", false) {
  240. err := guest.CanPerformPrepaidRecycle()
  241. if err == nil {
  242. self.SetStageComplete(ctx, nil)
  243. guest.DoPerformPrepaidRecycle(ctx, self.GetUserCred(), true)
  244. }
  245. }
  246. }
  247. if jsonutils.QueryBoolean(self.GetParams(), "auto_start", false) {
  248. self.SetStage("OnAutoStartGuest", nil)
  249. params := jsonutils.NewDict()
  250. params.Set("start_from_create", jsonutils.JSONTrue)
  251. guest.StartGueststartTask(ctx, self.GetUserCred(), params, self.GetTaskId())
  252. } else {
  253. self.SetStage("OnSyncStatusComplete", nil)
  254. guest.StartSyncstatus(ctx, self.GetUserCred(), self.GetTaskId())
  255. }
  256. }
  257. func (self *GuestCreateTask) OnDeployEipCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  258. guest := obj.(*models.SGuest)
  259. guest.SetStatus(ctx, self.UserCred, api.INSTANCE_ASSOCIATE_EIP_FAILED, "deploy_failed")
  260. db.OpsLog.LogEvent(guest, db.ACT_EIP_ATTACH, data, self.UserCred)
  261. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_EIP_ASSOCIATE, data, self.UserCred, false)
  262. notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.INSTANCE_ASSOCIATE_EIP_FAILED, data.String())
  263. self.SetStageFailed(ctx, data)
  264. }
  265. func (self *GuestCreateTask) OnAutoStartGuest(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  266. guest := obj.(*models.SGuest)
  267. self.TaskComplete(ctx, guest)
  268. }
  269. func (self *GuestCreateTask) OnSyncStatusComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  270. guest := obj.(*models.SGuest)
  271. self.TaskComplete(ctx, guest)
  272. }
  273. func (self *GuestCreateTask) TaskComplete(ctx context.Context, guest *models.SGuest) {
  274. db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE, "", self.UserCred)
  275. logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_ALLOCATE, "", self.UserCred, true)
  276. self.SetStageComplete(ctx, guest.GetShortDesc(ctx))
  277. }