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