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  }