yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/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 azure 16 17 import ( 18 "context" 19 "fmt" 20 "net/url" 21 "strconv" 22 "strings" 23 "time" 24 25 "yunion.io/x/jsonutils" 26 "yunion.io/x/log" 27 "yunion.io/x/pkg/errors" 28 29 billing_api "yunion.io/x/cloudmux/pkg/apis/billing" 30 api "yunion.io/x/cloudmux/pkg/apis/compute" 31 "yunion.io/x/cloudmux/pkg/cloudprovider" 32 "yunion.io/x/cloudmux/pkg/multicloud" 33 ) 34 35 type DiskSku struct { 36 Name string `json:"name,omitempty"` 37 Tier string `json:"tier,omitempty"` 38 } 39 40 type ImageDiskReference struct { 41 ID string 42 Lun int32 `json:"lun,omitempty"` 43 } 44 45 type CreationData struct { 46 CreateOption string `json:"createOption,omitempty"` 47 StorageAccountID string 48 // ImageReference *ImageDiskReference `json:"imageReference,omitempty"` 49 ImageReference *ImageReference `json:"imageReference,omitempty"` 50 SourceURI string `json:"sourceUri,omitempty"` 51 SourceResourceID string `json:"sourceResourceId,omitempty"` 52 } 53 54 type TAzureInt32 string 55 56 func (ai TAzureInt32) Int32() int32 { 57 num, _ := strconv.Atoi(strings.Trim(string(ai), "\t")) 58 return int32(num) 59 } 60 61 type DiskProperties struct { 62 TimeCreated time.Time `json:"timeCreated,omitempty"` 63 OsType string `json:"osType,omitempty"` 64 CreationData CreationData `json:"creationData,omitempty"` 65 DiskSizeGB TAzureInt32 `json:"diskSizeGB,omitempty"` 66 ProvisioningState string `json:"provisioningState,omitempty"` 67 DiskState string `json:"diskState,omitempty"` 68 } 69 70 type SDisk struct { 71 storage *SStorage 72 multicloud.SDisk 73 AzureTags 74 75 ManagedBy string `json:"managedBy,omitempty"` 76 Sku DiskSku `json:"sku,omitempty"` 77 Zones []string `json:"zones,omitempty"` 78 ID string `json:"id,omitempty"` 79 Name string `json:"name,omitempty"` 80 Type string `json:"type,omitempty"` 81 Location string `json:"location,omitempty"` 82 Properties DiskProperties `json:"properties,omitempty"` 83 } 84 85 func (self *SRegion) CreateDisk(storageType string, name string, sizeGb int32, imageId, snapshotId, resourceGroup string) (*SDisk, error) { 86 params := jsonutils.Marshal(map[string]interface{}{ 87 "Name": name, 88 "Location": self.Name, 89 "Sku": map[string]string{ 90 "Name": storageType, 91 }, 92 "Type": "Microsoft.Compute/disks", 93 }).(*jsonutils.JSONDict) 94 properties := map[string]interface{}{ 95 "CreationData": map[string]string{ 96 "CreateOption": "Empty", 97 }, 98 "DiskSizeGB": sizeGb, 99 } 100 if len(imageId) > 0 { 101 image, err := self.GetImageById(imageId) 102 if err != nil { 103 return nil, errors.Wrapf(err, "GetImageById(%s)", imageId) 104 } 105 // 通过镜像创建的磁盘只能传ID参数,不能通过sku,offer等参数创建. 106 imageId, err := self.getOfferedImageId(&image) 107 if err != nil { 108 return nil, errors.Wrapf(err, "getOfferedImageId") 109 } 110 properties = map[string]interface{}{ 111 "CreationData": map[string]interface{}{ 112 "CreateOption": "FromImage", 113 "ImageReference": map[string]string{ 114 "Id": imageId, 115 }, 116 }, 117 } 118 } else if len(snapshotId) > 0 { 119 properties = map[string]interface{}{ 120 "CreationData": map[string]interface{}{ 121 "CreateOption": "Copy", 122 "sourceResourceId": snapshotId, 123 }, 124 } 125 } 126 params.Add(jsonutils.Marshal(properties), "Properties") 127 disk := &SDisk{} 128 return disk, self.create(resourceGroup, params, disk) 129 } 130 131 func (self *SRegion) DeleteDisk(diskId string) error { 132 return cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) { 133 err := self.del(diskId) 134 if err == nil { 135 return true, nil 136 } 137 // Disk vdisk_stress-testvm-azure-1-1_1555940308395625000 is attached to VM /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Compute/virtualMachines/stress-testvm-azure-1. 138 // 更换系统盘后,数据未刷新会出现如上错误,多尝试几次即可 139 if strings.Contains(err.Error(), "is attached to VM") { 140 return false, nil 141 } 142 return false, err 143 }) 144 } 145 146 func (self *SRegion) ResizeDisk(diskId string, sizeGb int32) error { 147 disk, err := self.GetDisk(diskId) 148 if err != nil { 149 return err 150 } 151 disk.Properties.DiskSizeGB = TAzureInt32(fmt.Sprintf("%d", sizeGb)) 152 disk.Properties.ProvisioningState = "" 153 return self.update(jsonutils.Marshal(disk), nil) 154 } 155 156 func (self *SRegion) GetDisk(diskId string) (*SDisk, error) { 157 disk := SDisk{} 158 return &disk, self.get(diskId, url.Values{}, &disk) 159 } 160 161 func (self *SRegion) GetDisks() ([]SDisk, error) { 162 disks := []SDisk{} 163 err := self.list("Microsoft.Compute/disks", url.Values{}, &disks) 164 if err != nil { 165 return nil, err 166 } 167 return disks, nil 168 } 169 170 func (self *SDisk) GetTags() (map[string]string, error) { 171 return self.Tags, nil 172 } 173 174 func (self *SDisk) GetStatus() string { 175 // 为了不统计这种磁盘挂载率, 单独设置一个状态 176 if self.Properties.DiskState == "ActiveSAS" { 177 return self.Properties.DiskState 178 } 179 status := self.Properties.ProvisioningState 180 switch status { 181 case "Updating": 182 return api.DISK_ALLOCATING 183 case "Succeeded": 184 return api.DISK_READY 185 default: 186 log.Errorf("Unknow azure disk %s status: %s", self.ID, status) 187 return api.DISK_UNKNOWN 188 } 189 } 190 191 func (self *SDisk) GetId() string { 192 return self.ID 193 } 194 195 func (self *SDisk) Refresh() error { 196 disk, err := self.storage.zone.region.GetDisk(self.ID) 197 if err != nil { 198 return errors.Wrapf(err, "GetDisk(%s)", self.ID) 199 } 200 return jsonutils.Update(self, disk) 201 } 202 203 func (self *SDisk) Delete(ctx context.Context) error { 204 return self.storage.zone.region.DeleteDisk(self.ID) 205 } 206 207 func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error { 208 return self.storage.zone.region.ResizeDisk(self.ID, int32(sizeMb/1024)) 209 } 210 211 func (self *SDisk) GetName() string { 212 if len(self.Name) > 0 { 213 return self.Name 214 } 215 return self.ID 216 } 217 218 func (self *SDisk) GetGlobalId() string { 219 return strings.ToLower(self.ID) 220 } 221 222 func (self *SDisk) IsEmulated() bool { 223 return false 224 } 225 226 func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { 227 return self.storage, nil 228 } 229 230 func (self *SDisk) GetFsFormat() string { 231 return "" 232 } 233 234 func (self *SDisk) GetIsNonPersistent() bool { 235 return false 236 } 237 238 func (self *SDisk) GetDriver() string { 239 return "scsi" 240 } 241 242 func (self *SDisk) GetCacheMode() string { 243 return "none" 244 } 245 246 func (self *SDisk) GetMountpoint() string { 247 return "" 248 } 249 250 func (self *SDisk) GetDiskFormat() string { 251 return "vhd" 252 } 253 254 func (self *SDisk) GetDiskSizeMB() int { 255 return int(self.Properties.DiskSizeGB.Int32()) * 1024 256 } 257 258 func (self *SDisk) GetIsAutoDelete() bool { 259 return false 260 } 261 262 func (self *SDisk) GetTemplateId() string { 263 if self.Properties.CreationData.ImageReference != nil { 264 return self.Properties.CreationData.ImageReference.ID 265 } 266 return "" 267 } 268 269 func (self *SDisk) GetDiskType() string { 270 if len(self.Properties.OsType) > 0 { 271 return api.DISK_TYPE_SYS 272 } 273 return api.DISK_TYPE_DATA 274 } 275 276 func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { 277 snapshot, err := self.storage.zone.region.CreateSnapshot(self.ID, name, desc) 278 if err != nil { 279 return nil, errors.Wrapf(err, "CreateSnapshot") 280 } 281 return snapshot, nil 282 } 283 284 func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { 285 snapshots, err := self.storage.zone.region.ListSnapshots() 286 if err != nil { 287 return nil, errors.Wrapf(err, "ListSnapshots") 288 } 289 ret := []cloudprovider.ICloudSnapshot{} 290 for i := range snapshots { 291 if strings.ToLower(snapshots[i].Properties.CreationData.SourceResourceID) == strings.ToLower(self.ID) { 292 snapshots[i].region = self.storage.zone.region 293 ret = append(ret, &snapshots[i]) 294 } 295 } 296 return ret, nil 297 } 298 299 func (self *SDisk) GetBillingType() string { 300 return billing_api.BILLING_TYPE_POSTPAID 301 } 302 303 func (self *SDisk) GetCreatedAt() time.Time { 304 return self.Properties.TimeCreated 305 } 306 307 func (self *SDisk) GetExpiredAt() time.Time { 308 return time.Time{} 309 } 310 311 func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { 312 if self.Properties.DiskState != "Unattached" { 313 return "", fmt.Errorf("Azure reset disk needs to be done in the Unattached state, current status: %s", self.Properties.DiskState) 314 } 315 disk, err := self.storage.zone.region.CreateDisk(self.Sku.Name, self.Name, 0, "", snapshotId, self.GetProjectId()) 316 if err != nil { 317 return "", errors.Wrap(err, "CreateDisk") 318 } 319 err = self.storage.zone.region.DeleteDisk(self.ID) 320 if err != nil { 321 log.Warningf("delete old disk %s error: %v", self.ID, err) 322 } 323 return disk.ID, nil 324 } 325 326 func (disk *SDisk) GetAccessPath() string { 327 return "" 328 } 329 330 func (self *SDisk) Rebuild(ctx context.Context) error { 331 // TODO 332 return cloudprovider.ErrNotSupported 333 } 334 335 func (self *SDisk) GetProjectId() string { 336 return getResourceGroup(self.ID) 337 }