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 }