disk_delete_task.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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 disk
  15. import (
  16. "context"
  17. "database/sql"
  18. "fmt"
  19. "yunion.io/x/cloudmux/pkg/cloudprovider"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. api "yunion.io/x/onecloud/pkg/apis/compute"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  27. "yunion.io/x/onecloud/pkg/compute/models"
  28. "yunion.io/x/onecloud/pkg/compute/options"
  29. "yunion.io/x/onecloud/pkg/util/logclient"
  30. )
  31. type DiskDeleteTask struct {
  32. SDiskBaseTask
  33. }
  34. func init() {
  35. taskman.RegisterTask(DiskDeleteTask{})
  36. taskman.RegisterTask(StorageDeleteRbdDiskTask{})
  37. }
  38. func (self *DiskDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  39. disk := obj.(*models.SDisk)
  40. cnt, err := disk.GetGuestDiskCount()
  41. if err != nil {
  42. reason := "Disk GetGuestDiskCount fail: " + err.Error()
  43. self.SetStageFailed(ctx, jsonutils.NewString(reason))
  44. db.OpsLog.LogEvent(disk, db.ACT_DELOCATE_FAIL, reason, self.UserCred)
  45. return
  46. }
  47. if cnt > 0 {
  48. reason := "Disk has been attached to server"
  49. self.SetStageFailed(ctx, jsonutils.NewString(reason))
  50. db.OpsLog.LogEvent(disk, db.ACT_DELOCATE_FAIL, reason, self.UserCred)
  51. return
  52. }
  53. if jsonutils.QueryBoolean(self.Params, "delete_snapshots", false) {
  54. self.SetStage("OnDiskSnapshotDelete", nil)
  55. self.StartDeleteDiskSnapshots(ctx, disk)
  56. } else {
  57. self.OnDeleteSnapshots(ctx, disk)
  58. }
  59. }
  60. func (self *DiskDeleteTask) OnDeleteSnapshots(ctx context.Context, disk *models.SDisk) {
  61. isPurge := jsonutils.QueryBoolean(self.Params, "purge", false)
  62. overridePendingDelete := jsonutils.QueryBoolean(self.Params, "override_pending_delete", false)
  63. if len(disk.ExternalId) > 0 {
  64. _, err := disk.GetIDisk(ctx)
  65. if errors.Cause(err) == cloudprovider.ErrNotFound {
  66. overridePendingDelete = true
  67. }
  68. }
  69. if options.Options.EnablePendingDelete && !isPurge && !overridePendingDelete {
  70. if disk.PendingDeleted {
  71. self.SetStageComplete(ctx, nil)
  72. return
  73. }
  74. self.startPendingDeleteDisk(ctx, disk)
  75. } else {
  76. self.startDeleteDisk(ctx, disk)
  77. }
  78. }
  79. func (self *DiskDeleteTask) StartDeleteDiskSnapshots(ctx context.Context, disk *models.SDisk) {
  80. disk.DeleteSnapshots(ctx, self.UserCred, self.GetId())
  81. }
  82. func (self *DiskDeleteTask) OnDiskSnapshotDelete(ctx context.Context, disk *models.SDisk, data jsonutils.JSONObject) {
  83. self.OnDeleteSnapshots(ctx, disk)
  84. }
  85. func (self *DiskDeleteTask) OnDiskSnapshotDeleteFailed(ctx context.Context, disk *models.SDisk, data jsonutils.JSONObject) {
  86. log.Errorf("Delete disk snapshots failed %s", data.String())
  87. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, data)
  88. }
  89. func (self *DiskDeleteTask) startDeleteDisk(ctx context.Context, disk *models.SDisk) {
  90. db.OpsLog.LogEvent(disk, db.ACT_DELOCATING, disk.GetShortDesc(ctx), self.UserCred)
  91. if disk.Status == api.DISK_INIT {
  92. self.OnGuestDiskDeleteComplete(ctx, disk, nil)
  93. return
  94. }
  95. storage, err := disk.GetStorage()
  96. if err != nil {
  97. if errors.Cause(err) == sql.ErrNoRows { // dirty data
  98. self.OnGuestDiskDeleteComplete(ctx, disk, nil)
  99. return
  100. }
  101. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString("disk.GetStorage"))
  102. return
  103. }
  104. purgeParams := jsonutils.QueryBoolean(self.Params, "purge", false)
  105. var host *models.SHost
  106. if hostId := disk.GetLastAttachedHost(ctx, self.UserCred); hostId != "" {
  107. host = models.HostManager.FetchHostById(hostId)
  108. }
  109. if host == nil {
  110. host, err = storage.GetMasterHost()
  111. if err != nil && errors.Cause(err) != sql.ErrNoRows && !purgeParams {
  112. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString("storage.GetMasterHost"))
  113. return
  114. }
  115. }
  116. if host != nil {
  117. disk.RecordDiskSnapshotsLastHost(ctx, self.UserCred, host.Id)
  118. }
  119. isPurge := false
  120. if (host == nil || !host.GetEnabled()) && purgeParams {
  121. isPurge = true
  122. }
  123. disk.SetStatus(ctx, self.UserCred, api.DISK_DEALLOC, "")
  124. if isPurge {
  125. self.OnGuestDiskDeleteComplete(ctx, disk, nil)
  126. return
  127. }
  128. if isNeed, _ := disk.IsNeedWaitSnapshotsDeleted(); isNeed { // for kvm rbd disk
  129. self.OnGuestDiskDeleteComplete(ctx, disk, nil)
  130. return
  131. }
  132. if len(disk.BackupStorageId) > 0 {
  133. self.SetStage("OnMasterStorageDeleteDiskComplete", nil)
  134. } else {
  135. self.SetStage("OnGuestDiskDeleteComplete", nil)
  136. }
  137. if host == nil {
  138. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString("fail to find master host"))
  139. return
  140. }
  141. driver, err := host.GetHostDriver()
  142. if err != nil {
  143. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString(errors.Wrapf(err, "GetHostDriver").Error()))
  144. return
  145. }
  146. err = driver.RequestDeallocateDiskOnHost(ctx, host, storage, disk, false, self)
  147. if err != nil {
  148. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString(err.Error()))
  149. return
  150. }
  151. }
  152. func (self *DiskDeleteTask) OnMasterStorageDeleteDiskComplete(ctx context.Context, disk *models.SDisk, data jsonutils.JSONObject) {
  153. self.SetStage("OnGuestDiskDeleteComplete", nil)
  154. storage := models.StorageManager.FetchStorageById(disk.BackupStorageId)
  155. host, err := storage.GetMasterHost()
  156. if err != nil {
  157. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString(fmt.Sprintf("backup storage %s fail to find master host", disk.BackupStorageId)))
  158. return
  159. }
  160. driver, err := host.GetHostDriver()
  161. if err != nil {
  162. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString(err.Error()))
  163. return
  164. }
  165. err = driver.RequestDeallocateDiskOnHost(ctx, host, storage, disk, false, self)
  166. if err != nil {
  167. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString(err.Error()))
  168. }
  169. }
  170. func (self *DiskDeleteTask) OnMasterStorageDeleteDiskCompleteFailed(ctx context.Context, disk *models.SDisk, reason jsonutils.JSONObject) {
  171. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, reason)
  172. }
  173. func (self *DiskDeleteTask) startPendingDeleteDisk(ctx context.Context, disk *models.SDisk) {
  174. err := disk.DoPendingDelete(ctx, self.UserCred)
  175. if err != nil {
  176. self.OnGuestDiskDeleteCompleteFailed(ctx, disk, jsonutils.NewString("pending delete disk failed"))
  177. return
  178. }
  179. err = models.SnapshotPolicyResourceManager.RemoveByResource(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
  180. if err != nil {
  181. self.OnGuestDiskDeleteCompleteFailed(ctx, disk,
  182. jsonutils.NewString("detach all snapshotpolicies of disk failed"))
  183. return
  184. }
  185. self.SetStageComplete(ctx, nil)
  186. }
  187. func (self *DiskDeleteTask) OnGuestDiskDeleteComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  188. if obj == nil {
  189. self.SetStageComplete(ctx, nil)
  190. return
  191. }
  192. disk := obj.(*models.SDisk)
  193. self.CleanHostSchedCache(disk)
  194. db.OpsLog.LogEvent(disk, db.ACT_DELOCATE, disk.GetShortDesc(ctx), self.UserCred)
  195. notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
  196. Obj: disk,
  197. Action: notifyclient.ActionDelete,
  198. })
  199. if len(disk.SnapshotId) > 0 && disk.GetMetadata(ctx, "merge_snapshot", nil) == "true" {
  200. models.SnapshotManager.AddRefCount(disk.SnapshotId, -1)
  201. }
  202. disk.RealDelete(ctx, self.UserCred)
  203. self.SetStageComplete(ctx, nil)
  204. }
  205. func (self *DiskDeleteTask) OnGuestDiskDeleteCompleteFailed(ctx context.Context, disk *models.SDisk, reason jsonutils.JSONObject) {
  206. disk.SetStatus(ctx, self.GetUserCred(), api.DISK_DEALLOC_FAILED, reason.String())
  207. self.SetStageFailed(ctx, reason)
  208. db.OpsLog.LogEvent(disk, db.ACT_DELOCATE_FAIL, disk.GetShortDesc(ctx), self.GetUserCred())
  209. notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
  210. Obj: disk,
  211. Action: notifyclient.ActionDelete,
  212. IsFail: true,
  213. })
  214. logclient.AddActionLogWithContext(ctx, disk, logclient.ACT_DELOCATE, reason, self.UserCred, false)
  215. }
  216. type StorageDeleteRbdDiskTask struct {
  217. taskman.STask
  218. }
  219. func (self *StorageDeleteRbdDiskTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
  220. storage := obj.(*models.SStorage)
  221. self.DeleteDisk(ctx, storage, self.Params)
  222. }
  223. func (self *StorageDeleteRbdDiskTask) OnDeleteDisk(ctx context.Context, storage *models.SStorage, data jsonutils.JSONObject) {
  224. self.DeleteDisk(ctx, storage, data)
  225. }
  226. func (self *StorageDeleteRbdDiskTask) DeleteDisk(ctx context.Context, storage *models.SStorage, data jsonutils.JSONObject) {
  227. disksId := make([]string, 0)
  228. data.Unmarshal(&disksId, "disks_id")
  229. if len(disksId) == 0 {
  230. self.SetStageComplete(ctx, nil)
  231. return
  232. }
  233. params := jsonutils.NewDict()
  234. params.Set("disks_id", jsonutils.Marshal(disksId[1:]))
  235. params.Set("delete_disk", jsonutils.NewString(disksId[0]))
  236. self.SetStage("OnDeleteDisk", params)
  237. header := self.GetTaskRequestHeader()
  238. url := fmt.Sprintf("/disks/%s/delete/%s", storage.Id, disksId[0])
  239. body := jsonutils.NewDict()
  240. host, err := storage.GetMasterHost()
  241. if err != nil {
  242. self.OnDeleteDiskFailed(ctx, storage, jsonutils.NewString("storage.GetMasterHost"))
  243. return
  244. }
  245. _, err = host.Request(ctx, self.GetUserCred(), "POST", url, header, body)
  246. if err != nil {
  247. params.Set("err", jsonutils.NewString(err.Error()))
  248. self.OnDeleteDiskFailed(ctx, storage, params)
  249. }
  250. }
  251. func (self *StorageDeleteRbdDiskTask) OnDeleteDiskFailed(ctx context.Context, storage *models.SStorage, data jsonutils.JSONObject) {
  252. deleteDisk, _ := data.GetString("delete_disk")
  253. db.OpsLog.LogEvent(storage, db.ACT_DELETE_OBJECT, fmt.Sprintf("delete disk %s failed", deleteDisk), self.UserCred)
  254. notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
  255. Obj: storage,
  256. Action: notifyclient.ActionDelete,
  257. IsFail: true,
  258. })
  259. self.DeleteDisk(ctx, storage, self.Params)
  260. }