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  }