github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/qrm-plugins/network/state/state.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 state 18 19 import ( 20 "encoding/json" 21 "fmt" 22 23 info "github.com/google/cadvisor/info/v1" 24 pluginapi "k8s.io/kubelet/pkg/apis/resourceplugin/v1alpha1" 25 26 "github.com/kubewharf/katalyst-core/pkg/util/general" 27 "github.com/kubewharf/katalyst-core/pkg/util/machine" 28 ) 29 30 type AllocationInfo struct { 31 PodUid string `json:"pod_uid,omitempty"` 32 PodNamespace string `json:"pod_namespace,omitempty"` 33 PodName string `json:"pod_name,omitempty"` 34 ContainerName string `json:"container_name,omitempty"` 35 ContainerType string `json:"container_type,omitempty"` 36 ContainerIndex uint64 `json:"container_index,omitempty"` 37 RampUp bool `json:"ramp_up,omitempty"` 38 PodRole string `json:"pod_role,omitempty"` 39 PodType string `json:"pod_type,omitempty"` 40 Egress uint32 `json:"egress"` 41 Ingress uint32 `json:"ingress"` 42 IfName string `json:"if_name"` // we do not support cross-nic bandwidth 43 NumaNodes machine.CPUSet `json:"numa_node"` // associated numa nodes of the socket connecting to the selected NIC 44 45 Labels map[string]string `json:"labels"` 46 Annotations map[string]string `json:"annotations"` 47 } 48 49 type ( 50 ContainerEntries map[string]*AllocationInfo // Keyed by container name 51 PodEntries map[string]ContainerEntries // Keyed by pod UID 52 ) 53 54 // NICState indicates the status of a NIC, including the capacity/reservation/allocation (in Mbps) 55 type NICState struct { 56 EgressState BandwidthInfo `json:"egress_state"` 57 IngressState BandwidthInfo `json:"ingress_state"` 58 PodEntries PodEntries `json:"pod_entries"` 59 } 60 61 type BandwidthInfo struct { 62 // Per K8s definition: allocatable = capacity - reserved, free = allocatable - allocated 63 // All rates are in unit of Mbps 64 65 // Actual line speed of a NIC. E.g. a 25Gbps NIC's max bandwidth is around 23.5Gbps 66 // It's configurable. Its value = NIC line speed x configured CapacityRate 67 Capacity uint32 68 // Reserved bandwidth on this NIC (e.g. for system components or high priority tasks) 69 // For the sake of safety, we generally keep an overflow buffer and do not allocate all bandwidth to tasks 70 // Thus, both reservations should be set slightly larger than the actual required amount 71 SysReservation uint32 72 Reservation uint32 73 Allocatable uint32 74 Allocated uint32 75 Free uint32 76 } 77 78 type NICMap map[string]*NICState // keyed by NIC name i.e. eth0 79 80 func (ai *AllocationInfo) String() string { 81 if ai == nil { 82 return "" 83 } 84 85 contentBytes, err := json.Marshal(ai) 86 if err != nil { 87 general.LoggerWithPrefix("AllocationInfo.String", general.LoggingPKGFull).Errorf("marshal AllocationInfo failed with error: %v", err) 88 return "" 89 } 90 return string(contentBytes) 91 } 92 93 func (ai *AllocationInfo) Clone() *AllocationInfo { 94 if ai == nil { 95 return nil 96 } 97 98 clone := &AllocationInfo{ 99 PodUid: ai.PodUid, 100 PodNamespace: ai.PodNamespace, 101 PodName: ai.PodName, 102 ContainerName: ai.ContainerName, 103 ContainerType: ai.ContainerType, 104 ContainerIndex: ai.ContainerIndex, 105 RampUp: ai.RampUp, 106 PodRole: ai.PodRole, 107 PodType: ai.PodType, 108 Egress: ai.Egress, 109 Ingress: ai.Ingress, 110 IfName: ai.IfName, 111 NumaNodes: ai.NumaNodes.Clone(), 112 Labels: general.DeepCopyMap(ai.Labels), 113 Annotations: general.DeepCopyMap(ai.Annotations), 114 } 115 116 return clone 117 } 118 119 // CheckMainContainer returns true if the AllocationInfo is for main container 120 func (ai *AllocationInfo) CheckMainContainer() bool { 121 return ai.ContainerType == pluginapi.ContainerType_MAIN.String() 122 } 123 124 // CheckSideCar returns true if the AllocationInfo is for side-car container 125 func (ai *AllocationInfo) CheckSideCar() bool { 126 return ai.ContainerType == pluginapi.ContainerType_SIDECAR.String() 127 } 128 129 func (pe PodEntries) Clone() PodEntries { 130 clone := make(PodEntries) 131 for podUID, containerEntries := range pe { 132 clone[podUID] = make(ContainerEntries) 133 for containerName, allocationInfo := range containerEntries { 134 clone[podUID][containerName] = allocationInfo.Clone() 135 } 136 } 137 return clone 138 } 139 140 func (pe PodEntries) String() string { 141 if pe == nil { 142 return "" 143 } 144 145 contentBytes, err := json.Marshal(pe) 146 if err != nil { 147 general.LoggerWithPrefix("PodEntries.String", general.LoggingPKGFull).Errorf("marshal PodEntries failed with error: %v", err) 148 return "" 149 } 150 return string(contentBytes) 151 } 152 153 // GetMainContainerAllocation returns AllocationInfo that belongs 154 // the main container for this pod 155 func (pe PodEntries) GetMainContainerAllocation(podUID string) (*AllocationInfo, bool) { 156 for _, allocationInfo := range pe[podUID] { 157 if allocationInfo.CheckMainContainer() { 158 return allocationInfo, true 159 } 160 } 161 return nil, false 162 } 163 164 func (ns *NICState) String() string { 165 if ns == nil { 166 return "" 167 } 168 169 contentBytes, err := json.Marshal(ns) 170 if err != nil { 171 general.LoggerWithPrefix("NICState.String", general.LoggingPKGFull).Errorf("marshal NICState failed with error: %v", err) 172 return "" 173 } 174 return string(contentBytes) 175 } 176 177 func (ns *NICState) Clone() *NICState { 178 if ns == nil { 179 return nil 180 } 181 182 return &NICState{ 183 EgressState: BandwidthInfo{ 184 Capacity: ns.EgressState.Capacity, 185 SysReservation: ns.EgressState.SysReservation, 186 Reservation: ns.EgressState.Reservation, 187 Allocatable: ns.EgressState.Allocatable, 188 Allocated: ns.EgressState.Allocated, 189 Free: ns.EgressState.Free, 190 }, 191 IngressState: BandwidthInfo{ 192 Capacity: ns.IngressState.Capacity, 193 SysReservation: ns.IngressState.SysReservation, 194 Reservation: ns.IngressState.Reservation, 195 Allocatable: ns.IngressState.Allocatable, 196 Allocated: ns.IngressState.Allocated, 197 Free: ns.IngressState.Free, 198 }, 199 PodEntries: ns.PodEntries.Clone(), 200 } 201 } 202 203 // SetAllocationInfo adds a new AllocationInfo (for pod/container pairs) into the given NICState 204 func (ns *NICState) SetAllocationInfo(podUID string, containerName string, allocationInfo *AllocationInfo) { 205 if ns == nil { 206 return 207 } 208 209 if allocationInfo == nil { 210 general.LoggerWithPrefix("NICState.SetAllocationInfo", general.LoggingPKGFull).Errorf("passed allocationInfo is nil") 211 return 212 } 213 214 if ns.PodEntries == nil { 215 ns.PodEntries = make(PodEntries) 216 } 217 218 if _, ok := ns.PodEntries[podUID]; !ok { 219 ns.PodEntries[podUID] = make(ContainerEntries) 220 } 221 222 ns.PodEntries[podUID][containerName] = allocationInfo.Clone() 223 } 224 225 func (nm NICMap) Clone() NICMap { 226 clone := make(NICMap) 227 for ifname, ns := range nm { 228 clone[ifname] = ns.Clone() 229 } 230 return clone 231 } 232 233 // EgressBandwidthPerNIC is a helper function to parse egress bandwidth per NIC 234 func (nm NICMap) EgressBandwidthPerNIC() (uint32, error) { 235 if len(nm) == 0 { 236 return 0, fmt.Errorf("getEgressBandwidthPerNICFromMachineState got nil nicMap") 237 } 238 239 for _, nicState := range nm { 240 if nicState != nil { 241 return nicState.EgressState.Allocatable, nil 242 } 243 } 244 245 return 0, fmt.Errorf("getEgressBandwidthPerNICFromMachineState doesn't get valid nicState") 246 } 247 248 // IngressBandwidthPerNIC is a helper function to parse egress bandwidth per NIC 249 func (nm NICMap) IngressBandwidthPerNIC() (uint32, error) { 250 if len(nm) == 0 { 251 return 0, fmt.Errorf("getIngressBandwidthPerNICFromMachineState got nil nicMap") 252 } 253 254 for _, nicState := range nm { 255 if nicState != nil { 256 return nicState.IngressState.Allocatable, nil 257 } 258 } 259 260 return 0, fmt.Errorf("getIngressBandwidthPerNICFromMachineState doesn't get valid nicState") 261 } 262 263 func (nm NICMap) String() string { 264 if nm == nil { 265 return "" 266 } 267 268 contentBytes, err := json.Marshal(nm) 269 if err != nil { 270 general.LoggerWithPrefix("NICMap.String", general.LoggingPKGFull).Errorf("marshal NICMap failed with error: %v", err) 271 return "" 272 } 273 return string(contentBytes) 274 } 275 276 // reader is used to get information from local states 277 type reader interface { 278 GetMachineState() NICMap 279 GetPodEntries() PodEntries 280 GetAllocationInfo(podUID, containerName string) *AllocationInfo 281 } 282 283 // writer is used to store information into local states, 284 // and it also provides functionality to maintain the local files 285 type writer interface { 286 SetMachineState(nicMap NICMap) 287 SetPodEntries(podEntries PodEntries) 288 SetAllocationInfo(podUID, containerName string, allocationInfo *AllocationInfo) 289 290 Delete(podUID, containerName string) 291 ClearState() 292 } 293 294 // ReadonlyState interface only provides methods for tracking pod assignments 295 type ReadonlyState interface { 296 reader 297 298 GetMachineInfo() *info.MachineInfo 299 GetEnabledNICs() []machine.InterfaceInfo 300 GetReservedBandwidth() map[string]uint32 301 } 302 303 // State interface provides methods for tracking and setting pod assignments 304 type State interface { 305 writer 306 ReadonlyState 307 }