github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/cnr_topology.go (about)

     1  /*
     2  Copyright 2022 The Katalyst Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"fmt"
    21  	"sort"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  
    25  	nodev1alpha1 "github.com/kubewharf/katalyst-api/pkg/apis/node/v1alpha1"
    26  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    27  	"github.com/kubewharf/katalyst-core/pkg/util/qos"
    28  )
    29  
    30  // ZoneMeta is a struct that contains the type and name of a zone.
    31  type ZoneMeta struct {
    32  	Type nodev1alpha1.TopologyType
    33  	Name string
    34  }
    35  
    36  // ZoneNode is a struct that contains the meta and an ID of a zone.
    37  type ZoneNode struct {
    38  	Meta ZoneMeta
    39  }
    40  
    41  // ZoneAttributes is list of attributes
    42  type ZoneAttributes []nodev1alpha1.Attribute
    43  
    44  // ZoneAllocations is list of allocations
    45  type ZoneAllocations []*nodev1alpha1.Allocation
    46  
    47  // ZoneSiblings is list of siblings
    48  type ZoneSiblings []nodev1alpha1.Sibling
    49  
    50  // ZoneTopology is a tree diagram of a zone
    51  type ZoneTopology struct {
    52  	Children map[ZoneNode]*ZoneTopology
    53  }
    54  
    55  func NewZoneTopology() *ZoneTopology {
    56  	return &ZoneTopology{
    57  		Children: make(map[ZoneNode]*ZoneTopology),
    58  	}
    59  }
    60  
    61  // TopologyZoneGenerator is a struct that generates a tree diagram of zone,
    62  // it uses AddNode to add new zone node into this tree according to its parent,
    63  // and gets a list of TopologyZone by calling GenerateTopologyZoneStatus with
    64  // the zone information map.
    65  // TopologyZoneGenerator will be used by reporter plugin
    66  type TopologyZoneGenerator struct {
    67  	// rootZoneTopology is root topology of the zone tree
    68  	rootZoneTopology *ZoneTopology
    69  
    70  	// rootZoneTopology is children topology of all zoneNode,
    71  	// it will be used as cache to construct zone tree
    72  	subZoneTopology map[ZoneNode]*ZoneTopology
    73  }
    74  
    75  // NewZoneTopologyGenerator creates a new TopologyZoneGenerator
    76  func NewZoneTopologyGenerator() *TopologyZoneGenerator {
    77  	return &TopologyZoneGenerator{
    78  		rootZoneTopology: NewZoneTopology(),
    79  		subZoneTopology:  make(map[ZoneNode]*ZoneTopology),
    80  	}
    81  }
    82  
    83  // AddNode adds a node to the zone tree,
    84  //   - if parent is nil, it will be added to the root topology
    85  //   - if parent is not nil, it will be added to the sub topology of the parent,
    86  //     the parent must already add into this generator before
    87  func (z *TopologyZoneGenerator) AddNode(parent *ZoneNode, current ZoneNode) error {
    88  	if parent == nil {
    89  		if _, ok := z.rootZoneTopology.Children[current]; !ok {
    90  			newZoneTopology := NewZoneTopology()
    91  			z.rootZoneTopology.Children[current] = newZoneTopology
    92  			z.subZoneTopology[current] = newZoneTopology
    93  		}
    94  	} else {
    95  		// if the zone node has been added into subZoneTopology just skip it,
    96  		// and this requires that we won't add a ZoneNode node twice
    97  		if _, ok := z.subZoneTopology[current]; ok {
    98  			return nil
    99  		}
   100  
   101  		// try to get children topology of parent from subZoneTopology and add current zone node to it,
   102  		// if not found parent in subZoneTopology just return error
   103  		if top, ok := z.subZoneTopology[*parent]; ok {
   104  			newZoneTopology := NewZoneTopology()
   105  			top.Children[current] = newZoneTopology
   106  			z.subZoneTopology[current] = newZoneTopology
   107  		} else {
   108  			return fmt.Errorf("zone node %v parent %v not found", current, parent)
   109  		}
   110  	}
   111  	return nil
   112  }
   113  
   114  // GenerateTopologyZoneStatus generates topology zone status by allocations, resources and attributes
   115  func (z *TopologyZoneGenerator) GenerateTopologyZoneStatus(
   116  	allocationsMap map[ZoneNode]ZoneAllocations,
   117  	resourcesMap map[ZoneNode]nodev1alpha1.Resources,
   118  	attributesMap map[ZoneNode]ZoneAttributes,
   119  	siblingsMap map[ZoneNode]ZoneSiblings,
   120  ) []*nodev1alpha1.TopologyZone {
   121  	return generateTopologyZoneStatus(z.rootZoneTopology, allocationsMap, resourcesMap, attributesMap, siblingsMap)
   122  }
   123  
   124  // generateTopologyZoneStatus generates topology zone status
   125  func generateTopologyZoneStatus(
   126  	zoneTopology *ZoneTopology,
   127  	allocationsMap map[ZoneNode]ZoneAllocations,
   128  	resourcesMap map[ZoneNode]nodev1alpha1.Resources,
   129  	attributesMap map[ZoneNode]ZoneAttributes,
   130  	siblingsMap map[ZoneNode]ZoneSiblings,
   131  ) []*nodev1alpha1.TopologyZone {
   132  	if zoneTopology == nil {
   133  		return nil
   134  	}
   135  
   136  	var result []*nodev1alpha1.TopologyZone
   137  	for zone, topology := range (*zoneTopology).Children {
   138  		topologyZone := &nodev1alpha1.TopologyZone{
   139  			Type: zone.Meta.Type,
   140  			Name: zone.Meta.Name,
   141  		}
   142  
   143  		if resources, ok := resourcesMap[zone]; ok {
   144  			topologyZone.Resources = resources
   145  		}
   146  
   147  		if attributes, ok := attributesMap[zone]; ok {
   148  			// merge attributes to make sure that the attributes are unique and sorted
   149  			topologyZone.Attributes = MergeAttributes(topologyZone.Attributes, attributes)
   150  		}
   151  
   152  		if allocations, ok := allocationsMap[zone]; ok {
   153  			// merge allocations to make sure that the allocations are unique and sorted
   154  			topologyZone.Allocations = MergeAllocations(topologyZone.Allocations, allocations)
   155  		}
   156  
   157  		if siblings, ok := siblingsMap[zone]; ok {
   158  			topologyZone.Siblings = MergeSiblings(topologyZone.Siblings, siblings)
   159  		}
   160  
   161  		if topology != nil {
   162  			zoneChildren := generateTopologyZoneStatus(topology, allocationsMap, resourcesMap, attributesMap, siblingsMap)
   163  			if len(zoneChildren) > 0 {
   164  				topologyZone.Children = zoneChildren
   165  			}
   166  		}
   167  
   168  		result = append(result, topologyZone)
   169  	}
   170  
   171  	sort.SliceStable(result, func(i, j int) bool {
   172  		if result[i].Type == result[j].Type {
   173  			return result[i].Name < result[j].Name
   174  		}
   175  		return result[i].Type < result[j].Type
   176  	})
   177  
   178  	return result
   179  }
   180  
   181  // ValidateSharedCoresWithNumaBindingPod is to check whether zone requests of shared_cores with numa_binding pod is valid
   182  func ValidateSharedCoresWithNumaBindingPod(qosConf *generic.QoSConfiguration, pod *v1.Pod, zoneRequests map[ZoneNode]*v1.ResourceList) (bool, error) {
   183  	sharedQoS, err := qosConf.CheckSharedQoS(pod, map[string]string{})
   184  	if err != nil {
   185  		return false, err
   186  	}
   187  
   188  	if !sharedQoS || !qos.IsPodNumaBinding(qosConf, pod) {
   189  		return false, nil
   190  	}
   191  
   192  	var bindingNumaNode *ZoneNode
   193  	for zoneNode, resourceList := range zoneRequests {
   194  		if zoneNode.Meta.Type != nodev1alpha1.TopologyTypeNuma {
   195  			continue
   196  		}
   197  
   198  		if resourceList != nil &&
   199  			(!resourceList.Cpu().IsZero() || !resourceList.Memory().IsZero()) {
   200  			// check whether cpu or memory are bound to more than one numa node
   201  			if bindingNumaNode != nil {
   202  				return false, fmt.Errorf("shared_cores with numa binding pod cpu or memory " +
   203  					"not support binding more than one numa node")
   204  			}
   205  			bindingNumaNode = &zoneNode
   206  		}
   207  	}
   208  
   209  	if bindingNumaNode == nil {
   210  		return false, fmt.Errorf("shared_cores with numa binding pod without binding numa")
   211  	}
   212  
   213  	return true, nil
   214  }