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 }