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  }