yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/zstack/host.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 zstack 16 17 import ( 18 "fmt" 19 "net/url" 20 "strings" 21 22 "github.com/pkg/errors" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 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 SHost struct { 33 multicloud.SHostBase 34 zone *SZone 35 36 ZStackBasic 37 Username string `json:"username"` 38 SSHPort int `json:"sshPort"` 39 ZoneUUID string `json:"zoneUuid"` 40 ClusterUUID string `json:"clusterUuid"` 41 ManagementIP string `json:"managementIp"` 42 HypervisorType string `json:"hypervisorType"` 43 State string `json:"state"` 44 Status string `json:"status"` 45 TotalCPUCapacity int `json:"totalCpuCapacity"` 46 AvailableCPUCapacity int `json:"availableCpuCapacity"` 47 CPUSockets int `json:"cpuSockets"` 48 TotalMemoryCapacity int `json:"totalMemoryCapacity"` 49 AvailableMemoryCapacity int `json:"availableMemoryCapacity"` 50 CPUNum int `json:"cpuNum"` 51 ZStackTime 52 } 53 54 func (region *SRegion) GetHosts(zoneId string, hostId string) ([]SHost, error) { 55 hosts := []SHost{} 56 params := url.Values{} 57 if len(zoneId) > 0 { 58 params.Add("q", "zone.uuid="+zoneId) 59 } 60 if len(hostId) > 0 { 61 params.Add("q", "uuid="+hostId) 62 } 63 if SkipEsxi { 64 params.Add("q", "hypervisorType!=ESX") 65 } 66 return hosts, region.client.listAll("hosts", params, &hosts) 67 } 68 69 func (region *SRegion) GetHost(hostId string) (*SHost, error) { 70 host := &SHost{} 71 err := region.client.getResource("hosts", hostId, host) 72 if err != nil { 73 return nil, err 74 } 75 zone, err := region.GetZone(host.ZoneUUID) 76 if err != nil { 77 return nil, err 78 } 79 host.zone = zone 80 return host, nil 81 } 82 83 func (host *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { 84 wires, err := host.zone.region.GetWires(host.ZoneUUID, "", host.ClusterUUID) 85 if err != nil { 86 return nil, err 87 } 88 iwires := []cloudprovider.ICloudWire{} 89 for i := 0; i < len(wires); i++ { 90 iwires = append(iwires, &wires[i]) 91 } 92 return iwires, nil 93 } 94 95 func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { 96 storages, err := host.zone.region.GetStorages(host.zone.UUID, host.ClusterUUID, "") 97 if err != nil { 98 return nil, err 99 } 100 istorages := []cloudprovider.ICloudStorage{} 101 for i := 0; i < len(storages); i++ { 102 storages[i].region = host.zone.region 103 switch storages[i].Type { 104 case StorageTypeLocal: 105 localStorages, err := host.zone.region.getILocalStorages(storages[i].UUID, host.UUID) 106 if err != nil { 107 return nil, err 108 } 109 istorages = append(istorages, localStorages...) 110 case StorageTypeCeph: 111 istorages = append(istorages, &storages[i]) 112 case StorageTypeVCenter: 113 } 114 } 115 return istorages, nil 116 } 117 118 func (host *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { 119 return host.zone.GetIStorageById(id) 120 } 121 122 func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { 123 instances, err := host.zone.region.GetInstances(host.UUID, "", "") 124 if err != nil { 125 return nil, err 126 } 127 iInstnace := []cloudprovider.ICloudVM{} 128 for i := 0; i < len(instances); i++ { 129 instances[i].host = host 130 iInstnace = append(iInstnace, &instances[i]) 131 } 132 return iInstnace, nil 133 } 134 135 func (host *SHost) GetIVMById(instanceId string) (cloudprovider.ICloudVM, error) { 136 instance, err := host.zone.region.GetInstance(instanceId) 137 if err != nil { 138 return nil, err 139 } 140 instance.host = host 141 return instance, nil 142 } 143 144 func (host *SHost) GetId() string { 145 return host.UUID 146 } 147 148 func (host *SHost) GetName() string { 149 return host.Name 150 } 151 152 func (host *SHost) GetGlobalId() string { 153 return host.GetId() 154 } 155 156 func (host *SHost) IsEmulated() bool { 157 return false 158 } 159 160 func (host *SHost) GetStatus() string { 161 if host.Status == "Connected" { 162 return api.HOST_STATUS_RUNNING 163 } 164 return api.HOST_STATUS_UNKNOWN 165 } 166 167 func (host *SHost) Refresh() error { 168 return nil 169 } 170 171 func (host *SHost) GetHostStatus() string { 172 if host.Status == "Connected" { 173 return api.HOST_ONLINE 174 } 175 return api.HOST_OFFLINE 176 } 177 178 func (host *SHost) GetEnabled() bool { 179 return host.State == "Enabled" 180 } 181 182 func (host *SHost) GetAccessIp() string { 183 return host.ManagementIP 184 } 185 186 func (host *SHost) GetAccessMac() string { 187 return "" 188 } 189 190 func (host *SHost) GetSysInfo() jsonutils.JSONObject { 191 info := jsonutils.NewDict() 192 info.Add(jsonutils.NewString(CLOUD_PROVIDER_ZSTACK), "manufacture") 193 return info 194 } 195 196 func (host *SHost) GetSN() string { 197 return "" 198 } 199 200 func (host *SHost) GetReservedMemoryMb() int { 201 host.zone.fetchHostCmtbound() 202 return host.zone.reservedMemeoryMb 203 } 204 205 func (host *SHost) GetCpuCmtbound() float32 { 206 host.zone.fetchHostCmtbound() 207 return host.zone.cpuCmtbound 208 } 209 210 func (host *SHost) GetMemCmtbound() float32 { 211 host.zone.fetchHostCmtbound() 212 return host.zone.memCmtbound 213 } 214 215 func (host *SHost) GetCpuCount() int { 216 cpuCmtBound := host.GetCpuCmtbound() 217 if cpuCmtBound > 0 { 218 return int(float32(host.TotalCPUCapacity) / cpuCmtBound) 219 } 220 return host.TotalCPUCapacity 221 } 222 223 func (host *SHost) GetNodeCount() int8 { 224 return int8(host.CPUSockets) 225 } 226 227 func (host *SHost) GetCpuDesc() string { 228 return "" 229 } 230 231 func (host *SHost) GetCpuMhz() int { 232 return 0 233 } 234 235 func (host *SHost) GetMemSizeMB() int { 236 return host.TotalMemoryCapacity / 1024 / 1024 237 } 238 239 func (host *SHost) GetStorageSizeMB() int { 240 storages, err := host.zone.region.GetStorages(host.zone.UUID, host.ClusterUUID, "") 241 if err != nil { 242 return 0 243 } 244 totalStorage := 0 245 for _, storage := range storages { 246 if storage.Type == StorageTypeLocal { 247 localStorages, err := host.zone.region.GetLocalStorages(storage.UUID, host.UUID) 248 if err != nil { 249 return 0 250 } 251 for i := 0; i < len(localStorages); i++ { 252 totalStorage += int(localStorages[i].TotalCapacity) 253 } 254 } 255 } 256 return totalStorage / 1024 / 1024 257 } 258 259 func (host *SHost) GetStorageType() string { 260 return api.DISK_TYPE_HYBRID 261 } 262 263 func (host *SHost) GetHostType() string { 264 return api.HOST_TYPE_ZSTACK 265 } 266 267 func (region *SRegion) cleanDisks(diskIds []string) { 268 for i := 0; i < len(diskIds); i++ { 269 err := region.DeleteDisk(diskIds[i]) 270 if err != nil { 271 log.Errorf("clean disk %s error: %v", diskIds[i], err) 272 } 273 } 274 } 275 276 func (region *SRegion) createDataDisks(disks []cloudprovider.SDiskInfo, hostId string) ([]string, error) { 277 diskIds := []string{} 278 279 storages, err := region.GetStorages("", "", "") 280 if err != nil { 281 return nil, errors.Wrapf(err, "createDataDisks.GetStorages") 282 } 283 284 localstorages := []SLocalStorage{} 285 286 for _, storage := range storages { 287 if storage.Type == StorageTypeLocal { 288 localstorage, _ := region.GetLocalStorage(storage.UUID, hostId) 289 if localstorage != nil { 290 localstorages = append(localstorages, *localstorage) 291 } 292 } 293 } 294 295 for i := 0; i < len(disks); i++ { 296 storageInfo := strings.Split(disks[i].StorageExternalId, "/") 297 if len(storageInfo) == 0 { 298 return diskIds, fmt.Errorf("invalidate storage externalId: %s", disks[i].StorageExternalId) 299 } 300 storage, err := region.GetStorage(storageInfo[0]) 301 if err != nil { 302 return diskIds, errors.Wrapf(err, "createDataDisks") 303 } 304 305 switch storage.Type { 306 case StorageTypeCeph: 307 poolName := "" 308 for _, pool := range storage.Pools { 309 if pool.Type == CephPoolTypeData { 310 poolName = pool.PoolName 311 } 312 } 313 if len(poolName) == 0 { 314 return diskIds, fmt.Errorf("failed to found ceph data pool for storage %s to createDataDisk", storage.Name) 315 } 316 disk, err := region.CreateDisk(disks[i].Name, storage.UUID, "", poolName, disks[i].SizeGB, "") 317 if err != nil { 318 return diskIds, err 319 } 320 diskIds = append(diskIds, disk.UUID) 321 case StorageTypeLocal: 322 if len(localstorages) == 0 { 323 return nil, fmt.Errorf("No validate localstorage") 324 } 325 var disk *SDisk 326 var err error 327 for _, localstorage := range localstorages { 328 disk, err = region.CreateDisk(disks[i].Name, localstorage.primaryStorageID, hostId, "", disks[i].SizeGB, "") 329 if err != nil { 330 log.Warningf("createDataDisks error: %v", err) 331 } else { 332 diskIds = append(diskIds, disk.UUID) 333 break 334 } 335 } 336 if err != nil { 337 return diskIds, err 338 } 339 default: 340 return diskIds, fmt.Errorf("not support storageType %s", disks[i].StorageType) 341 } 342 } 343 return diskIds, nil 344 } 345 346 func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { 347 instance, err := host.zone.region._createVM(desc, host.ZoneUUID) 348 if err != nil { 349 return nil, errors.Wrapf(err, "host.zone.region._createVM") 350 } 351 352 diskIds, err := host.zone.region.createDataDisks(desc.DataDisks, instance.HostUUID) 353 if err != nil { 354 defer host.zone.region.cleanDisks(diskIds) 355 defer host.zone.region.DeleteVM(instance.UUID) 356 return nil, errors.Wrapf(err, "host.zone.region.createDataDisks") 357 } 358 359 err = host.zone.region.ResizeDisk(instance.RootVolumeUUID, int64(desc.SysDisk.SizeGB)*1024) 360 if err != nil { 361 log.Warningf("failed to resize system disk %s error: %v", instance.RootVolumeUUID, err) 362 } 363 364 for i := 0; i < len(diskIds); i++ { 365 err = host.zone.region.AttachDisk(instance.UUID, diskIds[i]) 366 if err != nil { 367 log.Errorf("failed to attach disk %s into instance %s error: %v", diskIds[i], instance.Name, err) 368 } 369 } 370 err = host.zone.region.AssignSecurityGroup(instance.UUID, desc.ExternalSecgroupId) 371 if err != nil { 372 return nil, err 373 } 374 return host.GetIVMById(instance.UUID) 375 } 376 377 func (region *SRegion) _createVM(desc *cloudprovider.SManagedVMCreateConfig, zoneId string) (*SInstance, error) { 378 l3Id := strings.Split(desc.ExternalNetworkId, "/")[0] 379 if len(l3Id) == 0 { 380 return nil, fmt.Errorf("invalid networkid: %s", desc.ExternalNetworkId) 381 } 382 _, err := region.GetL3Network(l3Id) 383 if err != nil { 384 log.Errorf("failed to found l3network %s error: %v", l3Id, err) 385 return nil, err 386 } 387 offerings := map[string]string{} 388 if len(desc.InstanceType) > 0 { 389 offering, err := region.GetInstanceOfferingByType(desc.InstanceType) 390 if err != nil { 391 if errors.Cause(err) == cloudprovider.ErrNotFound { 392 offering, err = region.CreateInstanceOffering(desc.InstanceType, desc.Cpu, desc.MemoryMB, "UserVm") 393 if err != nil { 394 return nil, err 395 } 396 } else { 397 return nil, err 398 } 399 } 400 offerings[offering.Name] = offering.UUID 401 } else { 402 _offerings, err := region.GetInstanceOfferings("", "", desc.Cpu, desc.MemoryMB) 403 if err != nil { 404 return nil, err 405 } 406 for _, offering := range _offerings { 407 offerings[offering.Name] = offering.UUID 408 } 409 if len(offerings) == 0 { 410 return nil, fmt.Errorf("instance type %dC%dMB not avaiable", desc.Cpu, desc.MemoryMB) 411 } 412 } 413 return region.CreateInstance(desc, l3Id, zoneId, offerings) 414 } 415 416 func (region *SRegion) CreateInstance(desc *cloudprovider.SManagedVMCreateConfig, l3Id, zoneId string, offerings map[string]string) (*SInstance, error) { 417 instance := &SInstance{} 418 systemTags := []string{ 419 "createWithoutCdRom::true", 420 "usbRedirect::false", 421 "vmConsoleMode::vnc", 422 "cleanTraffic::false", 423 } 424 if len(desc.IpAddr) > 0 { 425 systemTags = append(systemTags, fmt.Sprintf("staticIp::%s::%s", l3Id, desc.IpAddr)) 426 } 427 if len(desc.UserData) > 0 { 428 systemTags = append(systemTags, "userdata::"+desc.UserData) 429 } 430 if len(desc.PublicKey) > 0 { 431 systemTags = append(systemTags, "sshkey::"+desc.PublicKey) 432 } 433 var err error 434 for offerName, offerId := range offerings { 435 params := map[string]interface{}{ 436 "params": map[string]interface{}{ 437 "name": desc.NameEn, 438 "description": desc.Description, 439 "instanceOfferingUuid": offerId, 440 "imageUuid": desc.ExternalImageId, 441 "l3NetworkUuids": []string{ 442 l3Id, 443 }, 444 "zoneUuid": zoneId, 445 "dataVolumeSystemTags": []string{}, 446 "rootVolumeSystemTags": []string{}, 447 "vmMachineType": "", 448 "tagUuids": []string{}, 449 "defaultL3NetworkUuid": l3Id, 450 "dataDiskOfferingUuids": []string{}, 451 "systemTags": systemTags, 452 "vmNicConfig": []string{}, 453 }, 454 } 455 456 log.Debugf("Try instanceOffering : %s", offerName) 457 err = region.client.create("vm-instances", jsonutils.Marshal(params), instance) 458 if err == nil { 459 return instance, nil 460 } 461 log.Errorf("create %s instance failed error: %v", offerName, err) 462 } 463 if err != nil { 464 return nil, err 465 } 466 return nil, fmt.Errorf("instance type %dC%dMB not avaiable", desc.Cpu, desc.MemoryMB) 467 } 468 469 func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { 470 return nil, cloudprovider.ErrNotSupported 471 } 472 473 func (host *SHost) GetIsMaintenance() bool { 474 return false 475 } 476 477 func (host *SHost) GetVersion() string { 478 return "" 479 }