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