disk.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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 apsara
  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/utils"
  23. api "yunion.io/x/cloudmux/pkg/apis/compute"
  24. "yunion.io/x/cloudmux/pkg/cloudprovider"
  25. "yunion.io/x/cloudmux/pkg/multicloud"
  26. )
  27. type SMountInstances struct {
  28. MountInstance []string
  29. }
  30. type STags struct {
  31. Tag []string
  32. }
  33. type SDisk struct {
  34. multicloud.SResourceBase
  35. ApsaraTags
  36. storage *SStorage
  37. multicloud.SDisk
  38. AttachedTime time.Time
  39. AutoSnapshotPolicyId string
  40. Category string
  41. PerformanceLevel string
  42. CreationTime time.Time
  43. DeleteAutoSnapshot bool
  44. DeleteWithInstance bool
  45. Description string
  46. DetachedTime time.Time
  47. Device string
  48. DiskChargeType TChargeType
  49. DiskId string
  50. DiskName string
  51. EnableAutoSnapshot bool
  52. EnableAutomatedSnapshotPolicy bool
  53. Encrypted bool
  54. ExpiredTime time.Time
  55. ImageId string
  56. InstanceId string
  57. MountInstances SMountInstances
  58. OperationLocks SOperationLocks
  59. Portable bool
  60. ProductCode string
  61. RegionId string
  62. Size int
  63. SourceSnapshotId string
  64. Status string
  65. Tags STags
  66. Type string
  67. ZoneId string
  68. DepartmentInfo
  69. }
  70. func (self *SRegion) GetDisks(instanceId string, zoneId string, category string, diskIds []string, snapshotpolicyId string) ([]SDisk, error) {
  71. params := make(map[string]string)
  72. params["RegionId"] = self.RegionId
  73. params["PageSize"] = "50"
  74. pageNum := 1
  75. params["PageNumber"] = fmt.Sprintf("%d", pageNum)
  76. if len(instanceId) > 0 {
  77. params["InstanceId"] = instanceId
  78. }
  79. if len(zoneId) > 0 {
  80. params["ZoneId"] = zoneId
  81. }
  82. if len(category) > 0 {
  83. params["Category"] = category
  84. }
  85. if len(diskIds) > 0 {
  86. params["DiskIds"] = jsonutils.Marshal(diskIds).String()
  87. }
  88. if len(snapshotpolicyId) > 0 {
  89. params["AutoSnapshotPolicyId"] = snapshotpolicyId
  90. }
  91. ret := []SDisk{}
  92. for {
  93. body, err := self.ecsRequest("DescribeDisks", params)
  94. if err != nil {
  95. return nil, errors.Wrapf(err, "DescribeDisks")
  96. }
  97. part := make([]SDisk, 0)
  98. err = body.Unmarshal(&part, "Disks", "Disk")
  99. if err != nil {
  100. return nil, errors.Wrapf(err, "Unmarshal")
  101. }
  102. ret = append(ret, part...)
  103. total, _ := body.Int("TotalCount")
  104. if len(ret) >= int(total) || len(part) == 0 {
  105. break
  106. }
  107. pageNum++
  108. params["PageNumber"] = fmt.Sprintf("%d", pageNum)
  109. }
  110. return ret, nil
  111. }
  112. func (self *SDisk) GetId() string {
  113. return self.DiskId
  114. }
  115. func (self *SDisk) Delete(ctx context.Context) error {
  116. _, err := self.storage.zone.region.getDisk(self.DiskId)
  117. if err != nil {
  118. if errors.Cause(err) == cloudprovider.ErrNotFound {
  119. // 未找到disk, 说明disk已经被删除了. 避免回收站中disk-delete循环删除失败
  120. return nil
  121. }
  122. log.Errorf("Failed to find disk %s when delete: %s", self.DiskId, err)
  123. return err
  124. }
  125. for {
  126. err := self.storage.zone.region.DeleteDisk(self.DiskId)
  127. if err != nil {
  128. if isError(err, "IncorrectDiskStatus") {
  129. log.Infof("The disk is initializing, try later ...")
  130. time.Sleep(10 * time.Second)
  131. } else {
  132. log.Errorf("DeleteDisk fail: %s", err)
  133. return err
  134. }
  135. } else {
  136. break
  137. }
  138. }
  139. return cloudprovider.WaitDeleted(self, 10*time.Second, 300*time.Second) // 5minutes
  140. }
  141. func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error {
  142. return self.storage.zone.region.resizeDisk(self.DiskId, sizeMb)
  143. }
  144. func (self *SDisk) GetName() string {
  145. if len(self.DiskName) > 0 {
  146. return self.DiskName
  147. }
  148. return self.DiskId
  149. }
  150. func (self *SDisk) GetGlobalId() string {
  151. return self.DiskId
  152. }
  153. func (self *SDisk) IsEmulated() bool {
  154. return false
  155. }
  156. func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
  157. return self.storage, nil
  158. }
  159. func (self *SDisk) GetStatus() string {
  160. // In_use Available Attaching Detaching Creating ReIniting All
  161. switch self.Status {
  162. case "Creating", "ReIniting":
  163. return api.DISK_ALLOCATING
  164. default:
  165. return api.DISK_READY
  166. }
  167. }
  168. func (self *SDisk) Refresh() error {
  169. new, err := self.storage.zone.region.getDisk(self.DiskId)
  170. if err != nil {
  171. return err
  172. }
  173. return jsonutils.Update(self, new)
  174. }
  175. func (self *SDisk) ResizeDisk(newSize int64) error {
  176. // newSize 单位为 GB. 范围在20 ~2000. 只能往大调。不能调小
  177. // https://help.apsara.com/document_detail/25522.html?spm=a2c4g.11174283.6.897.aHwqkS
  178. return self.storage.zone.region.resizeDisk(self.DiskId, newSize)
  179. }
  180. func (self *SDisk) GetDiskFormat() string {
  181. return "vhd"
  182. }
  183. func (self *SDisk) GetDiskSizeMB() int {
  184. return self.Size * 1024
  185. }
  186. func (self *SDisk) GetIsAutoDelete() bool {
  187. return self.DeleteWithInstance
  188. }
  189. func (self *SDisk) GetTemplateId() string {
  190. return self.ImageId
  191. }
  192. func (self *SDisk) GetDiskType() string {
  193. switch self.Type {
  194. case "system":
  195. return api.DISK_TYPE_SYS
  196. case "data":
  197. return api.DISK_TYPE_DATA
  198. default:
  199. return api.DISK_TYPE_DATA
  200. }
  201. }
  202. func (self *SDisk) GetFsFormat() string {
  203. return ""
  204. }
  205. func (self *SDisk) GetIsNonPersistent() bool {
  206. return false
  207. }
  208. func (self *SDisk) GetDriver() string {
  209. return "scsi"
  210. }
  211. func (self *SDisk) GetCacheMode() string {
  212. return "none"
  213. }
  214. func (self *SDisk) GetMountpoint() string {
  215. return ""
  216. }
  217. func (self *SRegion) CreateDisk(zoneId string, category string, name string, sizeGb int, desc string, projectId string) (string, error) {
  218. params := make(map[string]string)
  219. params["ZoneId"] = zoneId
  220. params["DiskName"] = name
  221. if len(desc) > 0 {
  222. params["Description"] = desc
  223. }
  224. params["Encrypted"] = "false"
  225. params["DiskCategory"] = category
  226. if category == api.STORAGE_CLOUD_ESSD_PL2 {
  227. params["DiskCategory"] = api.STORAGE_CLOUD_ESSD
  228. params["PerformanceLevel"] = "PL2"
  229. }
  230. if category == api.STORAGE_CLOUD_ESSD_PL3 {
  231. params["DiskCategory"] = api.STORAGE_CLOUD_ESSD
  232. params["PerformanceLevel"] = "PL3"
  233. }
  234. if len(projectId) > 0 {
  235. params["ResourceGroupId"] = projectId
  236. }
  237. params["Size"] = fmt.Sprintf("%d", sizeGb)
  238. params["ClientToken"] = utils.GenRequestId(20)
  239. body, err := self.ecsRequest("CreateDisk", params)
  240. if err != nil {
  241. return "", err
  242. }
  243. return body.GetString("DiskId")
  244. }
  245. func (self *SRegion) getDisk(diskId string) (*SDisk, error) {
  246. disks, err := self.GetDisks("", "", "", []string{diskId}, "")
  247. if err != nil {
  248. return nil, err
  249. }
  250. for i := range disks {
  251. if disks[i].DiskId == diskId {
  252. return &disks[i], nil
  253. }
  254. }
  255. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", diskId)
  256. }
  257. func (self *SRegion) DeleteDisk(diskId string) error {
  258. params := make(map[string]string)
  259. params["DiskId"] = diskId
  260. _, err := self.ecsRequest("DeleteDisk", params)
  261. return err
  262. }
  263. func (self *SRegion) resizeDisk(diskId string, sizeMb int64) error {
  264. sizeGb := sizeMb / 1024
  265. params := make(map[string]string)
  266. params["DiskId"] = diskId
  267. params["NewSize"] = fmt.Sprintf("%d", sizeGb)
  268. _, err := self.ecsRequest("ResizeDisk", params)
  269. if err != nil {
  270. log.Errorf("resizing disk (%s) to %d GiB failed: %s", diskId, sizeGb, err)
  271. return err
  272. }
  273. return nil
  274. }
  275. func (self *SRegion) resetDisk(diskId, snapshotId string) error {
  276. params := make(map[string]string)
  277. params["DiskId"] = diskId
  278. params["SnapshotId"] = snapshotId
  279. _, err := self.ecsRequest("ResetDisk", params)
  280. if err != nil {
  281. log.Errorf("ResetDisk %s to snapshot %s fail %s", diskId, snapshotId, err)
  282. return err
  283. }
  284. return nil
  285. }
  286. func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) {
  287. if snapshotId, err := self.storage.zone.region.CreateSnapshot(self.DiskId, name, desc); err != nil {
  288. log.Errorf("createSnapshot fail %s", err)
  289. return nil, err
  290. } else if snapshot, err := self.getSnapshot(snapshotId); err != nil {
  291. return nil, err
  292. } else {
  293. snapshot.region = self.storage.zone.region
  294. if err := cloudprovider.WaitStatus(snapshot, api.SNAPSHOT_READY, 15*time.Second, 3600*time.Second); err != nil {
  295. return nil, err
  296. }
  297. return snapshot, nil
  298. }
  299. }
  300. func (self *SRegion) CreateSnapshot(diskId, name, desc string) (string, error) {
  301. params := make(map[string]string)
  302. params["RegionId"] = self.RegionId
  303. params["DiskId"] = diskId
  304. params["SnapshotName"] = name
  305. params["Description"] = desc
  306. if body, err := self.ecsRequest("CreateSnapshot", params); err != nil {
  307. log.Errorf("CreateSnapshot fail %s", err)
  308. return "", err
  309. } else {
  310. return body.GetString("SnapshotId")
  311. }
  312. }
  313. func (self *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
  314. if snapshot, err := self.getSnapshot(snapshotId); err != nil {
  315. return nil, err
  316. } else {
  317. snapshot.region = self.storage.zone.region
  318. return snapshot, nil
  319. }
  320. }
  321. func (self *SDisk) getSnapshot(snapshotId string) (*SSnapshot, error) {
  322. if snapshots, total, err := self.storage.zone.region.GetSnapshots("", "", "", []string{snapshotId}, 0, 1); err != nil {
  323. return nil, err
  324. } else if total != 1 {
  325. return nil, cloudprovider.ErrNotFound
  326. } else {
  327. return &snapshots[0], nil
  328. }
  329. }
  330. func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
  331. snapshots := make([]SSnapshot, 0)
  332. for {
  333. parts, total, err := self.storage.zone.region.GetSnapshots("", self.DiskId, "", []string{}, len(snapshots), 20)
  334. if err != nil {
  335. return nil, errors.Wrapf(err, "GetSnapshots")
  336. }
  337. snapshots = append(snapshots, parts...)
  338. if len(snapshots) >= total {
  339. break
  340. }
  341. }
  342. isnapshots := make([]cloudprovider.ICloudSnapshot, len(snapshots))
  343. for i := 0; i < len(snapshots); i++ {
  344. snapshots[i].region = self.storage.zone.region
  345. isnapshots[i] = &snapshots[i]
  346. }
  347. return isnapshots, nil
  348. }
  349. func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
  350. return "", self.storage.zone.region.resetDisk(self.DiskId, snapshotId)
  351. }
  352. func (self *SDisk) GetBillingType() string {
  353. return convertChargeType(self.DiskChargeType)
  354. }
  355. func (self *SDisk) GetCreatedAt() time.Time {
  356. return self.CreationTime
  357. }
  358. func (self *SDisk) GetAccessPath() string {
  359. return ""
  360. }
  361. func (self *SDisk) Rebuild(ctx context.Context) error {
  362. err := self.storage.zone.region.rebuildDisk(self.DiskId)
  363. if err != nil {
  364. if isError(err, "IncorrectInstanceStatus") {
  365. return nil
  366. }
  367. log.Errorf("rebuild disk fail %s", err)
  368. return err
  369. }
  370. return nil
  371. }
  372. func (self *SRegion) rebuildDisk(diskId string) error {
  373. params := make(map[string]string)
  374. params["DiskId"] = diskId
  375. _, err := self.ecsRequest("ReInitDisk", params)
  376. if err != nil {
  377. log.Errorf("ReInitDisk %s fail %s", diskId, err)
  378. return err
  379. }
  380. return nil
  381. }