storage_base.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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 storageman
  15. import (
  16. "context"
  17. "fmt"
  18. "io/ioutil"
  19. "path"
  20. "strings"
  21. "sync"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/util/qemuimgfmt"
  26. "yunion.io/x/pkg/util/regutils"
  27. "yunion.io/x/onecloud/pkg/apis"
  28. api "yunion.io/x/onecloud/pkg/apis/compute"
  29. hostapi "yunion.io/x/onecloud/pkg/apis/host"
  30. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  31. "yunion.io/x/onecloud/pkg/hostman/guestman/desc"
  32. deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
  33. "yunion.io/x/onecloud/pkg/hostman/hostutils"
  34. "yunion.io/x/onecloud/pkg/hostman/options"
  35. "yunion.io/x/onecloud/pkg/hostman/storageman/storageutils"
  36. "yunion.io/x/onecloud/pkg/httperrors"
  37. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  38. "yunion.io/x/onecloud/pkg/mcclient/modules/image"
  39. "yunion.io/x/onecloud/pkg/util/procutils"
  40. "yunion.io/x/onecloud/pkg/util/qemuimg"
  41. )
  42. const (
  43. _RECYCLE_BIN_ = "recycle_bin"
  44. _IMGSAVE_BACKUPS_ = "imgsave_backups"
  45. _SNAPSHOT_PATH_ = "snapshots"
  46. _BACKUP_PATH_ = "backups"
  47. ErrStorageTimeout = constError("storage accessible check timeout")
  48. TempBindMountPath = "/opt/cloud/workspace/temp-bind"
  49. )
  50. type constError string
  51. func (e constError) Error() string { return string(e) }
  52. var DELETEING_SNAPSHOTS = sync.Map{}
  53. type IStorageFactory interface {
  54. NewStorage(manager *SStorageManager, mountPoint string) IStorage
  55. StorageType() string
  56. }
  57. var (
  58. storagesFactories = make([]IStorageFactory, 0)
  59. )
  60. // for shared storages
  61. func registerStorageFactory(factory IStorageFactory) {
  62. storagesFactories = append(storagesFactories, factory)
  63. }
  64. func NewStorage(manager *SStorageManager, mountPoint, storageType string) IStorage {
  65. for i := range storagesFactories {
  66. if storageType == storagesFactories[i].StorageType() || strings.HasPrefix(mountPoint, storagesFactories[i].StorageType()) {
  67. return storagesFactories[i].NewStorage(manager, mountPoint)
  68. }
  69. }
  70. log.Errorf("no storage driver for %s found", storageType)
  71. return nil
  72. }
  73. type IStorage interface {
  74. GetId() string
  75. GetStorageName() string
  76. GetZoneId() string
  77. SetStorageInfo(storageId, storageName string, conf jsonutils.JSONObject) error
  78. SyncStorageInfo() (jsonutils.JSONObject, error)
  79. SyncStorageSize() (api.SHostStorageStat, error)
  80. StorageType() string
  81. GetStorageConf() *jsonutils.JSONDict
  82. GetStoragecacheId() string
  83. SetStoragecacheId(storagecacheId string)
  84. IsLocal() bool
  85. Lvmlockd() bool
  86. SetPath(string)
  87. GetPath() string
  88. GetSnapshotDir() string
  89. GetSnapshotPathByIds(diskId, snapshotId string) string
  90. DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
  91. DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
  92. IsSnapshotExist(diskId, snapshotId string) (bool, error)
  93. GetBackupDir() string
  94. StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error)
  95. StorageBackupRecovery(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
  96. GetFreeSizeMb() int
  97. GetCapacityMb() int
  98. // Find owner disks first, if not found, call create disk
  99. GetDiskById(diskId string) (IDisk, error)
  100. CreateDisk(diskId string) IDisk
  101. RemoveDisk(IDisk)
  102. GetDisksPath() ([]string, error)
  103. // DeleteDisk(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
  104. // *SDiskCreateByDiskinfo
  105. CreateDiskByDiskinfo(context.Context, interface{}) (jsonutils.JSONObject, error)
  106. SaveToGlance(context.Context, interface{}) (jsonutils.JSONObject, error)
  107. CreateDiskFromSnapshot(context.Context, IDisk, *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error)
  108. CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error
  109. CreateDiskFromBackup(context.Context, IDisk, *SDiskCreateByDiskinfo) error
  110. DiskMigrate(context.Context, interface{}) (jsonutils.JSONObject, error)
  111. // GetCloneTargetDiskPath generate target disk path by target disk id
  112. GetCloneTargetDiskPath(ctx context.Context, targetDiskId string) string
  113. // CloneDiskFromStorage clone disk from other storage
  114. CloneDiskFromStorage(ctx context.Context, srcStorage IStorage, srcDisk IDisk, targetDiskId string, fullCopy bool, encInfo apis.SEncryptInfo) (*hostapi.ServerCloneDiskFromStorageResponse, error)
  115. CreateSnapshotFormUrl(ctx context.Context, snapshotUrl, diskId, snapshotPath string) error
  116. DeleteDiskfile(diskPath string, skipRecycle bool) error
  117. GetFuseTmpPath() string
  118. GetFuseMountPath() string
  119. GetImgsaveBackupPath() string
  120. DestinationPrepareMigrate(ctx context.Context, liveMigrate bool, disksUri string, snapshotsUri string,
  121. disksBackingFile, diskSnapsChain, outChainSnaps jsonutils.JSONObject, rebaseDisks bool, diskDesc *desc.SGuestDisk, serverId string, idx, totalDiskCount int, encInfo *apis.SEncryptInfo, sysDiskHasTemplate bool) error
  122. Accessible() error
  123. Detach() error
  124. CleanRecycleDiskfiles(ctx context.Context)
  125. }
  126. type SBaseStorage struct {
  127. Manager *SStorageManager
  128. StorageId string
  129. Path string
  130. StorageName string
  131. StorageConf *jsonutils.JSONDict
  132. StoragecacheId string
  133. isSetStorageInfo bool
  134. Disks []IDisk
  135. DiskLock *sync.Mutex
  136. }
  137. func NewBaseStorage(manager *SStorageManager, path string) *SBaseStorage {
  138. var ret = new(SBaseStorage)
  139. ret.Disks = make([]IDisk, 0)
  140. ret.DiskLock = new(sync.Mutex)
  141. ret.Manager = manager
  142. ret.Path = path
  143. return ret
  144. }
  145. func (s *SBaseStorage) GetId() string {
  146. return s.StorageId
  147. }
  148. func (s *SBaseStorage) Lvmlockd() bool {
  149. return false
  150. }
  151. func (s *SBaseStorage) GetFuseTmpPath() string {
  152. return path.Join(s.Path, _FUSE_TMP_PATH_)
  153. }
  154. func (s *SBaseStorage) GetFuseMountPath() string {
  155. return path.Join(s.Path, _FUSE_MOUNT_PATH_)
  156. }
  157. func (s *SBaseStorage) GetStorageName() string {
  158. return s.StorageName
  159. }
  160. func (s *SBaseStorage) GetStoragecacheId() string {
  161. return s.StoragecacheId
  162. }
  163. func (s *SBaseStorage) SetStoragecacheId(storagecacheId string) {
  164. s.StoragecacheId = storagecacheId
  165. }
  166. func (s *SBaseStorage) StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error) {
  167. return nil, nil
  168. }
  169. func (s *SBaseStorage) StorageBackupRecovery(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
  170. return nil, nil
  171. }
  172. func (s *SBaseStorage) GetName(generateName func() string) string {
  173. if len(s.StorageName) > 0 {
  174. return s.StorageName
  175. } else {
  176. return generateName()
  177. }
  178. }
  179. func (s *SBaseStorage) GetPath() string {
  180. return s.Path
  181. }
  182. func (s *SBaseStorage) SetPath(p string) {
  183. s.Path = p
  184. }
  185. func (s *SBaseStorage) GetZoneId() string {
  186. return s.Manager.GetZoneId()
  187. }
  188. func (d *SBaseStorage) GetDisksPath() ([]string, error) {
  189. spath := d.GetPath()
  190. files, err := ioutil.ReadDir(spath)
  191. if err != nil {
  192. return nil, err
  193. }
  194. disksPath := make([]string, 0)
  195. for i := range files {
  196. if !files[i].IsDir() && regutils.MatchUUIDExact(files[i].Name()) {
  197. disksPath = append(disksPath, path.Join(spath, files[i].Name()))
  198. }
  199. }
  200. return disksPath, nil
  201. }
  202. func (s *SBaseStorage) GetCapacityMb() int {
  203. return s.GetAvailSizeMb()
  204. }
  205. func (s *SBaseStorage) GetStorageConf() *jsonutils.JSONDict {
  206. return s.StorageConf
  207. }
  208. func (s *SBaseStorage) GetAvailSizeMb() int {
  209. return s.GetTotalSizeMb()
  210. }
  211. func (s *SBaseStorage) GetUsedSizeMb() int {
  212. size, err := storageutils.GetUsedSizeMb(s.Path)
  213. if err != nil {
  214. log.Errorf("failed get %s used size: %s", s.Path, err)
  215. return -1
  216. }
  217. return size
  218. }
  219. /*func (s *SBaseStorage) GetMediumType() string {
  220. return s.Manager.GetMediumType()
  221. }*/
  222. func (s *SBaseStorage) GetFreeSizeMb() int {
  223. size, err := storageutils.GetFreeSizeMb(s.Path)
  224. if err != nil {
  225. log.Errorf("failed get %s free size: %s", s.Path, err)
  226. return -1
  227. }
  228. return size
  229. }
  230. func (s *SBaseStorage) GetTotalSizeMb() int {
  231. size, err := storageutils.GetTotalSizeMb(s.Path)
  232. if err != nil {
  233. log.Errorf("failed get %s total size: %s", s.Path, err)
  234. return -1
  235. }
  236. return size
  237. }
  238. func (s *SBaseStorage) SetStorageInfo(storageId, storageName string, conf jsonutils.JSONObject) error {
  239. s.StorageId = storageId
  240. s.StorageName = storageName
  241. if dconf, ok := conf.(*jsonutils.JSONDict); ok {
  242. s.StorageConf = dconf
  243. }
  244. s.BindMountStoragePath(s.Path)
  245. return nil
  246. }
  247. func (s *SBaseStorage) SyncStorageSize() (api.SHostStorageStat, error) {
  248. stat := api.SHostStorageStat{
  249. StorageId: s.StorageId,
  250. }
  251. stat.CapacityMb = int64(s.GetCapacityMb())
  252. stat.ActualCapacityUsedMb = int64(s.GetUsedSizeMb())
  253. return stat, nil
  254. }
  255. func (s *SBaseStorage) BindMountStoragePath(sPath string) error {
  256. if strings.HasPrefix(sPath, "/opt/cloud") {
  257. return nil
  258. }
  259. if s.isSetStorageInfo || !options.HostOptions.EnableRemoteExecutor {
  260. return nil
  261. }
  262. err := s.bindMountTo(sPath)
  263. if err == nil {
  264. s.isSetStorageInfo = true
  265. }
  266. return err
  267. }
  268. func (s *SBaseStorage) bindMountTo(sPath string) error {
  269. tempPath := path.Join(TempBindMountPath, sPath)
  270. out, err := procutils.NewCommand("mkdir", "-p", tempPath).Output()
  271. if err != nil {
  272. return errors.Errorf("mkdir temp mount path %s failed %s", tempPath, out)
  273. }
  274. out, err = procutils.NewCommand("mkdir", "-p", sPath).Output()
  275. if err != nil {
  276. return errors.Errorf("mkdir mount path %s failed %s", sPath, out)
  277. }
  278. if procutils.NewCommand("mountpoint", tempPath).Run() != nil {
  279. out, err = procutils.NewRemoteCommandAsFarAsPossible("mount", "--bind", sPath, tempPath).Output()
  280. if err != nil {
  281. return errors.Errorf("bind mount to temp path failed %s", out)
  282. }
  283. }
  284. if procutils.NewCommand("mountpoint", sPath).Run() != nil {
  285. out, err = procutils.NewCommand("mount", "--bind", tempPath, sPath).Output()
  286. if err != nil {
  287. return errors.Errorf("bind mount temp path to local image path failed %s", out)
  288. }
  289. }
  290. log.Infof("bind mount %s -> %s", tempPath, sPath)
  291. return nil
  292. }
  293. func (s *SBaseStorage) RemoveDisk(d IDisk) {
  294. s.DiskLock.Lock()
  295. defer s.DiskLock.Unlock()
  296. for i := 0; i < len(s.Disks); i++ {
  297. if s.Disks[i].GetId() == d.GetId() {
  298. s.Disks = append(s.Disks[:i], s.Disks[i+1:]...)
  299. break
  300. }
  301. }
  302. }
  303. func (s *SBaseStorage) DeleteDiskfile(diskpath string, skipRecycle bool) error {
  304. return fmt.Errorf("Not Implement")
  305. }
  306. func (s *SBaseStorage) CreateDiskByDiskinfo(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
  307. createParams, ok := params.(*SDiskCreateByDiskinfo)
  308. if !ok {
  309. return nil, hostutils.ParamsError
  310. }
  311. if createParams.Disk != nil {
  312. if !createParams.DiskInfo.Rebuild {
  313. return nil, fmt.Errorf("Disk exist")
  314. }
  315. if createParams.DiskInfo.Backup != nil && createParams.DiskInfo.Backup.BackupAsTar != nil {
  316. log.Infof("skip OnRebuildRoot step when backup_as_tar is provided")
  317. } else {
  318. if err := createParams.Disk.OnRebuildRoot(ctx, createParams.DiskInfo); err != nil {
  319. return nil, err
  320. }
  321. }
  322. }
  323. disk := createParams.Storage.CreateDisk(createParams.DiskId)
  324. if disk == nil {
  325. return nil, fmt.Errorf("Fail to Create disk %s", createParams.DiskId)
  326. }
  327. log.Infof("storage %s %s(%s) start create disk", createParams.Storage.StorageType(), createParams.Storage.GetStorageName(), createParams.Storage.GetId())
  328. switch {
  329. case len(createParams.DiskInfo.SnapshotId) > 0:
  330. log.Infof("CreateDiskFromSnpashot %s", createParams)
  331. return s.CreateDiskFromSnpashot(ctx, disk, createParams)
  332. case len(createParams.DiskInfo.ImageId) > 0 && createParams.DiskInfo.ImageFormat != imageapi.IMAGE_DISK_FORMAT_TGZ:
  333. log.Infof("CreateDiskFromTemplate %s", createParams)
  334. return s.CreateDiskFromTemplate(ctx, disk, createParams)
  335. case createParams.DiskInfo.Backup != nil:
  336. log.Infof("CreateDiskFromBackup %s", createParams)
  337. return s.createDiskFromBackup(ctx, disk, createParams)
  338. case len(createParams.DiskInfo.ExistingPath) > 0:
  339. return s.createDiskFromExistingPath(ctx, disk, createParams)
  340. case createParams.DiskInfo.DiskSizeMb > 0:
  341. log.Infof("CreateRawDisk %s", createParams)
  342. return s.CreateRawDisk(ctx, disk, createParams)
  343. default:
  344. return nil, fmt.Errorf("CreateDiskByDiskinfo with params %s empty snapshot_id, image_id or disk_size_mb", jsonutils.Marshal(createParams.DiskInfo))
  345. }
  346. }
  347. func (s *SBaseStorage) CreateRawDisk(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
  348. var encryptInfo *apis.SEncryptInfo
  349. if input.DiskInfo.Encryption {
  350. encryptInfo = &input.DiskInfo.EncryptInfo
  351. }
  352. return disk.CreateRaw(ctx, input.DiskInfo.DiskSizeMb,
  353. input.DiskInfo.Format,
  354. input.DiskInfo.FsFormat,
  355. input.DiskInfo.FsFeatures,
  356. encryptInfo, input.DiskId, "")
  357. }
  358. func (s *SBaseStorage) CreateDiskFromTemplate(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
  359. var (
  360. format = "qcow2" // force qcow2
  361. )
  362. var encryptInfo *apis.SEncryptInfo
  363. if input.DiskInfo.Encryption {
  364. encryptInfo = &input.DiskInfo.EncryptInfo
  365. }
  366. return disk.CreateFromTemplate(ctx, input.DiskInfo.ImageId, format, int64(input.DiskInfo.DiskSizeMb), encryptInfo)
  367. }
  368. func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
  369. var storage = input.Storage
  370. if len(input.DiskInfo.SnapshotUrl) == 0 {
  371. return nil, httperrors.NewMissingParameterError("snapshot_url")
  372. }
  373. return storage.CreateDiskFromSnapshot(ctx, disk, input)
  374. }
  375. func (s *SBaseStorage) createDiskFromExistingPath(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
  376. var storage = input.Storage
  377. if len(input.DiskInfo.ExistingPath) == 0 {
  378. return nil, httperrors.NewMissingParameterError("existing_path")
  379. }
  380. if !strings.HasPrefix(input.DiskInfo.ExistingPath, storage.GetPath()) {
  381. return nil, errors.Errorf("disk %s not in storage %s", input.DiskInfo.ExistingPath, storage.GetPath())
  382. }
  383. err := storage.CreateDiskFromExistingPath(ctx, disk, input)
  384. if err != nil {
  385. return nil, errors.Wrapf(err, "CreateDiskFromExistingPath")
  386. }
  387. return disk.GetDiskDesc(), nil
  388. }
  389. func (s *SBaseStorage) createDiskFromBackup(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
  390. var storage = input.Storage
  391. if input.DiskInfo.Backup == nil {
  392. return nil, httperrors.NewMissingParameterError("Backup")
  393. }
  394. err := storage.CreateDiskFromBackup(ctx, disk, input)
  395. if err != nil {
  396. return nil, errors.Wrapf(err, "CreateDiskFromBackup")
  397. }
  398. return disk.GetDiskDesc(), nil
  399. }
  400. func (s *SBaseStorage) DestinationPrepareMigrate(
  401. ctx context.Context, liveMigrate bool, disksUri string, snapshotsUri string,
  402. disksBackingFile, diskSnapsChain, outChainSnaps jsonutils.JSONObject, rebaseDisks bool, diskinfo *desc.SGuestDisk, serverId string, idx, totalDiskCount int, encInfo *apis.SEncryptInfo, sysDiskHasTemplate bool,
  403. ) error {
  404. return nil
  405. }
  406. func (s *SBaseStorage) GetCloneTargetDiskPath(ctx context.Context, targetDiskId string) string {
  407. return ""
  408. }
  409. func (s *SBaseStorage) CloneDiskFromStorage(
  410. ctx context.Context, srcStorage IStorage, srcDisk IDisk, targetDiskId string, fullCopy bool, encInfo apis.SEncryptInfo,
  411. ) (*hostapi.ServerCloneDiskFromStorageResponse, error) {
  412. return nil, httperrors.ErrNotImplemented
  413. }
  414. func (s *SBaseStorage) GetBackupDir() string {
  415. return path.Join(s.Path, _BACKUP_PATH_)
  416. }
  417. func (s *SBaseStorage) CreateDiskFromBackup(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error {
  418. info := input.DiskInfo
  419. backupPath := path.Join(s.GetBackupDir(), info.Backup.BackupId)
  420. img, err := qemuimg.NewQemuImage(backupPath)
  421. if err != nil {
  422. log.Errorf("unable to new qemu image for %s: %s", backupPath, err.Error())
  423. return errors.Wrapf(err, "unable to new qemu image for %s", backupPath)
  424. }
  425. _, err = img.Clone(disk.GetPath(), qemuimgfmt.QCOW2, false)
  426. return err
  427. }
  428. func (s *SBaseStorage) DiskMigrate(context.Context, interface{}) (jsonutils.JSONObject, error) {
  429. return nil, httperrors.ErrNotImplemented
  430. }
  431. func (s *SBaseStorage) onSaveToGlanceFailed(ctx context.Context, imageId string, reason string) {
  432. params := jsonutils.NewDict()
  433. params.Set("status", jsonutils.NewString("killed"))
  434. params.Set("reason", jsonutils.NewString(reason))
  435. _, err := image.Images.PerformAction(
  436. hostutils.GetImageSession(ctx),
  437. imageId, "update-status", params,
  438. )
  439. if err != nil {
  440. log.Errorln(err)
  441. }
  442. }
  443. func releaseInfoToParams(relInfo *deployapi.ReleaseInfo, params *jsonutils.JSONDict) {
  444. if relInfo != nil {
  445. params.Set("os_distribution", jsonutils.NewString(relInfo.Distro))
  446. if len(relInfo.Version) > 0 {
  447. params.Set("os_version", jsonutils.NewString(relInfo.Version))
  448. }
  449. if len(relInfo.Arch) > 0 {
  450. params.Set("os_arch", jsonutils.NewString(relInfo.Arch))
  451. }
  452. if len(relInfo.Version) > 0 {
  453. params.Set("os_language", jsonutils.NewString(relInfo.Language))
  454. }
  455. if len(relInfo.CurrentVersion) > 0 {
  456. params.Set("os_current_version", jsonutils.NewString(relInfo.CurrentVersion))
  457. }
  458. }
  459. }
  460. func requestDeleteSnapshot(
  461. storage IStorage, diskId, snapshotPath, deleteSnapshot, convertSnapshotPath,
  462. outfile string, pendingDelete bool,
  463. ) {
  464. deleteSnapshotPath := path.Join(snapshotPath, deleteSnapshot)
  465. DELETEING_SNAPSHOTS.Store(diskId, true)
  466. defer DELETEING_SNAPSHOTS.Delete(diskId)
  467. _, err := modules.Snapshots.PerformAction(hostutils.GetComputeSession(context.Background()),
  468. deleteSnapshot, "deleted", nil)
  469. if err != nil {
  470. log.Errorln(err)
  471. return
  472. }
  473. if out, err := procutils.NewCommand("rm", "-f", convertSnapshotPath).Output(); err != nil {
  474. log.Errorf("%s", out)
  475. return
  476. }
  477. if out, err := procutils.NewCommand("mv", "-f", outfile, convertSnapshotPath).Output(); err != nil {
  478. log.Errorf("%s", out)
  479. return
  480. }
  481. if !pendingDelete {
  482. if err := storage.DeleteDiskfile(deleteSnapshotPath, false); err != nil {
  483. log.Errorln(err)
  484. return
  485. }
  486. }
  487. }