yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/storage.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 openstack
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/errors"
    26  	"yunion.io/x/pkg/utils"
    27  
    28  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    29  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    30  	"yunion.io/x/cloudmux/pkg/multicloud"
    31  )
    32  
    33  const (
    34  	DEFAULT_STORAGE_TYPE = "scheduler"
    35  )
    36  
    37  type SExtraSpecs struct {
    38  	VolumeBackendName string
    39  }
    40  
    41  type SStorage struct {
    42  	multicloud.SStorageBase
    43  	zone       *SZone
    44  	Name       string
    45  	ExtraSpecs SExtraSpecs
    46  	ID         string
    47  }
    48  
    49  func (storage *SStorage) GetId() string {
    50  	return storage.ID
    51  }
    52  
    53  func (storage *SStorage) GetName() string {
    54  	return storage.Name
    55  }
    56  
    57  func (storage *SStorage) GetGlobalId() string {
    58  	return fmt.Sprintf("%s-%s", storage.zone.GetGlobalId(), storage.ID)
    59  }
    60  
    61  func (storage *SStorage) IsEmulated() bool {
    62  	return false
    63  }
    64  
    65  func (storage *SStorage) GetIZone() cloudprovider.ICloudZone {
    66  	return storage.zone
    67  }
    68  
    69  func (storage *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
    70  	disks, err := storage.zone.region.GetDisks()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	idisks := []cloudprovider.ICloudDisk{}
    75  	for i := 0; i < len(disks); i++ {
    76  		if disks[i].AvailabilityZone == storage.zone.ZoneName && (disks[i].VolumeType == storage.Name || strings.HasSuffix(disks[i].Host, "#"+storage.ExtraSpecs.VolumeBackendName)) {
    77  			disks[i].storage = storage
    78  			idisks = append(idisks, &disks[i])
    79  		}
    80  	}
    81  	return idisks, nil
    82  }
    83  
    84  func (storage *SStorage) GetStorageType() string {
    85  	if len(storage.ExtraSpecs.VolumeBackendName) == 0 {
    86  		return DEFAULT_STORAGE_TYPE
    87  	}
    88  	return storage.ExtraSpecs.VolumeBackendName
    89  }
    90  
    91  func (storage *SStorage) GetMediumType() string {
    92  	if strings.Contains(storage.Name, "SSD") {
    93  		return api.DISK_TYPE_SSD
    94  	}
    95  	return api.DISK_TYPE_ROTATE
    96  }
    97  
    98  func (storage *SStorage) GetCapacityMB() int64 {
    99  	return 0 // unlimited
   100  }
   101  
   102  func (storage *SStorage) GetCapacityUsedMB() int64 {
   103  	return 0
   104  }
   105  
   106  func (storage *SStorage) GetStorageConf() jsonutils.JSONObject {
   107  	conf := jsonutils.NewDict()
   108  	return conf
   109  }
   110  
   111  func (storage *SStorage) GetStatus() string {
   112  	ok, err := storage.zone.region.IsStorageAvailable(storage.GetStorageType())
   113  	if err != nil || !ok {
   114  		return api.STORAGE_OFFLINE
   115  	}
   116  	return api.STORAGE_ONLINE
   117  }
   118  
   119  func (storage *SStorage) Refresh() error {
   120  	// do nothing
   121  	return nil
   122  }
   123  
   124  func (storage *SStorage) GetEnabled() bool {
   125  	return true
   126  }
   127  
   128  func (storage *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
   129  	return storage.zone.region.getStoragecache()
   130  }
   131  
   132  func (storage *SStorage) CreateIDisk(conf *cloudprovider.DiskCreateConfig) (cloudprovider.ICloudDisk, error) {
   133  	disk, err := storage.zone.region.CreateDisk("", storage.Name, conf.Name, conf.SizeGb, conf.Desc, conf.ProjectId)
   134  	if err != nil {
   135  		log.Errorf("createDisk fail %v", err)
   136  		return nil, err
   137  	}
   138  	disk.storage = storage
   139  	return disk, cloudprovider.WaitStatus(disk, api.DISK_READY, time.Second*5, time.Minute*5)
   140  }
   141  
   142  func (storage *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) {
   143  	disk, err := storage.zone.region.GetDisk(idStr)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	if disk.AvailabilityZone != storage.zone.ZoneName {
   148  		return nil, errors.Wrapf(cloudprovider.ErrNotFound, "disk %s not in zone %s", disk.Name, storage.zone.ZoneName)
   149  	}
   150  	disk.storage = storage
   151  	return disk, nil
   152  }
   153  
   154  func (storage *SStorage) GetMountPoint() string {
   155  	return ""
   156  }
   157  
   158  func (storage *SStorage) IsSysDiskStore() bool {
   159  	return true
   160  }
   161  
   162  func (region *SRegion) GetStorageTypes() ([]SStorage, error) {
   163  	resource := "/types"
   164  	storages := []SStorage{}
   165  	query := url.Values{}
   166  	for {
   167  		resp, err := region.bsList(resource, query)
   168  		if err != nil {
   169  			return nil, errors.Wrap(err, "bsReqest")
   170  		}
   171  		part := struct {
   172  			VolumeTypes     []SStorage
   173  			VolumeTypeLinks SNextLinks
   174  		}{}
   175  		err = resp.Unmarshal(&part)
   176  		if err != nil {
   177  			return nil, errors.Wrap(err, "resp.Unmarshal")
   178  		}
   179  		storages = append(storages, part.VolumeTypes...)
   180  		marker := part.VolumeTypeLinks.GetNextMark()
   181  		if len(marker) == 0 {
   182  			break
   183  		}
   184  		query.Set("marker", marker)
   185  	}
   186  	return storages, nil
   187  }
   188  
   189  type SCinderService struct {
   190  	ActiveBackendId string
   191  	// cinder-volume
   192  	Binary            string
   193  	DisabledReason    string
   194  	Frozen            string
   195  	Host              string
   196  	ReplicationStatus string
   197  	State             string
   198  	Status            string
   199  	UpdatedAt         time.Time
   200  	Zone              string
   201  }
   202  
   203  func (region *SRegion) GetCinderServices() ([]SCinderService, error) {
   204  	resp, err := region.bsList("/os-services", nil)
   205  	if err != nil {
   206  		return nil, errors.Wrap(err, "bsList")
   207  	}
   208  	services := []SCinderService{}
   209  	err = resp.Unmarshal(&services, "services")
   210  	if err != nil {
   211  		return nil, errors.Wrap(err, "resp.Unmarshal")
   212  	}
   213  	return services, nil
   214  }
   215  
   216  func (region *SRegion) IsStorageAvailable(storageType string) (bool, error) {
   217  	if utils.IsInStringArray(storageType, []string{DEFAULT_STORAGE_TYPE, api.STORAGE_OPENSTACK_NOVA}) {
   218  		return true, nil
   219  	}
   220  	services, err := region.GetCinderServices()
   221  	if err != nil {
   222  		return false, errors.Wrap(err, "GetCinderServices")
   223  	}
   224  	for _, service := range services {
   225  		if service.Binary == "cinder-volume" && strings.Contains(service.Host, "@") {
   226  			hostInfo := strings.Split(service.Host, "@")
   227  			if hostInfo[len(hostInfo)-1] == storageType {
   228  				if service.State == "up" && service.Status == "enabled" {
   229  					return true, nil
   230  				}
   231  			}
   232  		}
   233  	}
   234  	log.Errorf("storage %s offline", storageType)
   235  	return false, nil
   236  }
   237  
   238  type SCapabilities struct {
   239  }
   240  
   241  type SPool struct {
   242  	Name         string
   243  	Capabilities SCapabilities
   244  }
   245  
   246  func (region *SRegion) GetSchedulerStatsPool() ([]SPool, error) {
   247  	resp, err := region.bsList("/scheduler-stats/get_pools", nil)
   248  	if err != nil {
   249  		return nil, errors.Wrap(err, "bsList")
   250  	}
   251  	pools := []SPool{}
   252  	err = resp.Unmarshal(&pools, "pools")
   253  	if err != nil {
   254  		return nil, errors.Wrap(err, "resp.Unmarshal")
   255  	}
   256  	return pools, nil
   257  }