mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-23 04:51:39 +08:00
523 lines
12 KiB
Go
523 lines
12 KiB
Go
// 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 openstack
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
"yunion.io/x/onecloud/pkg/multicloud"
|
|
)
|
|
|
|
const (
|
|
VOLUME_TYPES_API_VERSION = "2.67"
|
|
)
|
|
|
|
type CpuInfo struct {
|
|
Arch string
|
|
Model string
|
|
Vendor string
|
|
Feature []string
|
|
Topology map[string]int
|
|
}
|
|
|
|
type Service struct {
|
|
Host string
|
|
ID string
|
|
DisabledReason string
|
|
}
|
|
|
|
type SResource struct {
|
|
CPU int
|
|
DiskGB int
|
|
Host string
|
|
MemoryMb int
|
|
Project string
|
|
}
|
|
|
|
type SHost struct {
|
|
multicloud.SHostBase
|
|
zone *SZone
|
|
|
|
CpuInfo string
|
|
|
|
Aggregates []string
|
|
CurrentWorkload int
|
|
Status string
|
|
State string
|
|
DiskAvailableLeast int
|
|
HostIP string
|
|
FreeDiskGB int
|
|
FreeRamMB int
|
|
HypervisorHostname string
|
|
HypervisorType string
|
|
HypervisorVersion string
|
|
ID string
|
|
LocalGB int
|
|
LocalGbUsed int
|
|
MemoryMB int
|
|
MemoryMbUsed int
|
|
RunningVms int
|
|
Service Service
|
|
Vcpus int
|
|
VcpusUsed int8
|
|
|
|
// less then version 2.28
|
|
HostName string
|
|
Zone string
|
|
Resource []map[string]SResource
|
|
}
|
|
|
|
func (host *SHost) GetId() string {
|
|
if len(host.ID) > 0 {
|
|
return host.ID
|
|
}
|
|
return host.HostName
|
|
}
|
|
|
|
func (host *SHost) GetName() string {
|
|
if len(host.Service.Host) > 0 {
|
|
return host.Service.Host
|
|
}
|
|
return host.HostName
|
|
}
|
|
|
|
func (host *SHost) GetGlobalId() string {
|
|
return host.GetId()
|
|
}
|
|
|
|
func (host *SHost) GetMetadata() *jsonutils.JSONDict {
|
|
return nil
|
|
}
|
|
|
|
func (host *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
|
return host.zone.GetIWires()
|
|
}
|
|
|
|
func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
|
istorages, err := host.zone.GetIStorages()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
storageTypes := host.zone.getAvailableStorages()
|
|
storageTypes = append(storageTypes, host.zone.getUnavailableStorage()...)
|
|
result := []cloudprovider.ICloudStorage{}
|
|
|
|
for _, istorage := range istorages {
|
|
if utils.IsInStringArray(istorage.GetStorageType(), storageTypes) {
|
|
result = append(result, istorage)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (host *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
|
return host.zone.GetIStorageById(id)
|
|
}
|
|
|
|
func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
|
instances, err := host.zone.region.GetInstances(host.GetName())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
iVMs := []cloudprovider.ICloudVM{}
|
|
for i := 0; i < len(instances); i++ {
|
|
instances[i].host = host
|
|
iVMs = append(iVMs, &instances[i])
|
|
}
|
|
return iVMs, nil
|
|
}
|
|
|
|
func (host *SHost) GetIVMById(gid string) (cloudprovider.ICloudVM, error) {
|
|
instance, err := host.zone.region.GetInstance(gid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
instance.host = host
|
|
return instance, nil
|
|
}
|
|
|
|
func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
|
network, err := host.zone.region.GetNetwork(desc.ExternalNetworkId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
zoneName := ""
|
|
for zone, hosts := range host.zone.cachedHosts {
|
|
if utils.IsInStringArray(host.GetName(), hosts) {
|
|
zoneName = zone
|
|
break
|
|
}
|
|
}
|
|
if len(zoneName) == 0 {
|
|
return nil, fmt.Errorf("failed to find zone info for host %s", host.GetName())
|
|
}
|
|
|
|
secgroups := []map[string]string{}
|
|
|
|
for _, secgroupId := range desc.ExternalSecgroupIds {
|
|
if secgroupId != SECGROUP_NOT_SUPPORT {
|
|
secgroups = append(secgroups, map[string]string{"name": secgroupId})
|
|
}
|
|
}
|
|
|
|
image, err := host.zone.region.GetImage(desc.ExternalImageId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sysDiskSizeGB := image.Size / 1024 / 1024 / 1024
|
|
if desc.SysDisk.SizeGB < sysDiskSizeGB {
|
|
desc.SysDisk.SizeGB = sysDiskSizeGB
|
|
}
|
|
|
|
if desc.SysDisk.SizeGB < image.GetMinOsDiskSizeGb() {
|
|
desc.SysDisk.SizeGB = image.GetMinOsDiskSizeGb()
|
|
}
|
|
|
|
BlockDeviceMappingV2 := []map[string]interface{}{}
|
|
|
|
diskIds := []string{}
|
|
|
|
defer func() {
|
|
for _, diskId := range diskIds {
|
|
err = host.zone.region.DeleteDisk(diskId)
|
|
if err != nil {
|
|
log.Errorf("clean disk %s error: %v", diskId, err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if desc.SysDisk.StorageType != api.STORAGE_OPENSTACK_NOVA { //新建volume
|
|
istorage, err := host.zone.GetIStorageById(desc.SysDisk.StorageExternalId)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "GetIStorageById(%s)", desc.SysDisk.StorageExternalId)
|
|
}
|
|
|
|
_sysDisk, err := host.zone.region.CreateDisk(desc.ExternalImageId, istorage.GetName(), "", desc.SysDisk.SizeGB, desc.SysDisk.Name, desc.ProjectId)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "CreateDisk %s", desc.SysDisk.Name)
|
|
}
|
|
|
|
diskIds = append(diskIds, _sysDisk.GetGlobalId())
|
|
|
|
BlockDeviceMappingV2 = append(BlockDeviceMappingV2, map[string]interface{}{
|
|
"boot_index": 0,
|
|
"uuid": _sysDisk.GetGlobalId(),
|
|
"source_type": "volume",
|
|
"destination_type": "volume",
|
|
"delete_on_termination": true,
|
|
})
|
|
} else {
|
|
BlockDeviceMappingV2 = append(BlockDeviceMappingV2, map[string]interface{}{
|
|
"boot_index": 0,
|
|
"uuid": image.ID,
|
|
"source_type": "image",
|
|
"destination_type": "local",
|
|
"delete_on_termination": true,
|
|
})
|
|
}
|
|
|
|
var _disk *SDisk
|
|
for index, disk := range desc.DataDisks {
|
|
istorage, err := host.zone.GetIStorageById(disk.StorageExternalId)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "GetIStorageById(%s)", disk.StorageExternalId)
|
|
}
|
|
_disk, err = host.zone.region.CreateDisk("", istorage.GetName(), "", disk.SizeGB, disk.Name, desc.ProjectId)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "CreateDisk %s", disk.Name)
|
|
}
|
|
diskIds = append(diskIds, _disk.ID)
|
|
|
|
mapping := map[string]interface{}{
|
|
"source_type": "volume",
|
|
"destination_type": "volume",
|
|
"delete_on_termination": true,
|
|
"boot_index": index + 1,
|
|
"uuid": _disk.ID,
|
|
}
|
|
|
|
BlockDeviceMappingV2 = append(BlockDeviceMappingV2, mapping)
|
|
}
|
|
|
|
params := map[string]map[string]interface{}{
|
|
"server": {
|
|
"name": desc.Name,
|
|
"adminPass": desc.Password,
|
|
//"description": desc.Description,
|
|
"accessIPv4": desc.IpAddr,
|
|
"availability_zone": fmt.Sprintf("%s:%s", zoneName, host.GetName()),
|
|
"networks": []map[string]string{
|
|
{
|
|
"uuid": network.NetworkID,
|
|
"fixed_ip": desc.IpAddr,
|
|
},
|
|
},
|
|
"security_groups": secgroups,
|
|
"user_data": desc.UserData,
|
|
"imageRef": desc.ExternalImageId,
|
|
"block_device_mapping_v2": BlockDeviceMappingV2,
|
|
},
|
|
}
|
|
|
|
flavorId, err := host.zone.region.syncFlavor(desc.InstanceType, desc.Cpu, desc.MemoryMB, desc.SysDisk.SizeGB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params["server"]["flavorRef"] = flavorId
|
|
|
|
if len(desc.PublicKey) > 0 {
|
|
keypairName, err := host.zone.region.syncKeypair(desc.Name, desc.PublicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params["server"]["key_name"] = keypairName
|
|
}
|
|
|
|
_, resp, err := host.zone.region.PostWithProject(desc.ProjectId, "compute", "/servers", "", jsonutils.Marshal(params))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diskIds = []string{}
|
|
serverId, err := resp.GetString("server", "id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
instance, err := host.zone.region.GetInstance(serverId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
instance.host = host
|
|
return instance, nil
|
|
}
|
|
|
|
func (host *SHost) GetEnabled() bool {
|
|
return true
|
|
}
|
|
|
|
func (host *SHost) GetAccessIp() string {
|
|
return host.HostIP
|
|
}
|
|
|
|
func (host *SHost) GetAccessMac() string {
|
|
return ""
|
|
}
|
|
|
|
func (host *SHost) GetSysInfo() jsonutils.JSONObject {
|
|
info := jsonutils.NewDict()
|
|
info.Add(jsonutils.NewString(CLOUD_PROVIDER_OPENSTACK), "manufacture")
|
|
return info
|
|
}
|
|
|
|
func (host *SHost) GetSN() string {
|
|
return ""
|
|
}
|
|
|
|
func (host *SHost) GetCpuCmtbound() float32 {
|
|
aggregates, err := host.zone.region.GetAggregates()
|
|
if err != nil || len(aggregates) == 0 {
|
|
return 16.0
|
|
}
|
|
CpuCmtbound := 1000000.0
|
|
for _, aggregate := range aggregates {
|
|
if utils.IsInStringArray(host.GetName(), aggregate.Hosts) {
|
|
if _cmtbound, ok := aggregate.Metadata["cpu_allocation_ratio"]; ok {
|
|
cmtbound, err := strconv.ParseFloat(_cmtbound, 32)
|
|
if err == nil && CpuCmtbound > cmtbound {
|
|
CpuCmtbound = cmtbound
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if CpuCmtbound >= 1000000.0 {
|
|
return 16.0
|
|
}
|
|
return float32(CpuCmtbound)
|
|
}
|
|
|
|
func (host *SHost) GetMemCmtbound() float32 {
|
|
aggregates, err := host.zone.region.GetAggregates()
|
|
if err != nil || len(aggregates) == 0 {
|
|
return 1.5
|
|
}
|
|
MemCmtbound := 1000000.0
|
|
for _, aggregate := range aggregates {
|
|
if utils.IsInStringArray(host.GetName(), aggregate.Hosts) {
|
|
if _cmtbound, ok := aggregate.Metadata["ram_allocation_ratio"]; ok {
|
|
cmtbound, err := strconv.ParseFloat(_cmtbound, 32)
|
|
if err == nil && MemCmtbound > cmtbound {
|
|
MemCmtbound = cmtbound
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if MemCmtbound >= 1000000.0 {
|
|
return 1.5
|
|
}
|
|
return float32(MemCmtbound)
|
|
}
|
|
|
|
func (host *SHost) GetCpuCount() int {
|
|
if host.Vcpus > 0 {
|
|
return host.Vcpus
|
|
}
|
|
host.Refresh()
|
|
return host.Vcpus
|
|
}
|
|
|
|
func (host *SHost) GetNodeCount() int8 {
|
|
if len(host.CpuInfo) > 0 {
|
|
info, err := jsonutils.Parse([]byte(host.CpuInfo))
|
|
if err == nil {
|
|
cpuInfo := &CpuInfo{}
|
|
err = info.Unmarshal(cpuInfo)
|
|
if err == nil {
|
|
if cell, ok := cpuInfo.Topology["cells"]; ok {
|
|
return int8(cell)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return int8(host.GetCpuCount())
|
|
}
|
|
|
|
func (host *SHost) GetCpuDesc() string {
|
|
return host.CpuInfo
|
|
}
|
|
|
|
func (host *SHost) GetCpuMhz() int {
|
|
return 0
|
|
}
|
|
|
|
func (host *SHost) GetMemSizeMB() int {
|
|
if host.MemoryMB > 0 {
|
|
return host.MemoryMB
|
|
}
|
|
host.Refresh()
|
|
return host.MemoryMB
|
|
}
|
|
|
|
func (host *SHost) GetStorageSizeMB() int {
|
|
if host.LocalGB > 0 {
|
|
return host.LocalGB * 1024
|
|
}
|
|
host.Refresh()
|
|
return host.LocalGB * 1024
|
|
}
|
|
|
|
func (host *SHost) GetStorageType() string {
|
|
return api.DISK_TYPE_HYBRID
|
|
}
|
|
|
|
func (host *SHost) GetHostType() string {
|
|
return api.HOST_TYPE_OPENSTACK
|
|
}
|
|
|
|
func (host *SHost) GetHostStatus() string {
|
|
if host.Status == "disabled" {
|
|
return api.HOST_OFFLINE
|
|
}
|
|
switch host.State {
|
|
case "up", "":
|
|
return api.HOST_ONLINE
|
|
default:
|
|
return api.HOST_OFFLINE
|
|
}
|
|
}
|
|
|
|
func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
|
|
return nil, cloudprovider.ErrNotSupported
|
|
}
|
|
|
|
func (host *SHost) GetIsMaintenance() bool {
|
|
switch host.Status {
|
|
case "enabled", "":
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (host *SHost) GetVersion() string {
|
|
_, version, _ := host.zone.region.GetVersion("compute")
|
|
return version
|
|
}
|
|
|
|
func (host *SHost) GetStatus() string {
|
|
return api.HOST_STATUS_RUNNING
|
|
}
|
|
|
|
func (host *SHost) IsEmulated() bool {
|
|
return false
|
|
}
|
|
|
|
func (host *SHost) Refresh() error {
|
|
new, err := host.zone.region.GetIHostById(host.GetId())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := jsonutils.Update(host, new); err != nil {
|
|
return err
|
|
}
|
|
if len(host.Resource) > 0 {
|
|
for _, resouce := range host.Resource {
|
|
for _, info := range resouce {
|
|
if info.Project == "(total)" {
|
|
host.LocalGB = info.DiskGB
|
|
host.Vcpus = info.CPU
|
|
host.MemoryMB = info.MemoryMb
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type SAggregate struct {
|
|
AvailabilityZone string
|
|
CreatedAt time.Time
|
|
Deleted bool
|
|
Hosts []string
|
|
Id string
|
|
Metadata map[string]string
|
|
Name string
|
|
Uuid string
|
|
}
|
|
|
|
func (region *SRegion) GetAggregates() ([]SAggregate, error) {
|
|
_, resp, err := region.List("compute", "/os-aggregates", "", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
aggregates := []SAggregate{}
|
|
err = resp.Unmarshal(&aggregates, "aggregates")
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, `resp.Unmarshal(&aggregates, "aggregates")`)
|
|
}
|
|
return aggregates, nil
|
|
}
|