volume.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. package models
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "strings"
  7. "time"
  8. "yunion.io/x/jsonutils"
  9. "yunion.io/x/log"
  10. "yunion.io/x/pkg/errors"
  11. "yunion.io/x/pkg/util/httputils"
  12. "yunion.io/x/pkg/utils"
  13. commonapi "yunion.io/x/onecloud/pkg/apis"
  14. computeapi "yunion.io/x/onecloud/pkg/apis/compute"
  15. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  16. api "yunion.io/x/onecloud/pkg/apis/llm"
  17. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  18. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  19. "yunion.io/x/onecloud/pkg/httperrors"
  20. "yunion.io/x/onecloud/pkg/llm/options"
  21. "yunion.io/x/onecloud/pkg/mcclient"
  22. "yunion.io/x/onecloud/pkg/mcclient/auth"
  23. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  24. "yunion.io/x/onecloud/pkg/mcclient/modules/image"
  25. "yunion.io/x/onecloud/pkg/util/logclient"
  26. )
  27. func init() {
  28. GetVolumeManager()
  29. }
  30. var volumeManager *SVolumeManager
  31. func GetVolumeManager() *SVolumeManager {
  32. if volumeManager != nil {
  33. return volumeManager
  34. }
  35. volumeManager = &SVolumeManager{
  36. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  37. SVolume{},
  38. "volumes_tbl",
  39. "llm_volume",
  40. "llm_volumes",
  41. ),
  42. }
  43. volumeManager.SetVirtualObject(volumeManager)
  44. return volumeManager
  45. }
  46. type SVolumeManager struct {
  47. db.SVirtualResourceBaseManager
  48. SMountedModelsResourceManager
  49. }
  50. type SVolume struct {
  51. db.SVirtualResourceBase
  52. SMountedModelsResource
  53. LLMId string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"user"`
  54. // 存储类型
  55. StorageType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"user"`
  56. // 模板ID
  57. TemplateId string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"admin_optional" update:"user"`
  58. // size in MB
  59. SizeMB int `nullable:"false" default:"0" create:"optional" list:"user" update:"user"`
  60. CmpId string `width:"128" charset:"ascii" nullable:"true" list:"user"`
  61. Containers api.ContainerVolumeRelations `charset:"utf8" nullable:"true" list:"user" create:"optional"`
  62. }
  63. func (volume *SVolume) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  64. task, err := taskman.TaskManager.NewTask(ctx, "VolumeDeleteTask", volume, userCred, nil, parentTaskId, "", nil)
  65. if err != nil {
  66. return err
  67. }
  68. volume.SetStatus(ctx, userCred, commonapi.STATUS_DELETING, "")
  69. task.ScheduleRun(nil)
  70. return nil
  71. }
  72. func (volume *SVolume) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  73. return volume.SVirtualResourceBase.Delete(ctx, userCred)
  74. }
  75. func (volume *SVolume) StartResizeTask(ctx context.Context, userCred mcclient.TokenCredential, input api.VolumeResizeTaskInput, parentTaskId string) (*taskman.STask, error) {
  76. volume.SetStatus(ctx, userCred, computeapi.DISK_START_RESIZE, "StartResizeTask")
  77. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  78. task, err := taskman.TaskManager.NewTask(ctx, "VolumeResizeTask", volume, userCred, params, parentTaskId, "", nil)
  79. if err != nil {
  80. return nil, errors.Wrap(err, "NewTask")
  81. }
  82. if err := task.ScheduleRun(nil); err != nil {
  83. return nil, errors.Wrap(err, "ScheduleRun")
  84. }
  85. return task, nil
  86. }
  87. func fetchImage(ctx context.Context, userCred mcclient.TokenCredential, imageId string) (*imageapi.ImageDetails, error) {
  88. s := auth.GetSession(ctx, userCred, options.Options.Region)
  89. imgObj, err := image.Images.Get(s, imageId, nil)
  90. if err != nil {
  91. return nil, errors.Wrapf(err, "Image.Get %s", imageId)
  92. }
  93. img := imageapi.ImageDetails{}
  94. err = imgObj.Unmarshal(&img)
  95. if err != nil {
  96. return nil, errors.Wrap(err, "Unmarshal")
  97. }
  98. return &img, nil
  99. }
  100. func (volume *SVolume) UpdateMountedModelFullNames(mountModels []string) error {
  101. _, err := db.Update(volume, func() error {
  102. volume.MountedModels = mountModels
  103. return nil
  104. })
  105. if err != nil {
  106. return errors.Wrap(err, "update volume mounted_apps")
  107. }
  108. return nil
  109. }
  110. func (volume *SVolume) GetDisk(ctx context.Context) (*computeapi.DiskDetails, error) {
  111. if len(volume.CmpId) == 0 {
  112. return nil, errors.ErrInvalidStatus
  113. }
  114. s := auth.GetAdminSession(ctx, "")
  115. disk := computeapi.DiskDetails{}
  116. resp, err := compute.Disks.GetById(s, volume.CmpId, jsonutils.Marshal(map[string]interface{}{
  117. "scope": "max",
  118. }))
  119. if err != nil {
  120. if httputils.ErrorCode(err) == 404 {
  121. return nil, errors.Wrapf(errors.ErrNotFound, "GetById %s", volume.CmpId)
  122. }
  123. return nil, errors.Wrap(err, "fetch disk")
  124. }
  125. resp.Unmarshal(&disk)
  126. return &disk, nil
  127. }
  128. func (volume *SVolume) WaitDiskStatus(ctx context.Context, userCred mcclient.TokenCredential, targetStatus []string, timeoutSecs int) (*computeapi.DiskDetails, error) {
  129. expire := time.Now().Add(time.Second * time.Duration(timeoutSecs))
  130. for time.Now().Before(expire) {
  131. disk, err := volume.GetDisk(ctx)
  132. if err != nil {
  133. return nil, errors.Wrap(err, "GetDisk")
  134. }
  135. if utils.IsInArray(disk.Status, targetStatus) {
  136. return disk, nil
  137. }
  138. if strings.Contains(disk.Status, "fail") {
  139. return nil, errors.Wrap(errors.ErrInvalidStatus, disk.Status)
  140. }
  141. time.Sleep(2 * time.Second)
  142. }
  143. return nil, errors.Wrapf(httperrors.ErrTimeout, "wait disk status %s timeout", targetStatus)
  144. }
  145. func (volume *SVolume) GetLLM() *SLLM {
  146. if len(volume.LLMId) == 0 {
  147. return nil
  148. }
  149. obj, err := GetLLMManager().FetchById(volume.LLMId)
  150. if err != nil {
  151. log.Errorf("Volume %s fetch llm %s error %s", volume.Id, volume.LLMId, err)
  152. return nil
  153. }
  154. return obj.(*SLLM)
  155. }
  156. func (volume *SVolume) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.VolumePerformResetInput) (*api.LLMBatchPerformOutput, error) {
  157. if volume.Status != computeapi.DISK_READY {
  158. return nil, errors.Wrapf(httperrors.ErrInvalidStatus, "invalid status %s", volume.Status)
  159. }
  160. task, err := volume.StartResetTask(ctx, userCred, input, "")
  161. if err != nil {
  162. return nil, errors.Wrap(err, "StartResetTask")
  163. }
  164. return &api.LLMBatchPerformOutput{
  165. Data: []api.LLMPerformOutput{
  166. {
  167. Id: volume.Id,
  168. Name: volume.Name,
  169. RequestStatus: http.StatusOK,
  170. TaskId: task.Id,
  171. },
  172. },
  173. Task: task,
  174. }, nil
  175. }
  176. func (volume *SVolume) StartResetTask(ctx context.Context, userCred mcclient.TokenCredential, input api.VolumePerformResetInput, parentTaskId string) (*taskman.STask, error) {
  177. volume.SetStatus(ctx, userCred, api.VOLUME_STATUS_START_RESET, "StartResetTask")
  178. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  179. task, err := taskman.TaskManager.NewTask(ctx, "VolumeResetTask", volume, userCred, params, parentTaskId, "")
  180. if err != nil {
  181. return nil, errors.Wrap(err, "NewTask")
  182. }
  183. err = task.ScheduleRun(nil)
  184. if err != nil {
  185. return nil, errors.Wrap(err, "ScheduleRun")
  186. }
  187. return task, nil
  188. }
  189. func (volume *SVolume) DoReset(ctx context.Context, userCred mcclient.TokenCredential, templateId, backupId *string, sizeGb int) error {
  190. // clear desktop app list
  191. {
  192. llm := volume.GetLLM()
  193. if llm != nil {
  194. llm.purgeModelList()
  195. }
  196. }
  197. // clear mounts
  198. {
  199. _, err := db.Update(volume, func() error {
  200. volume.MountedModels = nil
  201. return nil
  202. })
  203. if err != nil {
  204. return errors.Wrap(err, "update volume mounted_apps")
  205. }
  206. }
  207. // reset disk
  208. s := auth.GetSession(ctx, userCred, options.Options.Region)
  209. params := computeapi.DiskRebuildInput{}
  210. params.TemplateId = templateId
  211. params.BackupId = backupId
  212. emptyStr := ""
  213. if params.BackupId == nil {
  214. params.BackupId = &emptyStr
  215. }
  216. if sizeGb > 0 {
  217. size := fmt.Sprintf("%dG", sizeGb)
  218. params.Size = &size
  219. }
  220. log.Debugf("need to reset phone data disk ... %s %dG", jsonutils.Marshal(params), sizeGb)
  221. _, err := compute.Disks.PerformAction(s, volume.CmpId, "rebuild", jsonutils.Marshal(params))
  222. if err != nil {
  223. return errors.Wrap(err, "rebuild disk")
  224. }
  225. return nil
  226. }
  227. func (volume *SVolume) UpdateSize(ctx context.Context, userCred mcclient.TokenCredential, sizeMb int) error {
  228. if volume.SizeMB == sizeMb {
  229. return nil
  230. }
  231. oldSize := volume.SizeMB
  232. _, err := db.Update(volume, func() error {
  233. volume.SizeMB = sizeMb
  234. return nil
  235. })
  236. if err != nil {
  237. logclient.AddSimpleActionLog(volume, logclient.ACT_RESIZE, err, userCred, false)
  238. return errors.Wrap(err, "update volume size")
  239. }
  240. notes := struct {
  241. OldSizeMb int `json:"old_size_mb"`
  242. NewSizeMb int `json:"new_size_mb"`
  243. }{
  244. OldSizeMb: oldSize,
  245. NewSizeMb: sizeMb,
  246. }
  247. logclient.AddSimpleActionLog(volume, logclient.ACT_RESIZE, jsonutils.Marshal(notes), userCred, true)
  248. return nil
  249. }