Files
cloudpods/pkg/scheduler/cache/candidate/base.go
2020-03-27 01:20:30 +08:00

554 lines
14 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 (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
computedb "yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
computemodels "yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/scheduler/api"
schedmodels "yunion.io/x/onecloud/pkg/scheduler/models"
)
type BaseHostDesc struct {
*computemodels.SHost
Region *computemodels.SCloudregion `json:"region"`
Zone *computemodels.SZone `json:"zone"`
Cloudprovider *computemodels.SCloudprovider `json:"cloudprovider"`
Cloudaccount *computemodels.SCloudaccount `json:"cloudaccount"`
Networks []*api.CandidateNetwork `json:"networks"`
NetInterfaces map[string][]computemodels.SNetInterface `json:"net_interfaces"`
Storages []*api.CandidateStorage `json:"storages"`
Tenants map[string]int64 `json:"tenants"`
HostSchedtags []computemodels.SSchedtag `json:"schedtags"`
InstanceGroups map[string]*api.CandidateGroup `json:"instance_groups"`
IpmiInfo types.SIPMIInfo `json:"ipmi_info"`
SharedDomains []string `json:"shared_domains"`
}
type baseHostGetter struct {
h *BaseHostDesc
}
func newBaseHostGetter(h *BaseHostDesc) *baseHostGetter {
return &baseHostGetter{h}
}
func (b baseHostGetter) Id() string {
return b.h.GetId()
}
func (b baseHostGetter) Name() string {
return b.h.GetName()
}
func (b baseHostGetter) Zone() *computemodels.SZone {
return b.h.Zone
}
func (b baseHostGetter) Host() *computemodels.SHost {
return b.h.SHost
}
func (b baseHostGetter) Cloudprovider() *computemodels.SCloudprovider {
return b.h.Cloudprovider
}
func (b baseHostGetter) IsPublic() bool {
return b.h.IsPublic
/*provider := b.Cloudprovider()
if provider == nil {
return false
}
account := provider.GetCloudaccount()
if account == nil {
return false
}
return account.ShareMode == computeapi.CLOUD_ACCOUNT_SHARE_MODE_SYSTEM*/
}
func (b baseHostGetter) DomainId() string {
return b.h.DomainId
}
func (b baseHostGetter) PublicScope() string {
return b.h.PublicScope
}
func (b baseHostGetter) SharedDomains() []string {
return b.h.SharedDomains
}
func (b baseHostGetter) Region() *computemodels.SCloudregion {
return b.h.Region
}
func (b baseHostGetter) HostType() string {
return b.h.HostType
}
func (b baseHostGetter) HostSchedtags() []computemodels.SSchedtag {
return b.h.HostSchedtags
}
func (b baseHostGetter) Storages() []*api.CandidateStorage {
return b.h.Storages
}
func (b baseHostGetter) InstanceGroups() map[string]*api.CandidateGroup {
return b.h.InstanceGroups
}
func (b baseHostGetter) GetFreeGroupCount(groupId string) (int, error) {
// Must Be
scg, ok := b.h.InstanceGroups[groupId]
if !ok {
return 0, fmt.Errorf("No such Group id")
}
free := scg.Granularity - scg.ReferCount
if free < 1 {
return 0, nil
}
pendingScg, ok := b.h.GetPendingUsage().InstanceGroupUsage[groupId]
if ok {
free -= pendingScg.ReferCount
}
return free, nil
}
func (b baseHostGetter) Networks() []*api.CandidateNetwork {
return b.h.Networks
}
func (b baseHostGetter) OvnCapable() bool {
return false
}
func (b baseHostGetter) ResourceType() string {
return reviseResourceType(b.h.ResourceType)
}
func (b baseHostGetter) NetInterfaces() map[string][]computemodels.SNetInterface {
return b.h.NetInterfaces
}
func (b baseHostGetter) Status() string {
return b.h.Status
}
func (b baseHostGetter) HostStatus() string {
return b.h.HostStatus
}
func (b baseHostGetter) Enabled() bool {
return b.h.GetEnabled()
}
func (b baseHostGetter) ProjectGuests() map[string]int64 {
return b.h.Tenants
}
func (b baseHostGetter) CreatingGuestCount() int {
return 0
}
func (b baseHostGetter) RunningCPUCount() int64 {
return 0
}
func (b baseHostGetter) TotalCPUCount(_ bool) int64 {
return int64(b.h.CpuCount)
}
func (b baseHostGetter) RunningMemorySize() int64 {
return 0
}
func (b baseHostGetter) TotalMemorySize(_ bool) int64 {
return int64(b.h.MemSize)
}
func (b baseHostGetter) GetFreeStorageSizeOfType(storageType string, useRsvd bool) int64 {
var size int64
for _, s := range b.Storages() {
if s.StorageType == storageType {
size += int64(float32(s.Capacity) * s.Cmtbound)
}
}
return size
}
func (b baseHostGetter) GetFreePort(netId string) int {
return b.h.GetFreePort(netId)
}
func (b baseHostGetter) GetIpmiInfo() types.SIPMIInfo {
return b.h.IpmiInfo
}
func (b baseHostGetter) GetQuotaKeys(s *api.SchedInfo) computemodels.SComputeResourceKeys {
return b.h.getQuotaKeys(s)
}
func reviseResourceType(resType string) string {
if resType == "" {
return computeapi.HostResourceTypeDefault
}
return resType
}
func newBaseHostDesc(host *computemodels.SHost) (*BaseHostDesc, error) {
host.ResourceType = reviseResourceType(host.ResourceType)
desc := &BaseHostDesc{
SHost: host,
}
if err := desc.fillCloudProvider(host); err != nil {
return nil, fmt.Errorf("Fill cloudprovider info error: %v", err)
}
if err := desc.fillNetworks(host); err != nil {
return nil, fmt.Errorf("Fill networks error: %v", err)
}
if err := desc.fillOnecloudVpcNetworks(); err != nil {
return nil, fmt.Errorf("Fill onecloud vpc networks error: %v", err)
}
if err := desc.fillZone(host); err != nil {
return nil, fmt.Errorf("Fill zone error: %v", err)
}
if err := desc.fillRegion(host); err != nil {
return nil, fmt.Errorf("Fill region error: %v", err)
}
if err := desc.fillResidentTenants(host); err != nil {
return nil, fmt.Errorf("Fill resident tenants error: %v", err)
}
if err := desc.fillStorages(host); err != nil {
return nil, fmt.Errorf("Fill storage error: %v", err)
}
if err := desc.fillSchedtags(); err != nil {
return nil, fmt.Errorf("Fill schedtag error: %v", err)
}
if err := desc.fillInstanceGroups(host); err != nil {
return nil, fmt.Errorf("Fill instance group error: %v", err)
}
if err := desc.fillIpmiInfo(host); err != nil {
return nil, fmt.Errorf("Fill ipmi info error: %v", err)
}
desc.fillSharedDomains()
return desc, nil
}
func (b BaseHostDesc) GetSchedDesc() *jsonutils.JSONDict {
desc := jsonutils.Marshal(b.SHost).(*jsonutils.JSONDict)
if b.Cloudprovider != nil {
p := b.Cloudprovider
cloudproviderDesc := jsonutils.NewDict()
cloudproviderDesc.Add(jsonutils.NewString(p.ProjectId), "tenant_id")
cloudproviderDesc.Add(jsonutils.NewString(p.Provider), "provider")
desc.Add(cloudproviderDesc, "cloudprovider")
}
return desc
}
func (b *BaseHostDesc) GetPendingUsage() *schedmodels.SPendingUsage {
usage, err := schedmodels.HostPendingUsageManager.GetPendingUsage(b.GetId())
if err != nil {
return schedmodels.NewPendingUsageBySchedInfo(b.GetId(), nil)
}
return usage
}
func (b *BaseHostDesc) GetFreePort(netId string) int {
var selNet *api.CandidateNetwork = nil
for _, n := range b.Networks {
if n.GetId() == netId {
selNet = n
break
}
}
if selNet == nil {
return 0
}
freeCount, _ := selNet.GetFreeAddressCount()
return freeCount
}
func (b BaseHostDesc) GetResourceType() string {
return b.ResourceType
}
func (b *BaseHostDesc) fillCloudProvider(host *computemodels.SHost) error {
b.Cloudprovider = host.GetCloudprovider()
if b.Cloudprovider != nil {
b.Cloudaccount = b.Cloudprovider.GetCloudaccount()
}
return nil
}
func (b *BaseHostDesc) fillRegion(host *computemodels.SHost) error {
b.Region = host.GetRegion()
return nil
}
func (b *BaseHostDesc) fillZone(host *computemodels.SHost) error {
zone := host.GetZone()
b.Zone = zone
b.ZoneId = host.ZoneId
return nil
}
func (b *BaseHostDesc) fillResidentTenants(host *computemodels.SHost) error {
rets, err := HostResidentTenantCount(host.Id)
if err != nil {
return err
}
b.Tenants = rets
return nil
}
func (b *BaseHostDesc) fillSchedtags() error {
b.HostSchedtags = b.SHost.GetSchedtags()
return nil
}
func (b *BaseHostDesc) fillSharedDomains() error {
b.SharedDomains = b.SHost.GetSharedDomains()
return nil
}
func (b *BaseHostDesc) fillNetworks(host *computemodels.SHost) error {
hostId := host.Id
hostwires := computemodels.HostwireManager.Query().SubQuery()
sq := hostwires.Query(sqlchemy.DISTINCT("wire_id", hostwires.Field("wire_id"))).Equals("host_id", hostId)
networks := computemodels.NetworkManager.Query().SubQuery()
q := networks.Query().In("wire_id", sq)
nets := make([]computemodels.SNetwork, 0)
err := computedb.FetchModelObjects(computemodels.NetworkManager, q, &nets)
if err != nil {
return err
}
b.Networks = make([]*api.CandidateNetwork, len(nets))
for idx, n := range nets {
b.Networks[idx] = &api.CandidateNetwork{
SNetwork: &nets[idx],
Schedtags: n.GetSchedtags(),
}
}
netifs := host.GetNetInterfaces()
netifIndexs := make(map[string][]computemodels.SNetInterface, 0)
for _, netif := range netifs {
if !netif.IsUsableServernic() {
continue
}
wire := netif.GetWire()
if wire == nil {
continue
}
if _, exist := netifIndexs[wire.Id]; !exist {
netifIndexs[wire.Id] = make([]computemodels.SNetInterface, 0)
}
netifIndexs[wire.Id] = append(netifIndexs[wire.Id], netif)
}
b.NetInterfaces = netifIndexs
return nil
}
func (b *BaseHostDesc) fillOnecloudVpcNetworks() error {
nets := computemodels.NetworkManager.Query()
wires := computemodels.WireManager.Query().SubQuery()
vpcs := computemodels.VpcManager.Query().SubQuery()
regions := computemodels.CloudregionManager.Query().SubQuery()
q := nets.AppendField(nets.QueryFields()...)
q = q.AppendField(
vpcs.Field("id", "vpc_id"),
regions.Field("provider"),
)
q = q.Join(wires, sqlchemy.Equals(wires.Field("id"), nets.Field("wire_id")))
q = q.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id")))
q = q.Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id")))
q = q.Filter(sqlchemy.AND(
sqlchemy.Equals(regions.Field("provider"), computeapi.CLOUD_PROVIDER_ONECLOUD),
sqlchemy.NOT(sqlchemy.Equals(vpcs.Field("id"), computeapi.DEFAULT_VPC_ID)),
))
type Row struct {
computemodels.SNetwork
VpcId string
Provider string
}
rows := []Row{}
if err := q.All(&rows); err != nil {
return errors.Wrap(err, "query onecloud vpc networks")
}
for i := range rows {
row := &rows[i]
candidateNet := &api.CandidateNetwork{
SNetwork: &row.SNetwork,
VpcId: row.VpcId,
Provider: row.Provider,
}
b.Networks = append(b.Networks, candidateNet)
}
return nil
}
func (b *BaseHostDesc) fillStorages(host *computemodels.SHost) error {
ss := make([]*api.CandidateStorage, 0)
for _, s := range host.GetHoststorages() {
storage := s.GetStorage()
ss = append(ss, &api.CandidateStorage{
SStorage: storage,
Schedtags: storage.GetSchedtags(),
})
}
b.Storages = ss
return nil
}
func (b *BaseHostDesc) fillInstanceGroups(host *computemodels.SHost) error {
candidateSet := make(map[string]*api.CandidateGroup)
groups, groupSet, err := host.InstanceGroups()
if err != nil {
b.InstanceGroups = candidateSet
return err
}
for i := range groups {
id := groups[i].GetId()
candidateSet[id] = &api.CandidateGroup{
SGroup: &groups[i],
ReferCount: groupSet[id],
}
}
b.InstanceGroups = candidateSet
return nil
}
func (b *BaseHostDesc) fillIpmiInfo(host *computemodels.SHost) error {
info, err := host.GetIpmiInfo()
if err != nil {
return err
}
b.IpmiInfo = info
return nil
}
func (h *BaseHostDesc) GetEnableStatus() string {
if h.GetEnabled() {
return "enable"
}
return "disable"
}
func (h *BaseHostDesc) GetHostType() string {
if h.HostType == api.HostTypeBaremetal && h.IsBaremetal {
return api.HostTypeBaremetal
}
return h.HostType
}
func (h *BaseHostDesc) getQuotaKeys(s *api.SchedInfo) computemodels.SComputeResourceKeys {
computeKeys := computemodels.SComputeResourceKeys{}
computeKeys.DomainId = s.Domain
computeKeys.ProjectId = s.Project
if h.Cloudprovider != nil {
computeKeys.Provider = h.Cloudaccount.Provider
computeKeys.Brand = h.Cloudaccount.Brand
computeKeys.CloudEnv = h.Cloudaccount.GetCloudEnv()
computeKeys.AccountId = h.Cloudaccount.Id
computeKeys.ManagerId = h.Cloudprovider.Id
} else {
computeKeys.Provider = computeapi.CLOUD_PROVIDER_ONECLOUD
computeKeys.Brand = computeapi.ONECLOUD_BRAND_ONECLOUD
computeKeys.CloudEnv = computeapi.CLOUD_ENV_ON_PREMISE
computeKeys.AccountId = ""
computeKeys.ManagerId = ""
}
computeKeys.RegionId = h.Region.Id
computeKeys.ZoneId = h.Zone.Id
computeKeys.Hypervisor = computeapi.HOSTTYPE_HYPERVISOR[h.HostType]
return computeKeys
}
func HostsResidentTenantStats(hostIDs []string) (map[string]map[string]interface{}, error) {
residentTenantStats, err := FetchHostsResidentTenants(hostIDs)
if err != nil {
return nil, err
}
stat3 := make([]utils.StatItem3, len(residentTenantStats))
for i, item := range residentTenantStats {
stat3[i] = item
}
return utils.ToStatDict3(stat3)
}
func HostResidentTenantCount(id string) (map[string]int64, error) {
residentTenantDict, err := HostsResidentTenantStats([]string{id})
if err != nil {
return nil, err
}
tenantMap, ok := residentTenantDict[id]
if !ok {
log.V(10).Infof("Not found host ID: %s when fill resident tenants, may be no guests on it.", id)
return nil, nil
}
rets := make(map[string]int64, len(tenantMap))
for tenantID, countObj := range tenantMap {
rets[tenantID] = countObj.(int64)
}
return rets, nil
}
type DescBuilder struct {
actor BuildActor
}
func NewDescBuilder(act BuildActor) *DescBuilder {
return &DescBuilder{
actor: act,
}
}
func (d *DescBuilder) Build(ids []string) ([]interface{}, error) {
return d.actor.Do(ids)
}