mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
1809 lines
50 KiB
Go
1809 lines
50 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 candidate
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"sort"
|
|
gosync "sync"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/sets"
|
|
"yunion.io/x/pkg/utils"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
|
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
|
"yunion.io/x/onecloud/pkg/apis/scheduler"
|
|
computedb "yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
|
computemodels "yunion.io/x/onecloud/pkg/compute/models"
|
|
"yunion.io/x/onecloud/pkg/scheduler/core"
|
|
schedmodels "yunion.io/x/onecloud/pkg/scheduler/models"
|
|
o "yunion.io/x/onecloud/pkg/scheduler/options"
|
|
"yunion.io/x/onecloud/pkg/util/cgrouputils/cpuset"
|
|
)
|
|
|
|
type hostGetter struct {
|
|
*baseHostGetter
|
|
h *HostDesc
|
|
}
|
|
|
|
func newHostGetter(h *HostDesc) *hostGetter {
|
|
return &hostGetter{
|
|
baseHostGetter: newBaseHostGetter(h.BaseHostDesc),
|
|
h: h,
|
|
}
|
|
}
|
|
|
|
func (h *hostGetter) CreatingGuestCount() int {
|
|
return int(h.h.CreatingGuestCount)
|
|
}
|
|
|
|
func (h *hostGetter) RunningCPUCount() int64 {
|
|
return h.h.RunningCPUCount
|
|
}
|
|
|
|
func (h *hostGetter) TotalCPUCount(useRsvd bool) int64 {
|
|
return h.h.GetTotalCPUCount(useRsvd)
|
|
}
|
|
|
|
func (h *hostGetter) FreeCPUCount(useRsvd bool) int64 {
|
|
return h.h.GetFreeCPUCount(useRsvd)
|
|
}
|
|
|
|
func (h *hostGetter) FreeMemorySize(useRsvd bool) int64 {
|
|
return h.h.GetFreeMemSize(useRsvd)
|
|
}
|
|
|
|
func (h *hostGetter) NumaAllocateEnabled() bool {
|
|
return h.h.HostTopo.NumaEnabled || h.h.HostType == computeapi.HOST_TYPE_CONTAINER
|
|
}
|
|
|
|
func (h *hostGetter) GetFreeCpuNuma() []*scheduler.SFreeNumaCpuMem {
|
|
return h.h.GetFreeCpuNuma()
|
|
}
|
|
|
|
func (h *hostGetter) RunningMemorySize() int64 {
|
|
return h.h.RunningMemSize
|
|
}
|
|
|
|
func (h *hostGetter) TotalMemorySize(useRsvd bool) int64 {
|
|
return h.h.GetTotalMemSize(useRsvd)
|
|
}
|
|
|
|
func (h *hostGetter) IsEmpty() bool {
|
|
return h.h.GuestCount == 0
|
|
}
|
|
|
|
func (h *hostGetter) StorageInfo() []*baremetal.BaremetalStorage {
|
|
return nil
|
|
}
|
|
|
|
func (h *hostGetter) GetFreeStorageSizeOfType(storageType string, mediumType string, useRsvd bool, reqMaxSize int64) (int64, int64, error) {
|
|
return h.h.GetFreeStorageSizeOfType(storageType, mediumType, useRsvd, reqMaxSize)
|
|
}
|
|
|
|
func (h *hostGetter) GetFreePort(netId string) int {
|
|
return h.h.GetFreePort(netId)
|
|
}
|
|
|
|
func (h *hostGetter) OvnCapable() bool {
|
|
return len(h.h.OvnVersion) > 0
|
|
}
|
|
|
|
type HostDesc struct {
|
|
*BaseHostDesc
|
|
|
|
// cpu
|
|
CPUCmtbound float32 `json:"cpu_cmtbound"`
|
|
CPUBoundCount int64 `json:"cpu_bound_count"`
|
|
CPULoad *float64 `json:"cpu_load"`
|
|
TotalCPUCount int64 `json:"total_cpu_count"`
|
|
RunningCPUCount int64 `json:"running_cpu_count"`
|
|
CreatingCPUCount int64 `json:"creating_cpu_count"`
|
|
RequiredCPUCount int64 `json:"required_cpu_count"`
|
|
FakeDeletedCPUCount int64 `json:"fake_deleted_cpu_count"`
|
|
FreeCPUCount int64 `json:"free_cpu_count"`
|
|
|
|
// memory
|
|
MemCmtbound float32 `json:"mem_cmtbound"`
|
|
TotalMemSize int64 `json:"total_mem_size"`
|
|
FreeMemSize int64 `json:"free_mem_size"`
|
|
RunningMemSize int64 `json:"running_mem_size"`
|
|
CreatingMemSize int64 `json:"creating_mem_size"`
|
|
RequiredMemSize int64 `json:"required_mem_size"`
|
|
FakeDeletedMemSize int64 `json:"fake_deleted_mem_size"`
|
|
|
|
EnableCpuNumaAllocate bool `json:"enable_cpu_numa_allocate"`
|
|
HostTopo *SHostTopo `json:"host_topo"`
|
|
|
|
// storage
|
|
StorageTypes []string `json:"storage_types"`
|
|
|
|
// IO
|
|
IOBoundCount int64 `json:"io_bound_count"`
|
|
IOLoad *float64 `json:"io_load"`
|
|
|
|
// server
|
|
GuestCount int64 `json:"guest_count"`
|
|
CreatingGuestCount int64 `json:"creating_guest_count"`
|
|
RunningGuestCount int64 `json:"running_guest_count"`
|
|
|
|
//Groups *GroupCounts `json:"groups"`
|
|
Metadata map[string]string `json:"metadata"`
|
|
IsMaintenance bool `json:"is_maintenance"`
|
|
GuestReservedResource *ReservedResource `json:"guest_reserved_resource"`
|
|
GuestReservedResourceUsed *ReservedResource `json:"guest_reserved_used"`
|
|
}
|
|
|
|
type CPUFree struct {
|
|
Cpu int
|
|
Free int
|
|
}
|
|
type SorttedCPUFree []*CPUFree
|
|
|
|
func (pq *SorttedCPUFree) LoadCpu(cpuId int) {
|
|
for i := range *pq {
|
|
if (*pq)[i].Cpu == cpuId {
|
|
(*pq)[i].Free -= 1
|
|
}
|
|
}
|
|
}
|
|
|
|
func (pq SorttedCPUFree) Len() int { return len(pq) }
|
|
|
|
func (pq SorttedCPUFree) Less(i, j int) bool {
|
|
return pq[i].Free > pq[j].Free
|
|
}
|
|
|
|
func (pq SorttedCPUFree) Swap(i, j int) {
|
|
pq[i], pq[j] = pq[j], pq[i]
|
|
}
|
|
|
|
func (pq *SorttedCPUFree) Push(item interface{}) {
|
|
*pq = append(*pq, item.(*CPUFree))
|
|
}
|
|
|
|
func (pq *SorttedCPUFree) Pop() interface{} {
|
|
old := *pq
|
|
n := len(old)
|
|
item := old[n-1]
|
|
old[n-1] = nil // avoid memory leak
|
|
*pq = old[0 : n-1]
|
|
return item
|
|
}
|
|
|
|
type CPUDie struct {
|
|
LogicalProcessors cpuset.CPUSet
|
|
// Core thread id maps
|
|
CoreThreadIdMaps map[int]int
|
|
|
|
CpuFree SorttedCPUFree
|
|
VcpuCount int
|
|
}
|
|
|
|
func (d *CPUDie) initCpuFree(cpuCmtbound int) {
|
|
cpuFree := make([]*CPUFree, 0)
|
|
for _, cpuId := range d.LogicalProcessors.ToSliceNoSort() {
|
|
cpuFree = append(cpuFree, &CPUFree{cpuId, cpuCmtbound})
|
|
}
|
|
d.CpuFree = cpuFree
|
|
sort.Sort(d.CpuFree)
|
|
}
|
|
|
|
type SorttedCPUDie []*CPUDie
|
|
|
|
func (pq SorttedCPUDie) Len() int { return len(pq) }
|
|
|
|
func (pq SorttedCPUDie) Less(i, j int) bool {
|
|
return pq[i].VcpuCount < pq[j].VcpuCount
|
|
}
|
|
|
|
func (pq SorttedCPUDie) Swap(i, j int) {
|
|
pq[i], pq[j] = pq[j], pq[i]
|
|
}
|
|
|
|
func (pq *SorttedCPUDie) Push(item interface{}) {
|
|
*pq = append(*pq, item.(*CPUDie))
|
|
}
|
|
|
|
func (pq *SorttedCPUDie) Pop() interface{} {
|
|
old := *pq
|
|
n := len(old)
|
|
item := old[n-1]
|
|
old[n-1] = nil // avoid memory leak
|
|
*pq = old[0 : n-1]
|
|
return item
|
|
}
|
|
|
|
func (pq *SorttedCPUDie) LoadCpus(cpus []int, vcpuCount int) {
|
|
var cpuDies = map[int][]int{}
|
|
for i := 0; i < len(cpus); i++ {
|
|
for j := 0; j < len(*pq); j++ {
|
|
if (*pq)[j].LogicalProcessors.Contains(cpus[i]) {
|
|
if cpuDie, ok := cpuDies[j]; !ok {
|
|
cpuDies[j] = []int{cpus[i]}
|
|
} else {
|
|
cpuDies[j] = append(cpuDie, cpus[i])
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(*pq); i++ {
|
|
if cpus, ok := cpuDies[i]; ok {
|
|
d := (*pq)[i]
|
|
for _, cpu := range cpus {
|
|
d.CpuFree.LoadCpu(cpu)
|
|
}
|
|
d.VcpuCount += vcpuCount
|
|
|
|
sort.Sort(d.CpuFree)
|
|
}
|
|
}
|
|
sort.Sort(pq)
|
|
}
|
|
|
|
type NumaNode struct {
|
|
CpuDies SorttedCPUDie
|
|
LogicalProcessors cpuset.CPUSet
|
|
VcpuCount int
|
|
CpuCount int
|
|
|
|
NodeId int
|
|
Distances []int
|
|
NumaNodeMemSizeKB int
|
|
NumaNodeFreeMemSizeKB int
|
|
}
|
|
|
|
func (n *NumaNode) nodeEnough(vcpuCount, memSizeKB int, cmtBound float32, enableNumaAlloc bool) bool {
|
|
if int(float32(n.CpuCount)*cmtBound)-n.VcpuCount < vcpuCount {
|
|
return false
|
|
}
|
|
if enableNumaAlloc {
|
|
if n.NumaNodeFreeMemSizeKB < memSizeKB {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (n *NumaNode) allocCpusetSequenceN(vcpuCount int, usedCpu map[int]int) {
|
|
var seqNumber = o.Options.GuestCpusetAllocSequenceInterval
|
|
if vcpuCount%seqNumber != 0 || n.CpuCount/len(n.CpuDies) < vcpuCount {
|
|
n._allocCpuset(vcpuCount, usedCpu)
|
|
return
|
|
}
|
|
|
|
for i := range n.CpuDies {
|
|
detectedSet := cpuset.NewCPUSet()
|
|
for j := range n.CpuDies[i].CpuFree {
|
|
if detectedSet.Contains(n.CpuDies[i].CpuFree[j].Cpu) {
|
|
continue
|
|
}
|
|
|
|
cpuIdBase := n.CpuDies[i].CpuFree[j].Cpu - n.CpuDies[i].CpuFree[j].Cpu%vcpuCount
|
|
lo, hi := cpuIdBase, cpuIdBase+vcpuCount-1
|
|
cpuIds := make([]int, hi-lo+1)
|
|
for m := range cpuIds {
|
|
cpuIds[m] = m + lo
|
|
}
|
|
|
|
var matched = true
|
|
cpuIdSet := cpuset.NewCPUSet(cpuIds...)
|
|
detectedSet = detectedSet.Union(cpuIdSet)
|
|
for k := range n.CpuDies[i].CpuFree {
|
|
if !cpuIdSet.Contains(n.CpuDies[i].CpuFree[k].Cpu) {
|
|
continue
|
|
}
|
|
if n.CpuDies[i].CpuFree[k].Free <= 0 {
|
|
matched = false
|
|
break
|
|
}
|
|
}
|
|
if !matched {
|
|
continue
|
|
}
|
|
|
|
for m := range cpuIds {
|
|
usedCpu[cpuIds[m]] = 1
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
n._allocCpuset(vcpuCount, usedCpu)
|
|
}
|
|
|
|
func (n *NumaNode) allocCpuset(vcpuCount int, usedCpu map[int]int) {
|
|
if o.Options.GuestCpusetAllocSequence {
|
|
n.allocCpusetSequenceN(vcpuCount, usedCpu)
|
|
return
|
|
}
|
|
n._allocCpuset(vcpuCount, usedCpu)
|
|
}
|
|
|
|
func (n *NumaNode) _allocCpuset(vcpuCount int, usedCpu map[int]int) {
|
|
for i := range n.CpuDies {
|
|
for j := range n.CpuDies[i].CpuFree {
|
|
cpuId, nFree := n.CpuDies[i].CpuFree[j].Cpu, n.CpuDies[i].CpuFree[j].Free
|
|
if cnt, ok := usedCpu[cpuId]; ok {
|
|
if cnt < nFree {
|
|
usedCpu[cpuId] = cnt + 1
|
|
vcpuCount -= 1
|
|
if vcpuCount <= 0 {
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if nFree > 0 {
|
|
usedCpu[cpuId] = 1
|
|
vcpuCount -= 1
|
|
if vcpuCount <= 0 {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if pairCpuId, ok := n.CpuDies[i].CoreThreadIdMaps[cpuId]; ok {
|
|
for k := range n.CpuDies[i].CpuFree {
|
|
if n.CpuDies[i].CpuFree[k].Cpu == pairCpuId {
|
|
pairNFree := n.CpuDies[i].CpuFree[k].Free
|
|
if cnt, ok := usedCpu[pairCpuId]; ok {
|
|
if cnt < pairNFree {
|
|
usedCpu[pairCpuId] = cnt + 1
|
|
vcpuCount -= 1
|
|
if vcpuCount <= 0 {
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if pairNFree > 0 {
|
|
usedCpu[pairCpuId] = 1
|
|
vcpuCount -= 1
|
|
if vcpuCount <= 0 {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//sort.Sort(n.CpuDies[i].CpuFree)
|
|
}
|
|
n._allocCpuset(vcpuCount, usedCpu)
|
|
}
|
|
|
|
func (n *NumaNode) AllocCpuset(vcpuCount int) []int {
|
|
if vcpuCount <= 0 {
|
|
return nil
|
|
}
|
|
|
|
var usedCpuCount = make(map[int]int)
|
|
n.allocCpuset(vcpuCount, usedCpuCount)
|
|
|
|
var ret = make([]int, 0)
|
|
for cpuId, cnt := range usedCpuCount {
|
|
for cnt > 0 {
|
|
ret = append(ret, cpuId)
|
|
cnt -= 1
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func NewNumaNode(nodeId int, nodeDistances []int, hugepageSizeKb int, nodeHugepages []hostapi.HostNodeHugepageNr, memSizeKB int, memCmtBound float32) *NumaNode {
|
|
n := new(NumaNode)
|
|
n.LogicalProcessors = cpuset.NewCPUSet()
|
|
n.NodeId = nodeId
|
|
n.Distances = nodeDistances
|
|
|
|
if len(nodeHugepages) > 0 {
|
|
for i := range nodeHugepages {
|
|
if nodeHugepages[i].NodeId == nodeId {
|
|
n.NumaNodeMemSizeKB = nodeHugepages[i].HugepageNr * hugepageSizeKb
|
|
}
|
|
}
|
|
} else {
|
|
n.NumaNodeMemSizeKB = int(float32(memSizeKB) * memCmtBound)
|
|
}
|
|
|
|
n.NumaNodeFreeMemSizeKB = n.NumaNodeMemSizeKB
|
|
return n
|
|
}
|
|
|
|
type SHostTopo struct {
|
|
Nodes []*NumaNode
|
|
NumaEnabled bool
|
|
CPUCmtbound float32
|
|
HostName string
|
|
}
|
|
|
|
func HostTopoSubPendingUsage(topo *SHostTopo, cpuUsage map[int]int, numaMemUsage map[int]int) *SHostTopo {
|
|
res := new(SHostTopo)
|
|
res.NumaEnabled = topo.NumaEnabled
|
|
res.CPUCmtbound = topo.CPUCmtbound
|
|
res.Nodes = make([]*NumaNode, len(topo.Nodes))
|
|
for i := range topo.Nodes {
|
|
res.Nodes[i] = new(NumaNode)
|
|
res.Nodes[i].LogicalProcessors = topo.Nodes[i].LogicalProcessors.Clone()
|
|
res.Nodes[i].VcpuCount = topo.Nodes[i].VcpuCount
|
|
res.Nodes[i].CpuCount = topo.Nodes[i].CpuCount
|
|
res.Nodes[i].NodeId = topo.Nodes[i].NodeId
|
|
res.Nodes[i].NumaNodeMemSizeKB = topo.Nodes[i].NumaNodeMemSizeKB
|
|
res.Nodes[i].NumaNodeFreeMemSizeKB = topo.Nodes[i].NumaNodeFreeMemSizeKB
|
|
res.Nodes[i].Distances = topo.Nodes[i].Distances
|
|
|
|
if memUsed, ok := numaMemUsage[topo.Nodes[i].NodeId]; ok {
|
|
res.Nodes[i].NumaNodeFreeMemSizeKB -= memUsed * 1024
|
|
}
|
|
res.Nodes[i].CpuDies = make([]*CPUDie, len(topo.Nodes[i].CpuDies))
|
|
for j := range topo.Nodes[i].CpuDies {
|
|
res.Nodes[i].CpuDies[j] = &CPUDie{
|
|
LogicalProcessors: topo.Nodes[i].CpuDies[j].LogicalProcessors.Clone(),
|
|
CpuFree: make(SorttedCPUFree, 0),
|
|
VcpuCount: topo.Nodes[i].CpuDies[j].VcpuCount,
|
|
}
|
|
|
|
for k := range topo.Nodes[i].CpuDies[j].CpuFree {
|
|
cpuFree := topo.Nodes[i].CpuDies[j].CpuFree[k]
|
|
cpuId := cpuFree.Cpu
|
|
free := cpuFree.Free
|
|
if pending, ok := cpuUsage[cpuId]; ok {
|
|
res.Nodes[i].CpuDies[j].CpuFree = append(res.Nodes[i].CpuDies[j].CpuFree, &CPUFree{cpuId, free - pending})
|
|
|
|
res.Nodes[i].CpuDies[j].VcpuCount += pending
|
|
res.Nodes[i].VcpuCount += pending
|
|
} else {
|
|
res.Nodes[i].CpuDies[j].CpuFree = append(res.Nodes[i].CpuDies[j].CpuFree, &CPUFree{cpuId, free})
|
|
}
|
|
}
|
|
|
|
sort.Sort(res.Nodes[i].CpuDies[j].CpuFree)
|
|
}
|
|
}
|
|
sort.Sort(res)
|
|
return res
|
|
}
|
|
|
|
func (pq SHostTopo) Len() int { return len(pq.Nodes) }
|
|
|
|
func (pq SHostTopo) Less(i, j int) bool {
|
|
if pq.NumaEnabled {
|
|
if pq.Nodes[i].NumaNodeFreeMemSizeKB == pq.Nodes[j].NumaNodeFreeMemSizeKB {
|
|
return pq.Nodes[i].VcpuCount < pq.Nodes[j].VcpuCount
|
|
}
|
|
return pq.Nodes[i].NumaNodeFreeMemSizeKB > pq.Nodes[j].NumaNodeFreeMemSizeKB
|
|
} else {
|
|
return pq.Nodes[i].NumaNodeFreeMemSizeKB > pq.Nodes[j].NumaNodeFreeMemSizeKB
|
|
}
|
|
}
|
|
|
|
func (pq SHostTopo) Swap(i, j int) {
|
|
pq.Nodes[i], pq.Nodes[j] = pq.Nodes[j], pq.Nodes[i]
|
|
}
|
|
|
|
func (pq *SHostTopo) Push(item interface{}) {
|
|
(*pq).Nodes = append((*pq).Nodes, item.(*NumaNode))
|
|
}
|
|
|
|
func (h *SHostTopo) LoadCpuNumaPin(guestsCpuNumaPin []scheduler.SCpuNumaPin) {
|
|
for _, gCpuNumaPin := range guestsCpuNumaPin {
|
|
var node *NumaNode
|
|
for i := range h.Nodes {
|
|
if h.Nodes[i].NodeId == gCpuNumaPin.NodeId {
|
|
node = h.Nodes[i]
|
|
}
|
|
}
|
|
|
|
cpus := gCpuNumaPin.CpuPin
|
|
node.CpuDies.LoadCpus(cpus, len(cpus))
|
|
if gCpuNumaPin.MemSizeMB != nil {
|
|
node.NumaNodeFreeMemSizeKB -= *gCpuNumaPin.MemSizeMB * 1024
|
|
}
|
|
node.VcpuCount += len(cpus)
|
|
}
|
|
sort.Sort(h)
|
|
}
|
|
|
|
func (h *SHostTopo) nodesEnough(nodeCount, vcpuCount int, memSizeKB int) bool {
|
|
var leastFree = memSizeKB / nodeCount
|
|
var leastCpuCount = vcpuCount / nodeCount
|
|
var remPcpuCount = vcpuCount % nodeCount
|
|
|
|
for i := 0; i < nodeCount; i++ {
|
|
if h.NumaEnabled {
|
|
if h.Nodes[i].NumaNodeFreeMemSizeKB < leastFree {
|
|
return false
|
|
}
|
|
}
|
|
|
|
requireCpuCount := leastCpuCount
|
|
if remPcpuCount > 0 {
|
|
requireCpuCount += 1
|
|
remPcpuCount -= 1
|
|
}
|
|
if (h.Nodes[i].VcpuCount + requireCpuCount) > int(float32(h.Nodes[i].CpuCount)*h.CPUCmtbound) {
|
|
return false
|
|
}
|
|
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (h *SHostTopo) allocCpuNumaNodesByPreferNodes(
|
|
vcpuCount, memSizeKB, nodeCount int, sortedNumaDistance []SSortedNumaDistance,
|
|
) []scheduler.SCpuNumaPin {
|
|
res := make([]scheduler.SCpuNumaPin, 0)
|
|
var nodeAllocSize = memSizeKB / nodeCount
|
|
var pcpuCount = vcpuCount / nodeCount
|
|
var remPcpuCount = vcpuCount % nodeCount
|
|
|
|
allocatedNode := 0
|
|
for i := range sortedNumaDistance {
|
|
if allocatedNode >= nodeCount {
|
|
break
|
|
}
|
|
|
|
var npcpuCount = pcpuCount
|
|
if remPcpuCount > 0 {
|
|
npcpuCount += 1
|
|
remPcpuCount -= 1
|
|
}
|
|
nodeIdx := sortedNumaDistance[i].NodeIndex
|
|
if h.Nodes[nodeIdx].nodeEnough(vcpuCount, memSizeKB, h.CPUCmtbound, h.NumaEnabled) {
|
|
cpuNumaPin := scheduler.SCpuNumaPin{
|
|
CpuPin: h.Nodes[nodeIdx].AllocCpuset(npcpuCount),
|
|
NodeId: h.Nodes[nodeIdx].NodeId,
|
|
}
|
|
allocSize := nodeAllocSize / 1024
|
|
cpuNumaPin.MemSizeMB = &allocSize
|
|
res = append(res, cpuNumaPin)
|
|
allocatedNode += 1
|
|
} else {
|
|
log.Infof("%s node %v not enough", h.HostName, h.Nodes[i])
|
|
}
|
|
|
|
log.Infof("node %d, free mems %d", h.Nodes[nodeIdx].NodeId, h.Nodes[nodeIdx].NumaNodeFreeMemSizeKB)
|
|
}
|
|
|
|
if allocatedNode < nodeCount {
|
|
return nil
|
|
}
|
|
return res
|
|
}
|
|
|
|
type SSortedNumaDistance struct {
|
|
NodeIndex int
|
|
Distance int
|
|
FreeMemSize int
|
|
}
|
|
|
|
func (h *SHostTopo) getDistancesSeqByPreferNodes(preferNumaNodes []int, memSizeKB int) []SSortedNumaDistance {
|
|
sortedNumaDistance := make([]SSortedNumaDistance, len(h.Nodes))
|
|
for i := range h.Nodes {
|
|
distance := 0
|
|
for j := range preferNumaNodes {
|
|
log.Infof("node distance %v", h.Nodes[i].Distances)
|
|
distance += h.Nodes[i].Distances[preferNumaNodes[j]]
|
|
}
|
|
sortedNumaDistance[i] = SSortedNumaDistance{
|
|
NodeIndex: i,
|
|
Distance: distance,
|
|
FreeMemSize: h.Nodes[i].NumaNodeFreeMemSizeKB,
|
|
}
|
|
}
|
|
sort.Slice(sortedNumaDistance, func(i, j int) bool {
|
|
// 7 is tolerant max distances
|
|
if sortedNumaDistance[i].Distance > (7 + sortedNumaDistance[j].Distance) {
|
|
return false
|
|
} else if (sortedNumaDistance[i].Distance + 7) < sortedNumaDistance[j].Distance {
|
|
return true
|
|
}
|
|
if sortedNumaDistance[i].Distance < sortedNumaDistance[j].Distance {
|
|
return sortedNumaDistance[i].FreeMemSize > memSizeKB && sortedNumaDistance[j].FreeMemSize-sortedNumaDistance[i].FreeMemSize <= 2*memSizeKB
|
|
} else {
|
|
return sortedNumaDistance[j].FreeMemSize > memSizeKB && sortedNumaDistance[i].FreeMemSize-sortedNumaDistance[j].FreeMemSize >= 2*memSizeKB
|
|
}
|
|
})
|
|
return sortedNumaDistance
|
|
}
|
|
|
|
func (h *SHostTopo) AllocCpuNumaNodes(vcpuCount, memSizeKB int, ignoreMemSingular bool, preferNumaNodes []int) []scheduler.SCpuNumaPin {
|
|
if h.NumaEnabled && len(preferNumaNodes) > 0 {
|
|
log.Infof("preferNumaNodes %v", preferNumaNodes)
|
|
sortedNumaDistance := h.getDistancesSeqByPreferNodes(preferNumaNodes, memSizeKB)
|
|
for nodeCount := 1; nodeCount <= len(h.Nodes); nodeCount *= 2 {
|
|
ret := h.allocCpuNumaNodesByPreferNodes(vcpuCount, memSizeKB, nodeCount, sortedNumaDistance)
|
|
if ret != nil {
|
|
return ret
|
|
}
|
|
}
|
|
}
|
|
|
|
res := make([]scheduler.SCpuNumaPin, 0)
|
|
for nodeCount := 1; nodeCount <= len(h.Nodes); nodeCount *= 2 {
|
|
if ok := h.nodesEnough(nodeCount, vcpuCount, memSizeKB); !ok {
|
|
log.Infof("host %s node count %d not enough", h.HostName, nodeCount)
|
|
continue
|
|
}
|
|
log.Infof("use node count %d", nodeCount)
|
|
|
|
var nodeAllocSize = memSizeKB / nodeCount
|
|
if h.NumaEnabled && !ignoreMemSingular {
|
|
if nodeAllocSize/1024%1024 > 0 {
|
|
log.Infof("host %s node alloc size singular %d", h.HostName, nodeAllocSize)
|
|
continue
|
|
}
|
|
}
|
|
|
|
var pcpuCount = vcpuCount / nodeCount
|
|
var remPcpuCount = vcpuCount % nodeCount
|
|
for i := 0; i < nodeCount; i++ {
|
|
var npcpuCount = pcpuCount
|
|
if remPcpuCount > 0 {
|
|
npcpuCount += 1
|
|
remPcpuCount -= 1
|
|
}
|
|
cpuNumaPin := scheduler.SCpuNumaPin{
|
|
CpuPin: h.Nodes[i].AllocCpuset(npcpuCount),
|
|
NodeId: h.Nodes[i].NodeId,
|
|
}
|
|
if h.NumaEnabled {
|
|
allocSize := nodeAllocSize / 1024
|
|
cpuNumaPin.MemSizeMB = &allocSize
|
|
}
|
|
res = append(res, cpuNumaPin)
|
|
}
|
|
break
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func (h *SHostTopo) AllocCpuNumaNodesWithNodeCount(vcpuCount, memSizeKB, nodeCount int) []scheduler.SCpuNumaPin {
|
|
res := make([]scheduler.SCpuNumaPin, 0)
|
|
var nodeAllocSize = memSizeKB / nodeCount
|
|
var pcpuCount = vcpuCount / nodeCount
|
|
var remPcpuCount = vcpuCount % nodeCount
|
|
for i := 0; i < nodeCount; i++ {
|
|
var npcpuCount = pcpuCount
|
|
if remPcpuCount > 0 {
|
|
npcpuCount += 1
|
|
remPcpuCount -= 1
|
|
}
|
|
|
|
cpuNumaPin := scheduler.SCpuNumaPin{
|
|
CpuPin: h.Nodes[i].AllocCpuset(npcpuCount),
|
|
NodeId: h.Nodes[i].NodeId,
|
|
}
|
|
if h.NumaEnabled {
|
|
allocSize := nodeAllocSize / 1024
|
|
cpuNumaPin.MemSizeMB = &allocSize
|
|
}
|
|
res = append(res, cpuNumaPin)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (b *HostBuilder) buildHostTopo(
|
|
desc *HostDesc, reservedCpus *cpuset.CPUSet,
|
|
hugepageSizeKb int, nodeHugepages []hostapi.HostNodeHugepageNr,
|
|
info *hostapi.HostTopology,
|
|
) error {
|
|
var numaEnabled = len(nodeHugepages) > 0
|
|
hostTopo := new(SHostTopo)
|
|
hostTopo.Nodes = make([]*NumaNode, len(info.Nodes))
|
|
|
|
hasL3Cache := false
|
|
for i := 0; i < len(info.Nodes); i++ {
|
|
nodoMemSizeKB := 0
|
|
if info.Nodes[i].Memory != nil {
|
|
nodoMemSizeKB = int(info.Nodes[i].Memory.TotalUsableBytes/1024) - (desc.MemReserved * 1024 / len(info.Nodes))
|
|
if desc.HostType == computeapi.HOST_TYPE_CONTAINER && o.Options.ContainerNumaAllocate {
|
|
numaEnabled = true
|
|
log.Infof("host %s ignore singular", desc.Name)
|
|
}
|
|
}
|
|
|
|
node := NewNumaNode(info.Nodes[i].ID, info.Nodes[i].Distances, hugepageSizeKb, nodeHugepages, nodoMemSizeKB, desc.MemCmtbound)
|
|
|
|
cpuDies := make([]*CPUDie, 0)
|
|
for j := 0; j < len(info.Nodes[i].Caches); j++ {
|
|
if info.Nodes[i].Caches[j].Level != 3 {
|
|
continue
|
|
}
|
|
hasL3Cache = true
|
|
cpuDie := new(CPUDie)
|
|
cpuDie.CoreThreadIdMaps = make(map[int]int)
|
|
dieBuilder := cpuset.NewBuilder()
|
|
for k := 0; k < len(info.Nodes[i].Caches[j].LogicalProcessors); k++ {
|
|
if reservedCpus != nil && reservedCpus.Contains(int(info.Nodes[i].Caches[j].LogicalProcessors[k])) {
|
|
continue
|
|
}
|
|
dieBuilder.Add(int(info.Nodes[i].Caches[j].LogicalProcessors[k]))
|
|
}
|
|
cpuDie.LogicalProcessors = dieBuilder.Result()
|
|
cpuDie.initCpuFree(int(desc.CPUCmtbound))
|
|
|
|
for _, c := range info.Nodes[i].Cores {
|
|
if len(c.LogicalProcessors) != 2 {
|
|
continue
|
|
}
|
|
if cpuDie.LogicalProcessors.Contains(c.LogicalProcessors[0]) {
|
|
cpuDie.CoreThreadIdMaps[c.LogicalProcessors[0]] = c.LogicalProcessors[1]
|
|
cpuDie.CoreThreadIdMaps[c.LogicalProcessors[1]] = c.LogicalProcessors[0]
|
|
}
|
|
}
|
|
|
|
node.CpuCount += cpuDie.LogicalProcessors.Size()
|
|
node.LogicalProcessors = node.LogicalProcessors.Union(cpuDie.LogicalProcessors)
|
|
cpuDies = append(cpuDies, cpuDie)
|
|
|
|
// TODO: add cpu core builder
|
|
}
|
|
if !hasL3Cache {
|
|
cpuDie := new(CPUDie)
|
|
dieBuilder := cpuset.NewBuilder()
|
|
for j := 0; j < len(info.Nodes[i].Cores); j++ {
|
|
for k := 0; k < len(info.Nodes[i].Cores[j].LogicalProcessors); k++ {
|
|
if reservedCpus != nil && reservedCpus.Contains(info.Nodes[i].Cores[j].LogicalProcessors[k]) {
|
|
continue
|
|
}
|
|
dieBuilder.Add(info.Nodes[i].Cores[j].LogicalProcessors[k])
|
|
}
|
|
}
|
|
cpuDie.LogicalProcessors = dieBuilder.Result()
|
|
node.CpuCount += cpuDie.LogicalProcessors.Size()
|
|
node.LogicalProcessors = node.LogicalProcessors.Union(cpuDie.LogicalProcessors)
|
|
cpuDies = append(cpuDies, cpuDie)
|
|
}
|
|
|
|
hasL3Cache = false
|
|
node.CpuDies = cpuDies
|
|
hostTopo.Nodes[i] = node
|
|
}
|
|
hostTopo.CPUCmtbound = desc.CPUCmtbound
|
|
hostTopo.NumaEnabled = numaEnabled
|
|
hostTopo.HostName = desc.Name
|
|
|
|
desc.HostTopo = hostTopo
|
|
//log.Infof("host topo %s", jsonutils.Marshal(hostTopo))
|
|
|
|
sort.Sort(desc.HostTopo)
|
|
desc.EnableCpuNumaAllocate = true
|
|
return nil
|
|
}
|
|
|
|
type ReservedResource struct {
|
|
CPUCount int64 `json:"cpu_count"`
|
|
MemorySize int64 `json:"memory_size"`
|
|
StorageSize int64 `json:"storage_size"`
|
|
}
|
|
|
|
func NewReservedResource(cpu, mem, storage int64) *ReservedResource {
|
|
return &ReservedResource{
|
|
CPUCount: cpu,
|
|
MemorySize: mem,
|
|
StorageSize: storage,
|
|
}
|
|
}
|
|
|
|
func NewGuestReservedResourceByBuilder(b *HostBuilder, host *computemodels.SHost) (ret *ReservedResource) {
|
|
ret = NewReservedResource(0, 0, 0)
|
|
//isoDevs := b.getUnusedIsolatedDevices(host.ID)
|
|
isoDevs := b.getIsolatedDevices(host.Id)
|
|
if len(isoDevs) == 0 {
|
|
return
|
|
}
|
|
|
|
reservedResource := host.GetDevsReservedResourceByDevStats(isoDevs)
|
|
if reservedResource != nil {
|
|
ret.CPUCount = int64(*reservedResource.ReservedCpu)
|
|
ret.MemorySize = int64(*reservedResource.ReservedMemory)
|
|
ret.StorageSize = int64(*reservedResource.ReservedStorage)
|
|
}
|
|
return
|
|
}
|
|
|
|
func NewGuestReservedResourceUsedByBuilder(b *HostBuilder, host *computemodels.SHost, free *ReservedResource) (ret *ReservedResource, err error) {
|
|
ret = NewReservedResource(0, 0, 0)
|
|
gst := b.getIsolatedDeviceGuests(host.Id)
|
|
if len(gst) == 0 {
|
|
return
|
|
}
|
|
var (
|
|
cpu int64 = 0
|
|
mem int64 = 0
|
|
disk int64 = 0
|
|
)
|
|
guestDiskSize := func(g *computemodels.SGuest, onlyLocal bool) int {
|
|
size := 0
|
|
disks, _ := g.GetDisks()
|
|
for _, disk := range disks {
|
|
if !onlyLocal || disk.IsLocal() {
|
|
size += disk.DiskSize
|
|
}
|
|
}
|
|
return size
|
|
}
|
|
for _, g := range gst {
|
|
dSize := guestDiskSize(&g, true)
|
|
disk += int64(dSize)
|
|
if o.Options.IgnoreNonrunningGuests && (g.Status == computeapi.VM_READY) {
|
|
continue
|
|
}
|
|
cpu += int64(g.VcpuCount)
|
|
mem += int64(g.VmemSize)
|
|
}
|
|
usedF := func(used, free int64) int64 {
|
|
if used <= free {
|
|
return used
|
|
}
|
|
return free
|
|
}
|
|
ret.CPUCount = usedF(cpu, free.CPUCount)
|
|
ret.MemorySize = usedF(mem, free.MemorySize)
|
|
ret.StorageSize = usedF(disk, free.StorageSize)
|
|
return
|
|
}
|
|
|
|
func (h *HostDesc) String() string {
|
|
s, _ := json.Marshal(h)
|
|
return string(s)
|
|
}
|
|
|
|
func (h *HostDesc) Type() int {
|
|
// Guest type
|
|
return 0
|
|
}
|
|
|
|
func (h *HostDesc) Getter() core.CandidatePropertyGetter {
|
|
return newHostGetter(h)
|
|
}
|
|
|
|
func (h *HostDesc) GetGuestCount() int64 {
|
|
return h.GuestCount
|
|
}
|
|
|
|
func (h *HostDesc) GetTotalLocalStorageSize(useRsvd bool) int64 {
|
|
return h.totalStorageSize(true, useRsvd)
|
|
}
|
|
|
|
func (h *HostDesc) GetFreeLocalStorageSize(useRsvd bool) int64 {
|
|
return h.freeStorageSize(true, useRsvd)
|
|
}
|
|
|
|
func (h *HostDesc) totalStorageSize(onlyLocal, useRsvd bool) int64 {
|
|
|
|
total := int64(0)
|
|
for _, storage := range h.Storages {
|
|
if !onlyLocal || storage.IsLocal() {
|
|
total += int64(storage.GetCapacity())
|
|
}
|
|
}
|
|
|
|
if onlyLocal {
|
|
return reservedResourceMinusCal(total, h.GuestReservedResource.StorageSize, useRsvd)
|
|
}
|
|
return total
|
|
}
|
|
|
|
func (h *HostDesc) freeStorageSize(onlyLocal, useRsvd bool) int64 {
|
|
total := int64(0)
|
|
for _, storage := range h.Storages {
|
|
if !onlyLocal || storage.IsLocal() {
|
|
total += int64(storage.FreeCapacity)
|
|
}
|
|
}
|
|
|
|
total = total + h.GuestReservedResourceUsed.StorageSize - h.GetReservedStorageSize()
|
|
sizeSub := h.GuestReservedResource.StorageSize - h.GuestReservedResourceUsed.StorageSize
|
|
if sizeSub < 0 {
|
|
total += sizeSub
|
|
}
|
|
if useRsvd {
|
|
return reservedResourceAddCal(total, h.GuestReservedStorageSizeFree(), useRsvd)
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
func (h *HostDesc) GetFreeStorageSizeOfType(sType string, mediumType string, useRsvd bool, reqMaxSize int64) (int64, int64, error) {
|
|
return h.freeStorageSizeOfType(sType, mediumType, useRsvd, reqMaxSize)
|
|
}
|
|
|
|
func (h *HostDesc) freeStorageSizeOfType(storageType string, mediumType string, useRsvd bool, reqMaxSize int64) (int64, int64, error) {
|
|
var total int64
|
|
var actualTotal int64
|
|
foundLEReqStore := false
|
|
errs := make([]error, 0)
|
|
|
|
for _, storage := range h.Storages {
|
|
if IsStorageBackendMediumMatch(storage, storageType, mediumType) {
|
|
total += int64(storage.FreeCapacity)
|
|
actualTotal += int64(storage.ActualFreeCapacity)
|
|
if err := checkStorageSize(storage, reqMaxSize, useRsvd); err != nil {
|
|
errs = append(errs, err)
|
|
} else {
|
|
foundLEReqStore = true
|
|
}
|
|
}
|
|
}
|
|
if utils.IsLocalStorage(storageType) {
|
|
total = total + h.GuestReservedResourceUsed.StorageSize - h.GetReservedStorageSize()
|
|
sizeSub := h.GuestReservedResource.StorageSize - h.GuestReservedResourceUsed.StorageSize
|
|
if sizeSub < 0 {
|
|
total += sizeSub
|
|
}
|
|
}
|
|
if !foundLEReqStore {
|
|
return 0, 0, errors.NewAggregate(errs)
|
|
}
|
|
|
|
if useRsvd {
|
|
return reservedResourceAddCal(total, h.GuestReservedStorageSizeFree(), useRsvd), actualTotal, nil
|
|
}
|
|
|
|
return total - int64(h.GetPendingUsage().DiskUsage.Get(storageType)), actualTotal, nil
|
|
}
|
|
|
|
func (h *HostDesc) GetFreePort(netId string) int {
|
|
freeCnt := h.BaseHostDesc.GetFreePort(netId)
|
|
return freeCnt - schedmodels.HostPendingUsageManager.GetNetPendingUsage(netId)
|
|
}
|
|
|
|
func reservedResourceCal(
|
|
curRes, rsvdRes int64,
|
|
useRsvd, minusRsvd bool,
|
|
) int64 {
|
|
actRes := curRes
|
|
if useRsvd {
|
|
if minusRsvd {
|
|
actRes -= rsvdRes
|
|
} else {
|
|
actRes += rsvdRes
|
|
}
|
|
}
|
|
return actRes
|
|
}
|
|
|
|
func reservedResourceAddCal(curRes, rsvdRes int64, useRsvd bool) int64 {
|
|
return reservedResourceCal(curRes, rsvdRes, useRsvd, false)
|
|
}
|
|
|
|
func reservedResourceMinusCal(curRes, rsvdRes int64, useRsvd bool) int64 {
|
|
return reservedResourceCal(curRes, rsvdRes, useRsvd, true)
|
|
}
|
|
|
|
func (h *HostDesc) GetTotalMemSize(useRsvd bool) int64 {
|
|
return reservedResourceMinusCal(h.TotalMemSize, h.GuestReservedResource.MemorySize, useRsvd)
|
|
}
|
|
|
|
func (h *HostDesc) GetFreeMemSize(useRsvd bool) int64 {
|
|
return reservedResourceAddCal(h.FreeMemSize, h.GuestReservedMemSizeFree(), useRsvd) - int64(h.GetPendingUsage().Memory)
|
|
}
|
|
|
|
func (h *HostDesc) GetFreeCpuNuma() scheduler.SortedFreeNumaCpuMam {
|
|
if !h.EnableCpuNumaAllocate {
|
|
return nil
|
|
}
|
|
|
|
res := make(scheduler.SortedFreeNumaCpuMam, 0)
|
|
cpuPin := h.GetPendingUsage().CpuPin
|
|
numaPin := h.GetPendingUsage().NumaMemPin
|
|
for i := range h.HostTopo.Nodes {
|
|
nodeFree := new(scheduler.SFreeNumaCpuMem)
|
|
nodeFree.NodeId = h.HostTopo.Nodes[i].NodeId
|
|
nodeFree.CpuCount = h.HostTopo.Nodes[i].CpuCount
|
|
nodeFree.MemSize = h.HostTopo.Nodes[i].NumaNodeFreeMemSizeKB / 1024
|
|
nodeFree.EnableNumaAllocate = h.HostTopo.NumaEnabled
|
|
nodeFree.FreeCpuCount = int(float32(h.HostTopo.Nodes[i].CpuCount)*h.CPUCmtbound) - h.HostTopo.Nodes[i].VcpuCount
|
|
for cpuId, pending := range cpuPin {
|
|
if h.HostTopo.Nodes[i].LogicalProcessors.Contains(cpuId) {
|
|
nodeFree.FreeCpuCount -= pending
|
|
}
|
|
}
|
|
|
|
if memSize, ok := numaPin[h.HostTopo.Nodes[i].NodeId]; ok {
|
|
nodeFree.MemSize -= memSize
|
|
}
|
|
res = append(res, nodeFree)
|
|
}
|
|
sort.Sort(res)
|
|
return res
|
|
}
|
|
|
|
func (h *HostDesc) GuestReservedMemSizeFree() int64 {
|
|
return h.GuestReservedResource.MemorySize - h.GuestReservedResourceUsed.MemorySize
|
|
}
|
|
|
|
func (h *HostDesc) GuestReservedCPUCountFree() int64 {
|
|
return h.GuestReservedResource.CPUCount - h.GuestReservedResourceUsed.CPUCount
|
|
}
|
|
|
|
func (h *HostDesc) GuestReservedStorageSizeFree() int64 {
|
|
return h.GuestReservedResource.StorageSize - h.GuestReservedResourceUsed.StorageSize
|
|
}
|
|
|
|
func (h *HostDesc) GetReservedMemSize() int64 {
|
|
return h.GuestReservedResource.MemorySize + int64(h.MemReserved)
|
|
}
|
|
|
|
func (h *HostDesc) GetReservedCPUCount() int64 {
|
|
return h.GuestReservedResource.CPUCount + int64(h.CpuReserved)
|
|
}
|
|
|
|
func (h *HostDesc) GetReservedStorageSize() int64 {
|
|
return h.GuestReservedResource.StorageSize
|
|
}
|
|
|
|
func (h *HostDesc) GetTotalCPUCount(useRsvd bool) int64 {
|
|
return reservedResourceMinusCal(h.TotalCPUCount, h.GuestReservedResource.CPUCount, useRsvd)
|
|
}
|
|
|
|
func (h *HostDesc) GetFreeCPUCount(useRsvd bool) int64 {
|
|
return reservedResourceAddCal(h.FreeCPUCount, h.GuestReservedCPUCountFree(), useRsvd) - int64(h.GetPendingUsage().Cpu)
|
|
}
|
|
|
|
func (h *HostDesc) IndexKey() string {
|
|
return h.Id
|
|
}
|
|
|
|
func (h *HostDesc) AllocCpuNumaPin(vcpuCount, memSizeKB int, preferNumaNodes []int) []scheduler.SCpuNumaPin {
|
|
if !h.EnableCpuNumaAllocate {
|
|
return nil
|
|
}
|
|
|
|
hostTopo := h.HostTopo
|
|
pendingUsage := h.GetPendingUsage()
|
|
if len(pendingUsage.CpuPin) > 0 || len(pendingUsage.NumaMemPin) > 0 {
|
|
hostTopo = HostTopoSubPendingUsage(h.HostTopo, pendingUsage.CpuPin, pendingUsage.NumaMemPin)
|
|
}
|
|
ignoreMemSingular := h.HostType == computeapi.HOST_TYPE_CONTAINER && o.Options.ContainerNumaAllocate
|
|
return hostTopo.AllocCpuNumaNodes(vcpuCount, memSizeKB, ignoreMemSingular, preferNumaNodes)
|
|
}
|
|
|
|
func (h *HostDesc) AllocCpuNumaPinWithNodeCount(vcpuCount, memSizeKB, nodeCount int) []scheduler.SCpuNumaPin {
|
|
if !h.EnableCpuNumaAllocate {
|
|
return nil
|
|
}
|
|
hostTopo := h.HostTopo
|
|
pendingUsage := h.GetPendingUsage()
|
|
if len(pendingUsage.CpuPin) > 0 || len(pendingUsage.NumaMemPin) > 0 {
|
|
hostTopo = HostTopoSubPendingUsage(h.HostTopo, pendingUsage.CpuPin, pendingUsage.NumaMemPin)
|
|
}
|
|
|
|
return hostTopo.AllocCpuNumaNodesWithNodeCount(vcpuCount, memSizeKB, nodeCount)
|
|
}
|
|
|
|
type WaitGroupWrapper struct {
|
|
gosync.WaitGroup
|
|
}
|
|
|
|
func (w *WaitGroupWrapper) Wrap(cb func()) {
|
|
w.Add(1)
|
|
go func() {
|
|
cb()
|
|
w.Done()
|
|
}()
|
|
}
|
|
|
|
func waitTimeOut(wg *WaitGroupWrapper, timeout time.Duration) bool {
|
|
ch := make(chan struct{})
|
|
go func() {
|
|
wg.Wait()
|
|
close(ch)
|
|
}()
|
|
select {
|
|
case <-ch:
|
|
return true
|
|
case <-time.After(timeout):
|
|
return false
|
|
}
|
|
}
|
|
|
|
type HostBuilder struct {
|
|
*baseBuilder
|
|
|
|
residentTenantDict map[string]map[string]interface{}
|
|
|
|
guests []computemodels.SGuest
|
|
guestDict map[string]interface{}
|
|
guestIDs []string
|
|
|
|
hostStorages []computemodels.SHoststorage
|
|
//hostStoragesDict map[string][]*computemodels.SStorage
|
|
storages []interface{}
|
|
storageStatesSizeDict map[string]map[string]interface{}
|
|
|
|
hostGuests map[string][]interface{}
|
|
hostBackupGuests map[string][]interface{}
|
|
|
|
//groupGuests []interface{}
|
|
//groups []interface{}
|
|
//groupDict map[string]interface{}
|
|
//hostGroupCountDict HostGroupCountDict
|
|
|
|
//hostMetadatas []interface{}
|
|
//hostMetadatasDict map[string][]interface{}
|
|
//guestMetadatas []interface{}
|
|
//guestMetadatasDict map[string][]interface{}
|
|
|
|
//diskStats []models.StorageCapacity
|
|
// isolatedDevicesDict map[string][]interface{}
|
|
|
|
cpuIOLoads map[string]map[string]float64
|
|
}
|
|
|
|
func newHostBuilder() *HostBuilder {
|
|
builder := new(HostBuilder)
|
|
builder.baseBuilder = newBaseBuilder(HostDescBuilder, builder)
|
|
return builder
|
|
}
|
|
|
|
func (b *HostBuilder) FetchHosts(ids []string) ([]computemodels.SHost, error) {
|
|
hosts := computemodels.HostManager.Query()
|
|
q := hosts.In("id", ids).NotEquals("host_type", computeapi.HOST_TYPE_BAREMETAL)
|
|
hostObjs := make([]computemodels.SHost, 0)
|
|
err := computedb.FetchModelObjects(computemodels.HostManager, q, &hostObjs)
|
|
return hostObjs, err
|
|
}
|
|
|
|
func (b *HostBuilder) setGuests(hosts []computemodels.SHost, errMessageChannel chan error) {
|
|
idsQuery := b.AllIDsQuery()
|
|
guests, err := FetchGuestByHostIDsQuery(idsQuery)
|
|
if err != nil {
|
|
errMessageChannel <- err
|
|
return
|
|
}
|
|
guestIDs := make([]string, len(guests))
|
|
func() {
|
|
for i, gst := range guests {
|
|
guestIDs[i] = gst.GetId()
|
|
}
|
|
}()
|
|
|
|
hostGuests, err := utils.GroupBy(guests, func(obj interface{}) (string, error) {
|
|
gst, ok := obj.(computemodels.SGuest)
|
|
if !ok {
|
|
return "", utils.ConvertError(obj, "computemodels.SGuest")
|
|
}
|
|
return gst.HostId, nil
|
|
})
|
|
if err != nil {
|
|
errMessageChannel <- err
|
|
return
|
|
}
|
|
|
|
hostBackupGuests, err := utils.GroupBy(guests, func(obj interface{}) (string, error) {
|
|
gst, ok := obj.(computemodels.SGuest)
|
|
if !ok {
|
|
return "", utils.ConvertError(obj, "computemodels.SGuest")
|
|
}
|
|
return gst.BackupHostId, nil
|
|
})
|
|
if err != nil {
|
|
errMessageChannel <- err
|
|
return
|
|
}
|
|
|
|
guestDict, err := utils.ToDict(guests, func(obj interface{}) (string, error) {
|
|
gst, ok := obj.(computemodels.SGuest)
|
|
if !ok {
|
|
return "", utils.ConvertError(obj, "computemodels.SGuest")
|
|
}
|
|
return gst.GetId(), nil
|
|
})
|
|
if err != nil {
|
|
errMessageChannel <- err
|
|
return
|
|
}
|
|
b.guestIDs = guestIDs
|
|
b.guests = guests
|
|
b.hostGuests = hostGuests
|
|
b.hostBackupGuests = hostBackupGuests
|
|
b.guestDict = guestDict
|
|
return
|
|
}
|
|
|
|
//func (b *HostBuilder) setGroupInfo(errMessageChannel chan error) {
|
|
//groupGuests, err := models.FetchByGuestIDs(models.GroupGuests, b.guestIDs)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
|
|
//groupIds, err := utils.SelectDistinct(groupGuests, func(obj interface{}) (string, error) {
|
|
//g, ok := obj.(*models.GroupGuest)
|
|
//if !ok {
|
|
//return "", utils.ConvertError(obj, "*models.GroupGuest")
|
|
//}
|
|
//return g.GroupID, nil
|
|
//})
|
|
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
|
|
//groups, err := models.FetchGroupByIDs(groupIds)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
|
|
//groupDict, err := utils.ToDict(groups, func(obj interface{}) (string, error) {
|
|
//grp, ok := obj.(*models.Group)
|
|
//if !ok {
|
|
//return "", utils.ConvertError(obj, "*models.Group")
|
|
//}
|
|
//return grp.ID, nil
|
|
//})
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
//b.groups = groups
|
|
//b.groupDict = groupDict
|
|
//b.groupGuests = groupGuests
|
|
//hostGroupCountDict, err := b.toHostGroupCountDict(groupGuests)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
//b.hostGroupCountDict = hostGroupCountDict
|
|
//return
|
|
//}
|
|
|
|
//type GroupCount struct {
|
|
//ID string `json:"id"` // group id
|
|
//Name string `json:"name"` // group name
|
|
//Count int64 `json:"count"` // guest count
|
|
//}
|
|
|
|
//type GroupCounts struct {
|
|
//Data map[string]*GroupCount `json:"data"` // group_id: group_count
|
|
//}
|
|
|
|
//func NewGroupCounts() *GroupCounts {
|
|
//return &GroupCounts{
|
|
//Data: make(map[string]*GroupCount),
|
|
//}
|
|
//}
|
|
|
|
//type HostGroupCountDict map[string]*GroupCounts
|
|
|
|
//func (b *HostBuilder) toHostGroupCountDict(groupGuests []interface{}) (HostGroupCountDict, error) {
|
|
//d := make(map[string]*GroupCounts)
|
|
//for _, groupGuestObj := range groupGuests {
|
|
//groupGuest := groupGuestObj.(*models.GroupGuest)
|
|
//groupObj, grpOK := b.groupDict[groupGuest.GroupID]
|
|
//guestObj, gstOK := b.guestDict[*groupGuest.GuestID]
|
|
//if !grpOK || !gstOK {
|
|
//continue
|
|
//}
|
|
//hostObj, ok := b.hostDict[guestObj.(*models.Guest).HostID]
|
|
//if !ok {
|
|
//continue
|
|
//}
|
|
//host := hostObj.(*models.Host)
|
|
//group := groupObj.(*models.Group)
|
|
|
|
//counts, ok := d[host.ID]
|
|
//if !ok {
|
|
//counts = NewGroupCounts()
|
|
//d[host.ID] = counts
|
|
//}
|
|
//count, ok := counts.Data[group.ID]
|
|
//if !ok {
|
|
//count = &GroupCount{ID: group.ID, Name: group.Name, Count: 1}
|
|
//counts.Data[group.ID] = count
|
|
//} else {
|
|
//count.Count++
|
|
//}
|
|
//counts.Data[host.ID] = count
|
|
//}
|
|
//return d, nil
|
|
//}
|
|
|
|
//func (b *HostBuilder) setMetadataInfo(hostIDs []string, errMessageChannel chan error) {
|
|
//hostMetadataNames := []string{"dynamic_load_cpu_percent", "dynamic_load_io_util",
|
|
//"enable_sriov", "bridge_driver"}
|
|
//hostMetadataNames = append(hostMetadataNames, models.HostExtraFeature...)
|
|
//hostMetadatas, err := models.FetchMetadatas(models.HostResourceName, hostIDs, hostMetadataNames)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
//guestMetadataNames := []string{"app_tags"}
|
|
//guestMetadatas, err := models.FetchMetadatas(models.GuestResourceName, b.guestIDs, guestMetadataNames)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
//idFunc := func(obj interface{}) (string, error) {
|
|
//metadata, ok := obj.(*models.Metadata)
|
|
//if !ok {
|
|
//return "", utils.ConvertError(obj, "*models.Metadata")
|
|
//}
|
|
//id := strings.Split(metadata.ID, "::")[1]
|
|
//return id, nil
|
|
//}
|
|
//hostMetadatasDict, err := utils.GroupBy(hostMetadatas, idFunc)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
//guestMetadatasDict, err := utils.GroupBy(guestMetadatas, idFunc)
|
|
//if err != nil {
|
|
//errMessageChannel <- err
|
|
//return
|
|
//}
|
|
//b.hostMetadatas = hostMetadatas
|
|
//b.hostMetadatasDict = hostMetadatasDict
|
|
//b.guestMetadatas = guestMetadatas
|
|
//b.guestMetadatasDict = guestMetadatasDict
|
|
//return
|
|
//}
|
|
|
|
/*func (b *HostBuilder) setDiskStats(errMessageChannel chan error) {
|
|
storageIDs := make([]string, len(b.storages))
|
|
func() {
|
|
for i, s := range b.storages {
|
|
storageIDs[i] = s.(*models.Storage).ID
|
|
}
|
|
}()
|
|
capacities, err := models.GetStorageCapacities(storageIDs)
|
|
stat3 := make([]utils.StatItem3, len(capacities))
|
|
for i, item := range capacities {
|
|
stat3[i] = item
|
|
}
|
|
if err != nil {
|
|
errMessageChannel <- err
|
|
return
|
|
}
|
|
storageStatesSizeDict, _ := utils.ToStatDict3(stat3)
|
|
b.storageStatesSizeDict = storageStatesSizeDict
|
|
b.diskStats = capacities
|
|
return
|
|
}*/
|
|
|
|
func (b *HostBuilder) Clone() BuildActor {
|
|
return newHostBuilder()
|
|
}
|
|
|
|
func (b *HostBuilder) AllIDs() ([]string, error) {
|
|
q := computemodels.HostManager.Query("id")
|
|
q = q.Filter(sqlchemy.NotEquals(q.Field("host_type"), computeapi.HOST_TYPE_BAREMETAL))
|
|
return FetchModelIds(q)
|
|
}
|
|
|
|
func (b *HostBuilder) AllIDsQuery() sqlchemy.IQuery {
|
|
q := computemodels.HostManager.Query("id")
|
|
q = q.Filter(sqlchemy.NotEquals(q.Field("host_type"), computeapi.HOST_TYPE_BAREMETAL))
|
|
return q
|
|
}
|
|
|
|
func (b *HostBuilder) InitFuncs() []InitFunc {
|
|
return []InitFunc{
|
|
// b.setSchedtags,
|
|
b.setGuests,
|
|
}
|
|
}
|
|
|
|
// build host desc
|
|
func (b *HostBuilder) BuildOne(host *computemodels.SHost, getter *networkGetter, baseDesc *BaseHostDesc) (interface{}, error) {
|
|
desc := &HostDesc{
|
|
BaseHostDesc: baseDesc,
|
|
}
|
|
|
|
desc.Metadata = make(map[string]string)
|
|
desc.CPUCmtbound = host.GetCPUOvercommitBound()
|
|
desc.MemCmtbound = host.GetMemoryOvercommitBound()
|
|
|
|
desc.GuestReservedResource = NewGuestReservedResourceByBuilder(b, host)
|
|
guestRsvdUsed, err := NewGuestReservedResourceUsedByBuilder(b, host, desc.GuestReservedResource)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
desc.GuestReservedResourceUsed = guestRsvdUsed
|
|
|
|
fillFuncs := []func(*HostDesc, *computemodels.SHost) error{
|
|
//b.fillResidentGroups,
|
|
b.fillMetadata,
|
|
b.fillCPUIOLoads,
|
|
b.fillGuestsCpuNumaPin,
|
|
b.fillGuestsResourceInfo,
|
|
}
|
|
|
|
for _, f := range fillFuncs {
|
|
err := f(desc, host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return desc, nil
|
|
}
|
|
|
|
func (b *HostBuilder) fillGuestsCpuNumaPin(desc *HostDesc, host *computemodels.SHost) error {
|
|
if !host.EnableNumaAllocate {
|
|
return nil
|
|
}
|
|
|
|
topoObj, err := host.SysInfo.Get("topology")
|
|
if err != nil {
|
|
return errors.Wrap(err, "get topology from host sys_info")
|
|
}
|
|
hostTopo := new(hostapi.HostTopology)
|
|
if err := topoObj.Unmarshal(hostTopo); err != nil {
|
|
return errors.Wrap(err, "Unmarshal host topology struct")
|
|
}
|
|
var reservedCpus *cpuset.CPUSet
|
|
reservedCpusStr := host.GetMetadata(context.Background(), computeapi.HOSTMETA_RESERVED_CPUS_INFO, nil)
|
|
if reservedCpusStr != "" {
|
|
reservedCpusJson, err := jsonutils.ParseString(reservedCpusStr)
|
|
if err != nil {
|
|
return errors.Wrap(err, "parse reserved cpus info failed")
|
|
}
|
|
reservedCpusInfo := computeapi.HostReserveCpusInput{}
|
|
err = reservedCpusJson.Unmarshal(&reservedCpusInfo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "unmarshal host reserved cpus info failed")
|
|
}
|
|
reservedCpuset, err := cpuset.Parse(reservedCpusInfo.Cpus)
|
|
if err != nil {
|
|
return errors.Wrap(err, "cpuset parse reserved cpus")
|
|
}
|
|
reservedCpus = &reservedCpuset
|
|
}
|
|
pinnedCpuset, err := host.GetPinnedCpusetCores(context.Background(), nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if pinnedCpuset != nil {
|
|
if reservedCpus == nil {
|
|
reservedCpus = pinnedCpuset
|
|
} else {
|
|
newset := reservedCpus.Union(*pinnedCpuset)
|
|
reservedCpus = &newset
|
|
}
|
|
}
|
|
|
|
nodeHugepages := make([]hostapi.HostNodeHugepageNr, 0)
|
|
if host.SysInfo.Contains("node_hugepages") {
|
|
err = host.SysInfo.Unmarshal(&nodeHugepages, "node_hugepages")
|
|
if err != nil {
|
|
return errors.Wrap(err, "unmarshal node hugepages")
|
|
}
|
|
}
|
|
|
|
hugepageSizeKb, err := host.SysInfo.Int("hugepage_size_kb")
|
|
if err != nil {
|
|
return errors.Wrap(err, "unmarshal hugepage size kb")
|
|
}
|
|
|
|
return b.buildHostTopo(desc, reservedCpus, int(hugepageSizeKb), nodeHugepages, hostTopo)
|
|
}
|
|
|
|
func (b *HostBuilder) fillGuestsResourceInfo(desc *HostDesc, host *computemodels.SHost) error {
|
|
var (
|
|
guestCount int64
|
|
runningCount int64
|
|
memSize int64
|
|
memReqSize int64
|
|
memFakeDeletedSize int64
|
|
cpuCount int64
|
|
cpuReqCount int64
|
|
cpuBoundCount int64
|
|
cpuFakeDeletedCount int64
|
|
ioBoundCount int64
|
|
creatingMemSize int64
|
|
creatingCPUCount int64
|
|
creatingGuestCount int64
|
|
guestsCpuNumaPin = make([]scheduler.SCpuNumaPin, 0)
|
|
)
|
|
guestsOnHost, ok := b.hostGuests[host.Id]
|
|
if !ok {
|
|
guestsOnHost = []interface{}{}
|
|
}
|
|
backupGuestsOnHost, ok := b.hostBackupGuests[host.Id]
|
|
if ok {
|
|
guestsOnHost = append(guestsOnHost, backupGuestsOnHost...)
|
|
}
|
|
|
|
pendingUsage := desc.GetPendingUsage()
|
|
desc.Tenants = make(map[string]int64)
|
|
for _, gst := range guestsOnHost {
|
|
guest := gst.(computemodels.SGuest)
|
|
projectId := guest.ProjectId
|
|
if count, ok := desc.Tenants[projectId]; ok {
|
|
desc.Tenants[projectId] = count + 1
|
|
} else {
|
|
desc.Tenants[projectId] = 1
|
|
}
|
|
if IsGuestPendingDelete(guest) {
|
|
memFakeDeletedSize += int64(guest.VmemSize)
|
|
cpuFakeDeletedCount += int64(guest.VcpuCount)
|
|
} else {
|
|
if _, ok := pendingUsage.PendingGuestIds[guest.Id]; ok {
|
|
log.Infof("fillGuestsResourceInfo guest %s in pending usage", guest.Id)
|
|
continue
|
|
}
|
|
if IsGuestCreating(guest) {
|
|
creatingGuestCount++
|
|
creatingMemSize += int64(guest.VmemSize)
|
|
creatingCPUCount += int64(guest.VcpuCount)
|
|
if guest.IsSchedulerNumaAllocate() {
|
|
cpuNumaPin := make([]scheduler.SCpuNumaPin, 0)
|
|
if err := guest.CpuNumaPin.Unmarshal(&cpuNumaPin); err != nil {
|
|
return errors.Wrap(err, "unmarshal cpu numa pin")
|
|
}
|
|
for i := range cpuNumaPin {
|
|
if cpuNumaPin[i].ExtraCpuCount > 0 {
|
|
creatingCPUCount += int64(cpuNumaPin[i].ExtraCpuCount)
|
|
}
|
|
}
|
|
guestsCpuNumaPin = append(guestsCpuNumaPin, cpuNumaPin...)
|
|
}
|
|
} else if !IsGuestStoppedStatus(guest) {
|
|
// running status
|
|
runningCount++
|
|
memSize += int64(guest.VmemSize)
|
|
cpuCount += int64(guest.VcpuCount)
|
|
if guest.IsSchedulerNumaAllocate() {
|
|
cpuNumaPin := make([]scheduler.SCpuNumaPin, 0)
|
|
if err := guest.CpuNumaPin.Unmarshal(&cpuNumaPin); err != nil {
|
|
return errors.Wrap(err, "unmarshal cpu numa pin")
|
|
}
|
|
for i := range cpuNumaPin {
|
|
if cpuNumaPin[i].ExtraCpuCount > 0 {
|
|
creatingCPUCount += int64(cpuNumaPin[i].ExtraCpuCount)
|
|
}
|
|
}
|
|
guestsCpuNumaPin = append(guestsCpuNumaPin, cpuNumaPin...)
|
|
}
|
|
}
|
|
}
|
|
//if IsGuestRunning(guest) {
|
|
// runningCount++
|
|
// memSize += int64(guest.VmemSize)
|
|
// cpuCount += int64(guest.VcpuCount)
|
|
// if host.EnableNumaAllocate && guest.CpuNumaPin != nil {
|
|
// cpuNumaPin := make([]scheduler.SCpuNumaPin, 0)
|
|
// if err := guest.CpuNumaPin.Unmarshal(&cpuNumaPin); err != nil {
|
|
// return errors.Wrap(err, "unmarshal cpu numa pin")
|
|
// }
|
|
// guestsCpuNumaPin = append(guestsCpuNumaPin, cpuNumaPin...)
|
|
// }
|
|
//} else if IsGuestCreating(guest) {
|
|
// creatingGuestCount++
|
|
// creatingMemSize += int64(guest.VmemSize)
|
|
// creatingCPUCount += int64(guest.VcpuCount)
|
|
// if host.EnableNumaAllocate && guest.CpuNumaPin != nil {
|
|
// cpuNumaPin := make([]scheduler.SCpuNumaPin, 0)
|
|
// if err := guest.CpuNumaPin.Unmarshal(&cpuNumaPin); err != nil {
|
|
// return errors.Wrap(err, "unmarshal cpu numa pin")
|
|
// }
|
|
// guestsCpuNumaPin = append(guestsCpuNumaPin, cpuNumaPin...)
|
|
// }
|
|
//} else if IsGuestPendingDelete(guest) {
|
|
// memFakeDeletedSize += int64(guest.VmemSize)
|
|
// cpuFakeDeletedCount += int64(guest.VcpuCount)
|
|
//}
|
|
|
|
guestCount++
|
|
cpuReqCount += int64(guest.VcpuCount)
|
|
memReqSize += int64(guest.VmemSize)
|
|
|
|
//appTags := b.guestAppTags(guest)
|
|
//for _, tag := range appTags {
|
|
//if tag == "cpu_bound" {
|
|
//cpuBoundCount += int64(guest.VcpuCount)
|
|
//} else if tag == "io_bound" {
|
|
//ioBoundCount++
|
|
//}
|
|
//}
|
|
}
|
|
|
|
if host.EnableNumaAllocate && len(guestsCpuNumaPin) > 0 {
|
|
desc.HostTopo.LoadCpuNumaPin(guestsCpuNumaPin)
|
|
}
|
|
//log.Infof("host %s topo %s", desc.Name, jsonutils.Marshal(desc.HostTopo))
|
|
|
|
desc.GuestCount = guestCount
|
|
desc.CreatingGuestCount = creatingGuestCount
|
|
desc.RunningGuestCount = runningCount
|
|
desc.RunningMemSize = memSize
|
|
desc.RequiredMemSize = memReqSize
|
|
desc.CreatingMemSize = creatingMemSize
|
|
desc.FakeDeletedMemSize = memFakeDeletedSize
|
|
desc.RunningCPUCount = cpuCount
|
|
desc.RequiredCPUCount = cpuReqCount
|
|
desc.CreatingCPUCount = creatingCPUCount
|
|
desc.FakeDeletedCPUCount = cpuFakeDeletedCount
|
|
|
|
desc.TotalMemSize = int64(float32(desc.MemSize) * desc.MemCmtbound)
|
|
desc.TotalCPUCount = int64(float32(desc.CpuCount) * desc.CPUCmtbound)
|
|
|
|
var memFreeSize int64
|
|
var cpuFreeCount int64
|
|
if o.Options.IgnoreNonrunningGuests {
|
|
memFreeSize = desc.TotalMemSize - desc.RunningMemSize - desc.CreatingMemSize
|
|
cpuFreeCount = desc.TotalCPUCount - desc.RunningCPUCount - desc.CreatingCPUCount
|
|
} else {
|
|
memFreeSize = desc.TotalMemSize - desc.RequiredMemSize
|
|
cpuFreeCount = desc.TotalCPUCount - desc.RequiredCPUCount
|
|
if o.Options.IgnoreFakeDeletedGuests {
|
|
memFreeSize += memFakeDeletedSize
|
|
cpuFreeCount += cpuFakeDeletedCount
|
|
}
|
|
}
|
|
|
|
// free memory size calculate
|
|
rsvdUseMem := desc.GuestReservedResourceUsed.MemorySize
|
|
memFreeSize = memFreeSize + rsvdUseMem - desc.GetReservedMemSize()
|
|
memSub := desc.GuestReservedResource.MemorySize - desc.GuestReservedResourceUsed.MemorySize
|
|
if memSub < 0 {
|
|
memFreeSize += memSub
|
|
}
|
|
desc.FreeMemSize = memFreeSize
|
|
|
|
// free cpu count calculate
|
|
rsvdUseCPU := desc.GuestReservedResourceUsed.CPUCount
|
|
cpuFreeCount = cpuFreeCount + rsvdUseCPU - desc.GetReservedCPUCount()
|
|
cpuSub := desc.GuestReservedResource.CPUCount - desc.GuestReservedResourceUsed.CPUCount
|
|
if cpuSub < 0 {
|
|
cpuFreeCount += cpuSub
|
|
}
|
|
desc.FreeCPUCount = cpuFreeCount
|
|
|
|
desc.CPUBoundCount = cpuBoundCount
|
|
desc.IOBoundCount = ioBoundCount
|
|
|
|
return nil
|
|
}
|
|
|
|
/*func (b *HostBuilder) guestAppTags(guest computemodels.SGuest) []string {
|
|
metadatas, ok := b.guestMetadatasDict[guest.GetId()]
|
|
if !ok {
|
|
return []string{}
|
|
}
|
|
for _, obj := range metadatas {
|
|
metadata, ok := obj.(*models.Metadata)
|
|
if !ok {
|
|
log.Errorf("%v", utils.ConvertError(obj, "*models.Metadata"))
|
|
return []string{}
|
|
}
|
|
if metadata.Key == "app_tags" {
|
|
tagsStr := metadata.Value
|
|
if len(tagsStr) > 0 {
|
|
return strings.Split(tagsStr, ",")
|
|
}
|
|
}
|
|
}
|
|
return []string{}
|
|
}
|
|
|
|
func (b *HostBuilder) storageUsedCapacity(storage *models.Storage, ready bool) int64 {
|
|
d, ok := b.storageStatesSizeDict[storage.ID]
|
|
if !ok {
|
|
return 0
|
|
}
|
|
if ready {
|
|
obj, ok := d[models.DiskReady]
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return obj.(int64)
|
|
}
|
|
var total int64
|
|
for status, sizeObj := range d {
|
|
if (status == models.DiskReady && ready) || (status != models.DiskReady && !ready) {
|
|
total += sizeObj.(int64)
|
|
}
|
|
}
|
|
return total
|
|
}
|
|
|
|
func (b *HostBuilder) fillResidentGroups(desc *HostDesc, host *computemodels.SHost) error {
|
|
groups, ok := b.hostGroupCountDict[host.Id]
|
|
if !ok {
|
|
desc.Groups = nil
|
|
return nil
|
|
}
|
|
desc.Groups = groups
|
|
return nil
|
|
}*/
|
|
|
|
func (b *HostBuilder) fillMetadata(desc *HostDesc, host *computemodels.SHost) error {
|
|
metadata, err := host.GetAllMetadata(nil, nil)
|
|
if err != nil {
|
|
log.Errorf("Get host %s metadata: %v", desc.GetId(), err)
|
|
return nil
|
|
}
|
|
desc.Metadata = metadata
|
|
return nil
|
|
}
|
|
|
|
func (b *HostBuilder) getUsedIsolatedDevicesGuests(hostID string) []string {
|
|
guests := make([]string, 0)
|
|
for _, dev := range b.getIsolatedDevices(hostID) {
|
|
if gss, ok := b.isolatedDeviceGuestsDict[dev.Id]; ok {
|
|
guests = append(guests, gss...)
|
|
}
|
|
}
|
|
return guests
|
|
}
|
|
|
|
func (b *HostBuilder) getIsolatedDeviceGuests(hostID string) (guests []computemodels.SGuest) {
|
|
guests = make([]computemodels.SGuest, 0)
|
|
usedGuests := b.getUsedIsolatedDevicesGuests(hostID)
|
|
if len(usedGuests) == 0 {
|
|
return
|
|
}
|
|
ids := sets.NewString()
|
|
for _, guestId := range usedGuests {
|
|
g, ok := b.guestDict[guestId]
|
|
if !ok {
|
|
continue
|
|
}
|
|
guest := g.(computemodels.SGuest)
|
|
if !ids.Has(guest.Id) {
|
|
ids.Insert(guest.Id)
|
|
guests = append(guests, guest)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
//func (b *HostBuilder) getUnusedIsolatedDevices(hostID string) (devs []computemodels.IsolatedDeviceAllocateStat) {
|
|
// devs = make([]computemodels.IsolatedDeviceAllocateStat, 0)
|
|
// for _, dev := range b.getIsolatedDevices(hostID) {
|
|
// if len(dev.GuestId) == 0 {
|
|
// devs = append(devs, dev)
|
|
// }
|
|
// }
|
|
// return
|
|
//}
|
|
|
|
func (b *HostBuilder) fillCPUIOLoads(desc *HostDesc, host *computemodels.SHost) error {
|
|
desc.CPULoad = b.loadByName(host.Id, "cpu_load")
|
|
desc.IOLoad = b.loadByName(host.Id, "io_load")
|
|
return nil
|
|
}
|
|
|
|
func (b *HostBuilder) loadByName(hostID, name string) *float64 {
|
|
if b.cpuIOLoads == nil {
|
|
return nil
|
|
}
|
|
loads, ok := b.cpuIOLoads[hostID]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
value := loads[name]
|
|
if value >= 0.0 && value <= 1.0 {
|
|
return &value
|
|
}
|
|
return nil
|
|
}
|