Files
cloudpods/pkg/multicloud/openstack/host.go
2020-05-26 14:31:50 +08:00

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
}