| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package azure
- import (
- "context"
- "fmt"
- "net/url"
- "strconv"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/cloudmux/pkg/multicloud"
- )
- type DiskSku struct {
- Name string `json:"name,omitempty"`
- Tier string `json:"tier,omitempty"`
- }
- type ImageDiskReference struct {
- ID string
- Lun int32 `json:"lun,omitempty"`
- }
- type CreationData struct {
- CreateOption string `json:"createOption,omitempty"`
- StorageAccountID string
- // ImageReference *ImageDiskReference `json:"imageReference,omitempty"`
- ImageReference *ImageReference `json:"imageReference,omitempty"`
- SourceURI string `json:"sourceUri,omitempty"`
- SourceResourceID string `json:"sourceResourceId,omitempty"`
- }
- type TAzureInt32 string
- func (ai TAzureInt32) Int32() int32 {
- num, _ := strconv.Atoi(strings.Trim(string(ai), "\t"))
- return int32(num)
- }
- type DiskProperties struct {
- TimeCreated time.Time `json:"timeCreated,omitempty"`
- OsType string `json:"osType,omitempty"`
- CreationData CreationData `json:"creationData,omitempty"`
- DiskSizeGB TAzureInt32 `json:"diskSizeGB,omitempty"`
- ProvisioningState string `json:"provisioningState,omitempty"`
- DiskState string `json:"diskState,omitempty"`
- }
- type SDisk struct {
- storage *SStorage
- multicloud.SDisk
- AzureTags
- ManagedBy string `json:"managedBy,omitempty"`
- Sku DiskSku `json:"sku,omitempty"`
- Zones []string `json:"zones,omitempty"`
- ID string `json:"id,omitempty"`
- Name string `json:"name,omitempty"`
- Type string `json:"type,omitempty"`
- Location string `json:"location,omitempty"`
- Properties DiskProperties `json:"properties,omitempty"`
- }
- func (self *SRegion) CreateDisk(storageType string, opts *cloudprovider.DiskCreateConfig) (*SDisk, error) {
- params := jsonutils.Marshal(map[string]interface{}{
- "Name": opts.Name,
- "Location": self.Name,
- "Sku": map[string]string{
- "Name": storageType,
- },
- "Type": "Microsoft.Compute/disks",
- }).(*jsonutils.JSONDict)
- properties := map[string]interface{}{
- "CreationData": map[string]string{
- "CreateOption": "Empty",
- },
- "DiskSizeGB": opts.SizeGb,
- }
- if len(opts.ImageId) > 0 {
- image, err := self.GetImageById(opts.ImageId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetImageById(%s)", opts.ImageId)
- }
- // 通过镜像创建的磁盘只能传ID参数,不能通过sku,offer等参数创建.
- imageId, err := self.getOfferedImageId(&image)
- if err != nil {
- return nil, errors.Wrapf(err, "getOfferedImageId")
- }
- properties = map[string]interface{}{
- "CreationData": map[string]interface{}{
- "CreateOption": "FromImage",
- "ImageReference": map[string]string{
- "Id": imageId,
- },
- },
- }
- } else if len(opts.SnapshotId) > 0 {
- properties = map[string]interface{}{
- "CreationData": map[string]interface{}{
- "CreateOption": "Copy",
- "sourceResourceId": opts.SnapshotId,
- },
- }
- }
- params.Add(jsonutils.Marshal(properties), "Properties")
- disk := &SDisk{}
- return disk, self.create(opts.ProjectId, params, disk)
- }
- func (self *SRegion) DeleteDisk(diskId string) error {
- return cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) {
- err := self.del(diskId)
- if err == nil {
- return true, nil
- }
- // 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.
- // 更换系统盘后,数据未刷新会出现如上错误,多尝试几次即可
- if strings.Contains(err.Error(), "is attached to VM") {
- return false, nil
- }
- return false, err
- })
- }
- func (self *SRegion) ResizeDisk(diskId string, sizeGb int32) error {
- disk, err := self.GetDisk(diskId)
- if err != nil {
- return err
- }
- disk.Properties.DiskSizeGB = TAzureInt32(fmt.Sprintf("%d", sizeGb))
- disk.Properties.ProvisioningState = ""
- return self.update(jsonutils.Marshal(disk), nil)
- }
- func (self *SRegion) GetDisk(diskId string) (*SDisk, error) {
- disk := SDisk{}
- return &disk, self.get(diskId, url.Values{}, &disk)
- }
- func (self *SRegion) GetDisks() ([]SDisk, error) {
- disks := []SDisk{}
- err := self.list("Microsoft.Compute/disks", url.Values{}, &disks)
- if err != nil {
- return nil, err
- }
- return disks, nil
- }
- func (self *SDisk) GetTags() (map[string]string, error) {
- return self.Tags, nil
- }
- func (self *SDisk) GetStatus() string {
- // 为了不统计这种磁盘挂载率, 单独设置一个状态
- if self.Properties.DiskState == "ActiveSAS" {
- return self.Properties.DiskState
- }
- status := self.Properties.ProvisioningState
- switch status {
- case "Updating":
- return api.DISK_ALLOCATING
- case "Succeeded":
- return api.DISK_READY
- default:
- log.Errorf("Unknow azure disk %s status: %s", self.ID, status)
- return api.DISK_UNKNOWN
- }
- }
- func (self *SDisk) GetId() string {
- return self.ID
- }
- func (self *SDisk) Refresh() error {
- disk, err := self.storage.zone.region.GetDisk(self.ID)
- if err != nil {
- return errors.Wrapf(err, "GetDisk(%s)", self.ID)
- }
- return jsonutils.Update(self, disk)
- }
- func (self *SDisk) Delete(ctx context.Context) error {
- return self.storage.zone.region.DeleteDisk(self.ID)
- }
- func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error {
- return self.storage.zone.region.ResizeDisk(self.ID, int32(sizeMb/1024))
- }
- func (self *SDisk) GetName() string {
- if len(self.Name) > 0 {
- return self.Name
- }
- return self.ID
- }
- func (self *SDisk) GetGlobalId() string {
- return strings.ToLower(self.ID)
- }
- func (self *SDisk) IsEmulated() bool {
- return false
- }
- func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
- return self.storage, nil
- }
- func (self *SDisk) GetFsFormat() string {
- return ""
- }
- func (self *SDisk) GetIsNonPersistent() bool {
- return false
- }
- func (self *SDisk) GetDriver() string {
- return "scsi"
- }
- func (self *SDisk) GetCacheMode() string {
- return "none"
- }
- func (self *SDisk) GetMountpoint() string {
- return ""
- }
- func (self *SDisk) GetDiskFormat() string {
- return "vhd"
- }
- func (self *SDisk) GetDiskSizeMB() int {
- return int(self.Properties.DiskSizeGB.Int32()) * 1024
- }
- func (self *SDisk) GetIsAutoDelete() bool {
- return false
- }
- func (self *SDisk) GetTemplateId() string {
- if self.Properties.CreationData.ImageReference != nil {
- return self.Properties.CreationData.ImageReference.ID
- }
- return ""
- }
- func (self *SDisk) GetDiskType() string {
- if len(self.Properties.OsType) > 0 {
- return api.DISK_TYPE_SYS
- }
- return api.DISK_TYPE_DATA
- }
- func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) {
- snapshot, err := self.storage.zone.region.CreateSnapshot(self.ID, name, desc)
- if err != nil {
- return nil, errors.Wrapf(err, "CreateSnapshot")
- }
- return snapshot, nil
- }
- func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
- snapshots, err := self.storage.zone.region.ListSnapshots()
- if err != nil {
- return nil, errors.Wrapf(err, "ListSnapshots")
- }
- ret := []cloudprovider.ICloudSnapshot{}
- for i := range snapshots {
- if strings.ToLower(snapshots[i].Properties.CreationData.SourceResourceID) == strings.ToLower(self.ID) {
- snapshots[i].region = self.storage.zone.region
- ret = append(ret, &snapshots[i])
- }
- }
- return ret, nil
- }
- func (self *SDisk) GetBillingType() string {
- return billing_api.BILLING_TYPE_POSTPAID
- }
- func (self *SDisk) GetCreatedAt() time.Time {
- return self.Properties.TimeCreated
- }
- func (self *SDisk) GetExpiredAt() time.Time {
- return time.Time{}
- }
- func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
- if self.Properties.DiskState != "Unattached" {
- return "", fmt.Errorf("Azure reset disk needs to be done in the Unattached state, current status: %s", self.Properties.DiskState)
- }
- opts := &cloudprovider.DiskCreateConfig{
- Name: self.Name,
- SizeGb: 0,
- SnapshotId: snapshotId,
- ProjectId: self.GetProjectId(),
- }
- disk, err := self.storage.zone.region.CreateDisk(self.Sku.Name, opts)
- if err != nil {
- return "", errors.Wrap(err, "CreateDisk")
- }
- err = self.storage.zone.region.DeleteDisk(self.ID)
- if err != nil {
- log.Warningf("delete old disk %s error: %v", self.ID, err)
- }
- return disk.ID, nil
- }
- func (disk *SDisk) GetAccessPath() string {
- return ""
- }
- func (self *SDisk) Rebuild(ctx context.Context) error {
- // TODO
- return cloudprovider.ErrNotSupported
- }
- func (self *SDisk) GetProjectId() string {
- return getResourceGroup(self.ID)
- }
|