volcano.sh/volcano@v1.9.0/pkg/scheduler/cache/event_handlers.go (about) 1 /* 2 Copyright 2017 The Kubernetes 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 cache 18 19 import ( 20 "context" 21 "fmt" 22 "math" 23 "reflect" 24 "strconv" 25 26 v1 "k8s.io/api/core/v1" 27 schedulingv1 "k8s.io/api/scheduling/v1" 28 sv1 "k8s.io/api/storage/v1" 29 "k8s.io/apimachinery/pkg/api/errors" 30 "k8s.io/apimachinery/pkg/api/resource" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/util/sets" 33 "k8s.io/client-go/tools/cache" 34 "k8s.io/component-helpers/storage/ephemeral" 35 storagehelpers "k8s.io/component-helpers/storage/volume" 36 "k8s.io/klog/v2" 37 "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology" 38 "k8s.io/kubernetes/pkg/scheduler/framework" 39 volumeutil "k8s.io/kubernetes/pkg/volume/util" 40 "k8s.io/utils/cpuset" 41 42 nodeinfov1alpha1 "volcano.sh/apis/pkg/apis/nodeinfo/v1alpha1" 43 "volcano.sh/apis/pkg/apis/scheduling" 44 "volcano.sh/apis/pkg/apis/scheduling/scheme" 45 schedulingv1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1" 46 "volcano.sh/apis/pkg/apis/utils" 47 48 schedulingapi "volcano.sh/volcano/pkg/scheduler/api" 49 "volcano.sh/volcano/pkg/scheduler/metrics" 50 commonutil "volcano.sh/volcano/pkg/util" 51 ) 52 53 var DefaultAttachableVolumeQuantity int64 = math.MaxInt32 54 55 func isTerminated(status schedulingapi.TaskStatus) bool { 56 return status == schedulingapi.Succeeded || status == schedulingapi.Failed 57 } 58 59 // getOrCreateJob will return corresponding Job for pi if it exists, or it will create a Job and return it if 60 // pi.Pod.Spec.SchedulerName is same as volcano scheduler's name, otherwise it will return nil. 61 func (sc *SchedulerCache) getOrCreateJob(pi *schedulingapi.TaskInfo) *schedulingapi.JobInfo { 62 if len(pi.Job) == 0 { 63 if !commonutil.Contains(sc.schedulerNames, pi.Pod.Spec.SchedulerName) { 64 klog.V(4).Infof("Pod %s/%s will not scheduled by %#v, skip creating PodGroup and Job for it", 65 pi.Pod.Namespace, pi.Pod.Name, sc.schedulerNames) 66 } 67 return nil 68 } 69 70 if _, found := sc.Jobs[pi.Job]; !found { 71 sc.Jobs[pi.Job] = schedulingapi.NewJobInfo(pi.Job) 72 } 73 74 return sc.Jobs[pi.Job] 75 } 76 77 // addPodCSIVolumesToTask counts the csi volumes used by task 78 // @Lily922 TODO: support counting shared volumes. Currently, if two different pods use the same attachable volume 79 // and scheduled on the same nodes the volume will be count twice, but actually only use one attachable limit resource 80 func (sc *SchedulerCache) addPodCSIVolumesToTask(pi *schedulingapi.TaskInfo) error { 81 volumes, err := sc.getPodCSIVolumes(pi.Pod) 82 if err != nil { 83 klog.Errorf("got pod csi attachment persistent volumes count error: %s", err.Error()) 84 return err 85 } 86 87 for key, count := range volumes { 88 pi.Resreq.AddScalar(key, float64(count)) 89 } 90 return nil 91 } 92 93 func (sc *SchedulerCache) getPodCSIVolumes(pod *v1.Pod) (map[v1.ResourceName]int64, error) { 94 volumes := make(map[v1.ResourceName]int64) 95 for _, vol := range pod.Spec.Volumes { 96 pvcName := "" 97 isEphemeral := false 98 switch { 99 case vol.PersistentVolumeClaim != nil: 100 // Normal CSI volume can only be used through PVC 101 pvcName = vol.PersistentVolumeClaim.ClaimName 102 case vol.Ephemeral != nil: 103 // Generic ephemeral inline volumes also use a PVC, 104 // just with a computed name and certain ownership. 105 // That is checked below once the pvc object is 106 // retrieved. 107 pvcName = ephemeral.VolumeClaimName(pod, &vol) 108 isEphemeral = true 109 default: 110 // @Lily922 TODO: support Inline volumes. 111 // Inline volume is not supported now 112 continue 113 } 114 if pvcName == "" { 115 return volumes, fmt.Errorf("PersistentVolumeClaim had no name") 116 } 117 118 pvc, err := sc.pvcInformer.Lister().PersistentVolumeClaims(pod.Namespace).Get(pvcName) 119 if err != nil { 120 // The PVC is required to proceed with 121 // scheduling of a new pod because it cannot 122 // run without it. Bail out immediately. 123 return volumes, fmt.Errorf("looking up PVC %s/%s: %v", pod.Namespace, pvcName, err) 124 } 125 // The PVC for an ephemeral volume must be owned by the pod. 126 if isEphemeral { 127 if err := ephemeral.VolumeIsForPod(pod, pvc); err != nil { 128 return volumes, err 129 } 130 } 131 132 driverName := sc.getCSIDriverInfo(pvc) 133 if sc.isIgnoredProvisioner(driverName) { 134 klog.V(5).InfoS("Provisioner ignored, skip count pod pvc num", "driverName", driverName) 135 continue 136 } 137 if driverName == "" { 138 klog.V(5).InfoS("Could not find a CSI driver name for pvc(%s/%s), not counting volume", pvc.Namespace, pvc.Name) 139 continue 140 } 141 142 // Count all csi volumes in cache, because the storage may change from unattachable volumes to attachable volumes after 143 // the cache set up, in this case it is very difficult to refresh all task caches. 144 // For unattachable volume, set the limits number to a very large value, in this way, scheduling will never 145 // be limited due to insufficient quantity of it. 146 k := v1.ResourceName(volumeutil.GetCSIAttachLimitKey(driverName)) 147 if _, ok := volumes[k]; !ok { 148 volumes[k] = 1 149 } else { 150 volumes[k] += 1 151 } 152 } 153 return volumes, nil 154 } 155 156 func (sc *SchedulerCache) isIgnoredProvisioner(driverName string) bool { 157 return sc.IgnoredCSIProvisioners.Has(driverName) 158 } 159 160 func (sc *SchedulerCache) getCSIDriverInfo(pvc *v1.PersistentVolumeClaim) string { 161 pvName := pvc.Spec.VolumeName 162 163 if pvName == "" { 164 klog.V(5).Infof("PV had no name for pvc <%s>", klog.KObj(pvc)) 165 return sc.getCSIDriverInfoFromSC(pvc) 166 } 167 168 pv, err := sc.pvInformer.Lister().Get(pvName) 169 if err != nil { 170 klog.V(5).InfoS("Failed to get pv <%s> for pvc <%s>: %v", klog.KRef("", pvName), klog.KObj(pvc), err) 171 // If we can't fetch PV associated with PVC, may be it got deleted 172 // or PVC was prebound to a PVC that hasn't been created yet. 173 // fallback to using StorageClass for volume counting 174 return sc.getCSIDriverInfoFromSC(pvc) 175 } 176 177 csiSource := pv.Spec.PersistentVolumeSource.CSI 178 if csiSource == nil { 179 // @Lily922 TODO: support non-CSI volumes and migrating pvc 180 // CSIMigration is not supported now. 181 klog.Warningf("Not support non-csi pvc.") 182 return "" 183 } 184 185 return csiSource.Driver 186 } 187 188 // getCSIDriverInfoFromSC get the name of the csi driver through the sc of the pvc. 189 func (sc *SchedulerCache) getCSIDriverInfoFromSC(pvc *v1.PersistentVolumeClaim) string { 190 scName := storagehelpers.GetPersistentVolumeClaimClass(pvc) 191 192 // If StorageClass is not set or not found, then PVC must be using immediate binding mode 193 // and hence it must be bound before scheduling. So it is safe to not count it. 194 if scName == "" { 195 klog.V(3).Infof("PVC <%s> has no StorageClass", klog.KObj(pvc)) 196 return "" 197 } 198 199 storageClass, err := sc.scInformer.Lister().Get(scName) 200 if err != nil { 201 klog.Errorf("Failed to get StorageClass for pvc <%s>: %v", klog.KObj(pvc), err) 202 return "" 203 } 204 205 if driverName, ok := storageClass.Parameters["csi.storage.k8s.io/csi-driver-name"]; ok { 206 return driverName 207 } 208 return storageClass.Provisioner 209 } 210 211 func (sc *SchedulerCache) addTask(pi *schedulingapi.TaskInfo) error { 212 if len(pi.NodeName) != 0 { 213 if _, found := sc.Nodes[pi.NodeName]; !found { 214 sc.Nodes[pi.NodeName] = schedulingapi.NewNodeInfo(nil) 215 sc.Nodes[pi.NodeName].Name = pi.NodeName 216 } 217 218 node := sc.Nodes[pi.NodeName] 219 if !isTerminated(pi.Status) { 220 if err := node.AddTask(pi); err != nil { 221 return err 222 } 223 } else { 224 klog.V(4).Infof("Pod <%v/%v> is in status %s.", pi.Namespace, pi.Name, pi.Status.String()) 225 } 226 } 227 228 job := sc.getOrCreateJob(pi) 229 if job != nil { 230 job.AddTaskInfo(pi) 231 } 232 233 return nil 234 } 235 236 func (sc *SchedulerCache) NewTaskInfo(pod *v1.Pod) (*schedulingapi.TaskInfo, error) { 237 taskInfo := schedulingapi.NewTaskInfo(pod) 238 if err := sc.addPodCSIVolumesToTask(taskInfo); err != nil { 239 return taskInfo, err 240 } 241 // Update BestEffort because the InitResreq maybe changes 242 taskInfo.BestEffort = taskInfo.InitResreq.IsEmpty() 243 return taskInfo, nil 244 } 245 246 // Assumes that lock is already acquired. 247 func (sc *SchedulerCache) addPod(pod *v1.Pod) error { 248 pi, err := sc.NewTaskInfo(pod) 249 if err != nil { 250 klog.Errorf("generate taskInfo for pod(%s) failed: %v", pod.Name, err) 251 sc.resyncTask(pi) 252 } 253 254 return sc.addTask(pi) 255 } 256 257 func (sc *SchedulerCache) syncTask(oldTask *schedulingapi.TaskInfo) error { 258 newPod, err := sc.kubeClient.CoreV1().Pods(oldTask.Namespace).Get(context.TODO(), oldTask.Name, metav1.GetOptions{}) 259 if err != nil { 260 if errors.IsNotFound(err) { 261 err := sc.deleteTask(oldTask) 262 if err != nil { 263 klog.Errorf("Failed to delete Pod <%v/%v> and remove from cache: %s", oldTask.Namespace, oldTask.Name, err.Error()) 264 return err 265 } 266 klog.V(3).Infof("Pod <%v/%v> was deleted, removed from cache.", oldTask.Namespace, oldTask.Name) 267 268 return nil 269 } 270 return fmt.Errorf("failed to get Pod <%v/%v>: err %v", oldTask.Namespace, oldTask.Name, err) 271 } 272 273 newTask, err := sc.NewTaskInfo(newPod) 274 if err != nil { 275 return fmt.Errorf("failed to generate taskInfo of pod(%s), error: %v", newPod.Name, err) 276 } 277 278 sc.Mutex.Lock() 279 defer sc.Mutex.Unlock() 280 return sc.updateTask(oldTask, newTask) 281 } 282 283 func (sc *SchedulerCache) updateTask(oldTask, newTask *schedulingapi.TaskInfo) error { 284 if err := sc.deleteTask(oldTask); err != nil { 285 klog.Warningf("Failed to delete task: %v", err) 286 } 287 288 return sc.addTask(newTask) 289 } 290 291 // Check the pod allocated status in cache 292 func (sc *SchedulerCache) allocatedPodInCache(pod *v1.Pod) bool { 293 pi := schedulingapi.NewTaskInfo(pod) 294 295 if job, found := sc.Jobs[pi.Job]; found { 296 if t, found := job.Tasks[pi.UID]; found { 297 return schedulingapi.AllocatedStatus(t.Status) 298 } 299 } 300 301 return false 302 } 303 304 // Assumes that lock is already acquired. 305 func (sc *SchedulerCache) updatePod(oldPod, newPod *v1.Pod) error { 306 //ignore the update event if pod is allocated in cache but not present in NodeName 307 if sc.allocatedPodInCache(newPod) && newPod.Spec.NodeName == "" { 308 klog.V(4).Infof("Pod <%s/%v> already in cache with allocated status, ignore the update event", newPod.Namespace, newPod.Name) 309 return nil 310 } 311 312 if err := sc.deletePod(oldPod); err != nil { 313 return err 314 } 315 //when delete pod, the ownerreference of pod will be set nil,just as orphan pod 316 if len(utils.GetController(newPod)) == 0 { 317 newPod.OwnerReferences = oldPod.OwnerReferences 318 } 319 return sc.addPod(newPod) 320 } 321 322 func (sc *SchedulerCache) deleteTask(pi *schedulingapi.TaskInfo) error { 323 var jobErr, nodeErr, numaErr error 324 325 if len(pi.Job) != 0 { 326 if job, found := sc.Jobs[pi.Job]; found { 327 jobErr = job.DeleteTaskInfo(pi) 328 } else { 329 klog.Warningf("Failed to find job <%v> for Task <%v/%v>", pi.Job, pi.Namespace, pi.Name) 330 } 331 } 332 333 if len(pi.NodeName) != 0 { 334 node := sc.Nodes[pi.NodeName] 335 if node != nil { 336 nodeErr = node.RemoveTask(pi) 337 } 338 } 339 340 if jobErr != nil || nodeErr != nil { 341 return schedulingapi.MergeErrors(jobErr, nodeErr, numaErr) 342 } 343 344 return nil 345 } 346 347 // Assumes that lock is already acquired. 348 func (sc *SchedulerCache) deletePod(pod *v1.Pod) error { 349 pi := schedulingapi.NewTaskInfo(pod) 350 351 // Delete the Task in cache to handle Binding status. 352 task := pi 353 if job, found := sc.Jobs[pi.Job]; found { 354 if t, found := job.Tasks[pi.UID]; found { 355 task = t 356 } 357 } 358 if err := sc.deleteTask(task); err != nil { 359 klog.Warningf("Failed to delete task: %v", err) 360 } 361 362 // If job was terminated, delete it. 363 if job, found := sc.Jobs[pi.Job]; found && schedulingapi.JobTerminated(job) { 364 sc.deleteJob(job) 365 } 366 367 return nil 368 } 369 370 // AddPod add pod to scheduler cache 371 func (sc *SchedulerCache) AddPod(obj interface{}) { 372 pod, ok := obj.(*v1.Pod) 373 if !ok { 374 klog.Errorf("Cannot convert to *v1.Pod: %v", obj) 375 return 376 } 377 378 sc.Mutex.Lock() 379 defer sc.Mutex.Unlock() 380 381 err := sc.addPod(pod) 382 if err != nil { 383 klog.Errorf("Failed to add pod <%s/%s> into cache: %v", 384 pod.Namespace, pod.Name, err) 385 return 386 } 387 klog.V(3).Infof("Added pod <%s/%v> into cache.", pod.Namespace, pod.Name) 388 } 389 390 // UpdatePod update pod to scheduler cache 391 func (sc *SchedulerCache) UpdatePod(oldObj, newObj interface{}) { 392 oldPod, ok := oldObj.(*v1.Pod) 393 if !ok { 394 klog.Errorf("Cannot convert oldObj to *v1.Pod: %v", oldObj) 395 return 396 } 397 newPod, ok := newObj.(*v1.Pod) 398 if !ok { 399 klog.Errorf("Cannot convert newObj to *v1.Pod: %v", newObj) 400 return 401 } 402 403 sc.Mutex.Lock() 404 defer sc.Mutex.Unlock() 405 406 err := sc.updatePod(oldPod, newPod) 407 if err != nil { 408 klog.Errorf("Failed to update pod %v in cache: %v", oldPod.Name, err) 409 return 410 } 411 412 klog.V(4).Infof("Updated pod <%s/%v> in cache.", oldPod.Namespace, oldPod.Name) 413 } 414 415 // DeletePod delete pod from scheduler cache 416 func (sc *SchedulerCache) DeletePod(obj interface{}) { 417 var pod *v1.Pod 418 switch t := obj.(type) { 419 case *v1.Pod: 420 pod = t 421 case cache.DeletedFinalStateUnknown: 422 var ok bool 423 pod, ok = t.Obj.(*v1.Pod) 424 if !ok { 425 klog.Errorf("Cannot convert to *v1.Pod: %v", t.Obj) 426 return 427 } 428 default: 429 klog.Errorf("Cannot convert to *v1.Pod: %v", t) 430 return 431 } 432 433 sc.Mutex.Lock() 434 defer sc.Mutex.Unlock() 435 436 err := sc.deletePod(pod) 437 if err != nil { 438 klog.Errorf("Failed to delete pod %v from cache: %v", pod.Name, err) 439 return 440 } 441 442 klog.V(3).Infof("Deleted pod <%s/%v> from cache.", pod.Namespace, pod.Name) 443 } 444 445 // addNodeImageStates adds states of the images on given node to the given nodeInfo and update the imageStates in 446 // scheduler cache. This function assumes the lock to scheduler cache has been acquired. 447 func (sc *SchedulerCache) addNodeImageStates(node *v1.Node, nodeInfo *schedulingapi.NodeInfo) { 448 newSum := make(map[string]*framework.ImageStateSummary) 449 450 for _, image := range node.Status.Images { 451 for _, name := range image.Names { 452 // update the entry in imageStates 453 state, ok := sc.imageStates[name] 454 if !ok { 455 state = &imageState{ 456 size: image.SizeBytes, 457 nodes: sets.NewString(node.Name), 458 } 459 sc.imageStates[name] = state 460 } else { 461 state.nodes.Insert(node.Name) 462 } 463 // create the imageStateSummary for this image 464 if _, ok := newSum[name]; !ok { 465 newSum[name] = sc.createImageStateSummary(state) 466 } 467 } 468 } 469 nodeInfo.ImageStates = newSum 470 } 471 472 // removeNodeImageStates removes the given node record from image entries having the node 473 // in imageStates cache. After the removal, if any image becomes free, i.e., the image 474 // is no longer available on any node, the image entry will be removed from imageStates. 475 func (sc *SchedulerCache) removeNodeImageStates(node string) { 476 for image, state := range sc.imageStates { 477 state.nodes.Delete(node) 478 if len(state.nodes) == 0 { 479 // Remove the unused image to make sure the length of 480 // imageStates represents the total number of different 481 // images on all nodes 482 delete(sc.imageStates, image) 483 } 484 } 485 } 486 487 // AddOrUpdateNode adds or updates node info in cache. 488 func (sc *SchedulerCache) AddOrUpdateNode(node *v1.Node) error { 489 sc.Mutex.Lock() 490 defer sc.Mutex.Unlock() 491 492 if sc.Nodes[node.Name] != nil { 493 sc.Nodes[node.Name].SetNode(node) 494 sc.removeNodeImageStates(node.Name) 495 } else { 496 sc.Nodes[node.Name] = schedulingapi.NewNodeInfo(node) 497 } 498 sc.addNodeImageStates(node, sc.Nodes[node.Name]) 499 500 var nodeExisted bool 501 for _, name := range sc.NodeList { 502 if name == node.Name { 503 nodeExisted = true 504 break 505 } 506 } 507 if !nodeExisted { 508 sc.NodeList = append(sc.NodeList, node.Name) 509 } 510 return nil 511 } 512 513 // RemoveNode removes node info from cache 514 func (sc *SchedulerCache) RemoveNode(nodeName string) error { 515 sc.Mutex.Lock() 516 defer sc.Mutex.Unlock() 517 518 for i, name := range sc.NodeList { 519 if name == nodeName { 520 sc.NodeList = append(sc.NodeList[:i], sc.NodeList[i+1:]...) 521 break 522 } 523 } 524 sc.removeNodeImageStates(nodeName) 525 526 if _, ok := sc.Nodes[nodeName]; !ok { 527 return fmt.Errorf("node <%s> does not exist", nodeName) 528 } 529 530 numaInfo := sc.Nodes[nodeName].NumaInfo 531 if numaInfo != nil { 532 klog.V(3).Infof("delete numatopo <%s/%s>", numaInfo.Namespace, numaInfo.Name) 533 err := sc.vcClient.NodeinfoV1alpha1().Numatopologies().Delete(context.TODO(), numaInfo.Name, metav1.DeleteOptions{}) 534 if err != nil { 535 klog.Errorf("delete numatopo <%s/%s> failed.", numaInfo.Namespace, numaInfo.Name) 536 } 537 } 538 delete(sc.Nodes, nodeName) 539 return nil 540 } 541 542 // AddNode add node to scheduler cache 543 func (sc *SchedulerCache) AddNode(obj interface{}) { 544 node, ok := obj.(*v1.Node) 545 if !ok { 546 klog.Errorf("Cannot convert to *v1.Node: %v", obj) 547 return 548 } 549 sc.nodeQueue.Add(node.Name) 550 } 551 552 // UpdateNode update node to scheduler cache 553 func (sc *SchedulerCache) UpdateNode(oldObj, newObj interface{}) { 554 _, ok := oldObj.(*v1.Node) 555 if !ok { 556 klog.Errorf("Cannot convert oldObj to *v1.Node: %v", oldObj) 557 return 558 } 559 newNode, ok := newObj.(*v1.Node) 560 if !ok { 561 klog.Errorf("Cannot convert newObj to *v1.Node: %v", newObj) 562 return 563 } 564 sc.nodeQueue.Add(newNode.Name) 565 } 566 567 // DeleteNode delete node from scheduler cache 568 func (sc *SchedulerCache) DeleteNode(obj interface{}) { 569 var node *v1.Node 570 switch t := obj.(type) { 571 case *v1.Node: 572 node = t 573 case cache.DeletedFinalStateUnknown: 574 var ok bool 575 node, ok = t.Obj.(*v1.Node) 576 if !ok { 577 klog.Errorf("Cannot convert to *v1.Node: %v", t.Obj) 578 return 579 } 580 default: 581 klog.Errorf("Cannot convert to *v1.Node: %v", t) 582 return 583 } 584 sc.nodeQueue.Add(node.Name) 585 } 586 587 func (sc *SchedulerCache) SyncNode(nodeName string) error { 588 node, err := sc.nodeInformer.Lister().Get(nodeName) 589 if err != nil { 590 if errors.IsNotFound(err) { 591 deleteErr := sc.RemoveNode(nodeName) 592 if deleteErr != nil { 593 klog.Errorf("Failed to delete node <%s> and remove from cache: %s", nodeName, deleteErr.Error()) 594 return deleteErr 595 } 596 597 klog.V(3).Infof("Node <%s> was deleted, removed from cache.", nodeName) 598 return nil 599 } 600 klog.Errorf("Failed to get node %s, error: %v", nodeName, err) 601 return err 602 } 603 604 csiNode, err := sc.csiNodeInformer.Lister().Get(nodeName) 605 if err == nil { 606 sc.setCSIResourceOnNode(csiNode, node) 607 } else if !errors.IsNotFound(err) { 608 return err 609 } 610 return sc.AddOrUpdateNode(node) 611 } 612 613 func (sc *SchedulerCache) AddOrUpdateCSINode(obj interface{}) { 614 csiNode, ok := obj.(*sv1.CSINode) 615 if !ok { 616 return 617 } 618 619 csiNodeStatus := &schedulingapi.CSINodeStatusInfo{ 620 CSINodeName: csiNode.Name, 621 DriverStatus: make(map[string]bool), 622 } 623 sc.Mutex.Lock() 624 defer sc.Mutex.Unlock() 625 626 for i := range csiNode.Spec.Drivers { 627 d := csiNode.Spec.Drivers[i] 628 csiNodeStatus.DriverStatus[d.Name] = d.Allocatable != nil && d.Allocatable.Count != nil 629 } 630 sc.CSINodesStatus[csiNode.Name] = csiNodeStatus 631 sc.nodeQueue.Add(csiNode.Name) 632 } 633 634 func (sc *SchedulerCache) UpdateCSINode(oldObj, newObj interface{}) { 635 oldCSINode, ok := oldObj.(*sv1.CSINode) 636 if !ok { 637 return 638 } 639 newCSINode, ok := newObj.(*sv1.CSINode) 640 if !ok { 641 return 642 } 643 if reflect.DeepEqual(oldCSINode.Spec, newCSINode.Spec) { 644 return 645 } 646 sc.AddOrUpdateCSINode(newObj) 647 } 648 649 func (sc *SchedulerCache) DeleteCSINode(obj interface{}) { 650 var csiNode *sv1.CSINode 651 switch t := obj.(type) { 652 case *sv1.CSINode: 653 csiNode = obj.(*sv1.CSINode) 654 case cache.DeletedFinalStateUnknown: 655 var ok bool 656 csiNode, ok = t.Obj.(*sv1.CSINode) 657 if !ok { 658 klog.Errorf("Cannot convert to *sv1.CSINode: %v", obj) 659 return 660 } 661 default: 662 klog.Errorf("Cannot convert to *sv1.CSINode: %v", obj) 663 return 664 } 665 666 sc.Mutex.Lock() 667 delete(sc.CSINodesStatus, csiNode.Name) 668 sc.Mutex.Unlock() 669 sc.nodeQueue.Add(csiNode.Name) 670 } 671 672 func getJobID(pg *schedulingapi.PodGroup) schedulingapi.JobID { 673 return schedulingapi.JobID(fmt.Sprintf("%s/%s", pg.Namespace, pg.Name)) 674 } 675 676 // Assumes that lock is already acquired. 677 func (sc *SchedulerCache) setPodGroup(ss *schedulingapi.PodGroup) error { 678 job := getJobID(ss) 679 if _, found := sc.Jobs[job]; !found { 680 sc.Jobs[job] = schedulingapi.NewJobInfo(job) 681 } 682 683 sc.Jobs[job].SetPodGroup(ss) 684 685 // TODO(k82cn): set default queue in admission. 686 if len(ss.Spec.Queue) == 0 { 687 sc.Jobs[job].Queue = schedulingapi.QueueID(sc.defaultQueue) 688 } 689 690 metrics.UpdateE2eSchedulingStartTimeByJob(sc.Jobs[job].Name, string(sc.Jobs[job].Queue), sc.Jobs[job].Namespace, 691 sc.Jobs[job].CreationTimestamp.Time) 692 return nil 693 } 694 695 // Assumes that lock is already acquired. 696 func (sc *SchedulerCache) updatePodGroup(newPodGroup *schedulingapi.PodGroup) error { 697 return sc.setPodGroup(newPodGroup) 698 } 699 700 // Assumes that lock is already acquired. 701 func (sc *SchedulerCache) deletePodGroup(id schedulingapi.JobID) error { 702 job, found := sc.Jobs[id] 703 if !found { 704 return fmt.Errorf("can not found job %v", id) 705 } 706 707 // Unset SchedulingSpec 708 job.UnsetPodGroup() 709 710 sc.deleteJob(job) 711 712 return nil 713 } 714 715 // AddPodGroupV1beta1 add podgroup to scheduler cache 716 func (sc *SchedulerCache) AddPodGroupV1beta1(obj interface{}) { 717 ss, ok := obj.(*schedulingv1beta1.PodGroup) 718 if !ok { 719 klog.Errorf("Cannot convert to *schedulingv1beta1.PodGroup: %v", obj) 720 return 721 } 722 723 podgroup := scheduling.PodGroup{} 724 if err := scheme.Scheme.Convert(ss, &podgroup, nil); err != nil { 725 klog.Errorf("Failed to convert podgroup from %T to %T", ss, podgroup) 726 return 727 } 728 729 pg := &schedulingapi.PodGroup{PodGroup: podgroup, Version: schedulingapi.PodGroupVersionV1Beta1} 730 klog.V(4).Infof("Add PodGroup(%s) into cache, spec(%#v)", ss.Name, ss.Spec) 731 732 sc.Mutex.Lock() 733 defer sc.Mutex.Unlock() 734 735 if err := sc.setPodGroup(pg); err != nil { 736 klog.Errorf("Failed to add PodGroup %s into cache: %v", ss.Name, err) 737 return 738 } 739 } 740 741 // UpdatePodGroupV1beta1 add podgroup to scheduler cache 742 func (sc *SchedulerCache) UpdatePodGroupV1beta1(oldObj, newObj interface{}) { 743 oldSS, ok := oldObj.(*schedulingv1beta1.PodGroup) 744 if !ok { 745 klog.Errorf("Cannot convert oldObj to *schedulingv1beta1.SchedulingSpec: %v", oldObj) 746 return 747 } 748 newSS, ok := newObj.(*schedulingv1beta1.PodGroup) 749 if !ok { 750 klog.Errorf("Cannot convert newObj to *schedulingv1beta1.SchedulingSpec: %v", newObj) 751 return 752 } 753 754 if oldSS.ResourceVersion == newSS.ResourceVersion { 755 return 756 } 757 758 podgroup := scheduling.PodGroup{} 759 if err := scheme.Scheme.Convert(newSS, &podgroup, nil); err != nil { 760 klog.Errorf("Failed to convert podgroup from %T to %T", newSS, podgroup) 761 return 762 } 763 764 pg := &schedulingapi.PodGroup{PodGroup: podgroup, Version: schedulingapi.PodGroupVersionV1Beta1} 765 766 sc.Mutex.Lock() 767 defer sc.Mutex.Unlock() 768 769 if err := sc.updatePodGroup(pg); err != nil { 770 klog.Errorf("Failed to update SchedulingSpec %s into cache: %v", pg.Name, err) 771 return 772 } 773 } 774 775 // DeletePodGroupV1beta1 delete podgroup from scheduler cache 776 func (sc *SchedulerCache) DeletePodGroupV1beta1(obj interface{}) { 777 var ss *schedulingv1beta1.PodGroup 778 switch t := obj.(type) { 779 case *schedulingv1beta1.PodGroup: 780 ss = t 781 case cache.DeletedFinalStateUnknown: 782 var ok bool 783 ss, ok = t.Obj.(*schedulingv1beta1.PodGroup) 784 if !ok { 785 klog.Errorf("Cannot convert to podgroup: %v", t.Obj) 786 return 787 } 788 default: 789 klog.Errorf("Cannot convert to podgroup: %v", t) 790 return 791 } 792 793 jobID := schedulingapi.JobID(fmt.Sprintf("%s/%s", ss.Namespace, ss.Name)) 794 795 sc.Mutex.Lock() 796 defer sc.Mutex.Unlock() 797 798 if err := sc.deletePodGroup(jobID); err != nil { 799 klog.Errorf("Failed to delete podgroup %s from cache: %v", ss.Name, err) 800 return 801 } 802 } 803 804 // AddQueueV1beta1 add queue to scheduler cache 805 func (sc *SchedulerCache) AddQueueV1beta1(obj interface{}) { 806 ss, ok := obj.(*schedulingv1beta1.Queue) 807 if !ok { 808 klog.Errorf("Cannot convert to *schedulingv1beta1.Queue: %v", obj) 809 return 810 } 811 812 queue := &scheduling.Queue{} 813 if err := scheme.Scheme.Convert(ss, queue, nil); err != nil { 814 klog.Errorf("Failed to convert queue from %T to %T", ss, queue) 815 return 816 } 817 818 sc.Mutex.Lock() 819 defer sc.Mutex.Unlock() 820 821 klog.V(4).Infof("Add Queue(%s) into cache, spec(%#v)", ss.Name, ss.Spec) 822 sc.addQueue(queue) 823 } 824 825 // UpdateQueueV1beta1 update queue to scheduler cache 826 func (sc *SchedulerCache) UpdateQueueV1beta1(oldObj, newObj interface{}) { 827 oldSS, ok := oldObj.(*schedulingv1beta1.Queue) 828 if !ok { 829 klog.Errorf("Cannot convert oldObj to *schedulingv1beta1.Queue: %v", oldObj) 830 return 831 } 832 newSS, ok := newObj.(*schedulingv1beta1.Queue) 833 if !ok { 834 klog.Errorf("Cannot convert newObj to *schedulingv1beta1.Queue: %v", newObj) 835 return 836 } 837 838 if oldSS.ResourceVersion == newSS.ResourceVersion { 839 return 840 } 841 842 newQueue := &scheduling.Queue{} 843 if err := scheme.Scheme.Convert(newSS, newQueue, nil); err != nil { 844 klog.Errorf("Failed to convert queue from %T to %T", newSS, newQueue) 845 return 846 } 847 848 sc.Mutex.Lock() 849 defer sc.Mutex.Unlock() 850 sc.updateQueue(newQueue) 851 } 852 853 // DeleteQueueV1beta1 delete queue from the scheduler cache 854 func (sc *SchedulerCache) DeleteQueueV1beta1(obj interface{}) { 855 var ss *schedulingv1beta1.Queue 856 switch t := obj.(type) { 857 case *schedulingv1beta1.Queue: 858 ss = t 859 case cache.DeletedFinalStateUnknown: 860 var ok bool 861 ss, ok = t.Obj.(*schedulingv1beta1.Queue) 862 if !ok { 863 klog.Errorf("Cannot convert to *schedulingv1beta1.Queue: %v", t.Obj) 864 return 865 } 866 default: 867 klog.Errorf("Cannot convert to *schedulingv1beta1.Queue: %v", t) 868 return 869 } 870 871 sc.Mutex.Lock() 872 defer sc.Mutex.Unlock() 873 sc.deleteQueue(schedulingapi.QueueID(ss.Name)) 874 } 875 876 func (sc *SchedulerCache) addQueue(queue *scheduling.Queue) { 877 qi := schedulingapi.NewQueueInfo(queue) 878 sc.Queues[qi.UID] = qi 879 } 880 881 func (sc *SchedulerCache) updateQueue(queue *scheduling.Queue) { 882 sc.addQueue(queue) 883 } 884 885 func (sc *SchedulerCache) deleteQueue(id schedulingapi.QueueID) { 886 if queue, ok := sc.Queues[id]; ok { 887 delete(sc.Queues, id) 888 metrics.DeleteQueueMetrics(queue.Name) 889 } 890 } 891 892 // DeletePriorityClass delete priorityclass from the scheduler cache 893 func (sc *SchedulerCache) DeletePriorityClass(obj interface{}) { 894 var ss *schedulingv1.PriorityClass 895 switch t := obj.(type) { 896 case *schedulingv1.PriorityClass: 897 ss = t 898 case cache.DeletedFinalStateUnknown: 899 var ok bool 900 ss, ok = t.Obj.(*schedulingv1.PriorityClass) 901 if !ok { 902 klog.Errorf("Cannot convert to *schedulingv1.PriorityClass: %v", t.Obj) 903 return 904 } 905 default: 906 klog.Errorf("Cannot convert to *schedulingv1.PriorityClass: %v", t) 907 return 908 } 909 910 sc.Mutex.Lock() 911 defer sc.Mutex.Unlock() 912 913 sc.deletePriorityClass(ss) 914 } 915 916 // UpdatePriorityClass update priorityclass to scheduler cache 917 func (sc *SchedulerCache) UpdatePriorityClass(oldObj, newObj interface{}) { 918 oldSS, ok := oldObj.(*schedulingv1.PriorityClass) 919 if !ok { 920 klog.Errorf("Cannot convert oldObj to *schedulingv1.PriorityClass: %v", oldObj) 921 922 return 923 } 924 925 newSS, ok := newObj.(*schedulingv1.PriorityClass) 926 if !ok { 927 klog.Errorf("Cannot convert newObj to *schedulingv1.PriorityClass: %v", newObj) 928 return 929 } 930 931 sc.Mutex.Lock() 932 defer sc.Mutex.Unlock() 933 934 sc.deletePriorityClass(oldSS) 935 sc.addPriorityClass(newSS) 936 } 937 938 // AddPriorityClass add priorityclass to scheduler cache 939 func (sc *SchedulerCache) AddPriorityClass(obj interface{}) { 940 ss, ok := obj.(*schedulingv1.PriorityClass) 941 if !ok { 942 klog.Errorf("Cannot convert to *schedulingv1.PriorityClass: %v", obj) 943 return 944 } 945 946 sc.Mutex.Lock() 947 defer sc.Mutex.Unlock() 948 949 sc.addPriorityClass(ss) 950 } 951 952 func (sc *SchedulerCache) deletePriorityClass(pc *schedulingv1.PriorityClass) { 953 if pc.GlobalDefault { 954 sc.defaultPriorityClass = nil 955 sc.defaultPriority = 0 956 } 957 958 delete(sc.PriorityClasses, pc.Name) 959 } 960 961 func (sc *SchedulerCache) addPriorityClass(pc *schedulingv1.PriorityClass) { 962 if pc.GlobalDefault { 963 if sc.defaultPriorityClass != nil { 964 klog.Errorf("Updated default priority class from <%s> to <%s> forcefully.", 965 sc.defaultPriorityClass.Name, pc.Name) 966 } 967 sc.defaultPriorityClass = pc 968 sc.defaultPriority = pc.Value 969 } 970 971 sc.PriorityClasses[pc.Name] = pc 972 } 973 974 func (sc *SchedulerCache) updateResourceQuota(quota *v1.ResourceQuota) { 975 collection, ok := sc.NamespaceCollection[quota.Namespace] 976 if !ok { 977 collection = schedulingapi.NewNamespaceCollection(quota.Namespace) 978 sc.NamespaceCollection[quota.Namespace] = collection 979 } 980 981 collection.Update(quota) 982 } 983 984 func (sc *SchedulerCache) deleteResourceQuota(quota *v1.ResourceQuota) { 985 collection, ok := sc.NamespaceCollection[quota.Namespace] 986 if !ok { 987 return 988 } 989 990 collection.Delete(quota) 991 } 992 993 // DeleteResourceQuota delete ResourceQuota from the scheduler cache 994 func (sc *SchedulerCache) DeleteResourceQuota(obj interface{}) { 995 var r *v1.ResourceQuota 996 switch t := obj.(type) { 997 case *v1.ResourceQuota: 998 r = t 999 case cache.DeletedFinalStateUnknown: 1000 var ok bool 1001 r, ok = t.Obj.(*v1.ResourceQuota) 1002 if !ok { 1003 klog.Errorf("Cannot convert to *v1.ResourceQuota: %v", t.Obj) 1004 return 1005 } 1006 default: 1007 klog.Errorf("Cannot convert to *v1.ResourceQuota: %v", t) 1008 return 1009 } 1010 1011 sc.Mutex.Lock() 1012 defer sc.Mutex.Unlock() 1013 1014 klog.V(3).Infof("Delete ResourceQuota <%s/%v> in cache", r.Namespace, r.Name) 1015 sc.deleteResourceQuota(r) 1016 } 1017 1018 // UpdateResourceQuota update ResourceQuota to scheduler cache 1019 func (sc *SchedulerCache) UpdateResourceQuota(oldObj, newObj interface{}) { 1020 newR, ok := newObj.(*v1.ResourceQuota) 1021 if !ok { 1022 klog.Errorf("Cannot convert newObj to *v1.ResourceQuota: %v", newObj) 1023 return 1024 } 1025 1026 sc.Mutex.Lock() 1027 defer sc.Mutex.Unlock() 1028 1029 klog.V(3).Infof("Update ResourceQuota <%s/%v> in cache, with spec: %v.", newR.Namespace, newR.Name, newR.Spec.Hard) 1030 sc.updateResourceQuota(newR) 1031 } 1032 1033 // AddResourceQuota add ResourceQuota to scheduler cache 1034 func (sc *SchedulerCache) AddResourceQuota(obj interface{}) { 1035 var r *v1.ResourceQuota 1036 switch t := obj.(type) { 1037 case *v1.ResourceQuota: 1038 r = t 1039 default: 1040 klog.Errorf("Cannot convert to *v1.ResourceQuota: %v", t) 1041 return 1042 } 1043 1044 sc.Mutex.Lock() 1045 defer sc.Mutex.Unlock() 1046 1047 klog.V(3).Infof("Add ResourceQuota <%s/%v> in cache, with spec: %v.", r.Namespace, r.Name, r.Spec.Hard) 1048 sc.updateResourceQuota(r) 1049 } 1050 1051 func getNumaInfo(srcInfo *nodeinfov1alpha1.Numatopology) *schedulingapi.NumatopoInfo { 1052 numaInfo := &schedulingapi.NumatopoInfo{ 1053 Namespace: srcInfo.Namespace, 1054 Name: srcInfo.Name, 1055 Policies: make(map[nodeinfov1alpha1.PolicyName]string), 1056 NumaResMap: make(map[string]*schedulingapi.ResourceInfo), 1057 CPUDetail: topology.CPUDetails{}, 1058 ResReserved: make(v1.ResourceList), 1059 } 1060 1061 policies := srcInfo.Spec.Policies 1062 for name, policy := range policies { 1063 numaInfo.Policies[name] = policy 1064 } 1065 1066 numaResMap := srcInfo.Spec.NumaResMap 1067 for name, resInfo := range numaResMap { 1068 tmp := schedulingapi.ResourceInfo{} 1069 tmp.Capacity = resInfo.Capacity 1070 allocatable, err := cpuset.Parse(resInfo.Allocatable) 1071 if err != nil { 1072 klog.ErrorS(err, "Failed to parse input as CPUSet", resInfo.Allocatable) 1073 } 1074 tmp.Allocatable = allocatable 1075 numaInfo.NumaResMap[name] = &tmp 1076 } 1077 1078 cpuDetail := srcInfo.Spec.CPUDetail 1079 for key, detail := range cpuDetail { 1080 cpuID, _ := strconv.Atoi(key) 1081 numaInfo.CPUDetail[cpuID] = topology.CPUInfo{ 1082 NUMANodeID: detail.NUMANodeID, 1083 SocketID: detail.SocketID, 1084 CoreID: detail.CoreID, 1085 } 1086 } 1087 1088 resReserved, err := schedulingapi.ParseResourceList(srcInfo.Spec.ResReserved) 1089 if err != nil { 1090 klog.Errorf("ParseResourceList failed, err=%v", err) 1091 } else { 1092 numaInfo.ResReserved = resReserved 1093 } 1094 1095 return numaInfo 1096 } 1097 1098 // Assumes that lock is already acquired. 1099 func (sc *SchedulerCache) addNumaInfo(info *nodeinfov1alpha1.Numatopology) error { 1100 if sc.Nodes[info.Name] == nil { 1101 sc.Nodes[info.Name] = schedulingapi.NewNodeInfo(nil) 1102 sc.Nodes[info.Name].Name = info.Name 1103 } 1104 1105 if sc.Nodes[info.Name].NumaInfo == nil { 1106 sc.Nodes[info.Name].NumaInfo = getNumaInfo(info) 1107 sc.Nodes[info.Name].NumaChgFlag = schedulingapi.NumaInfoMoreFlag 1108 } else { 1109 newLocalInfo := getNumaInfo(info) 1110 if sc.Nodes[info.Name].NumaInfo.Compare(newLocalInfo) { 1111 sc.Nodes[info.Name].NumaChgFlag = schedulingapi.NumaInfoMoreFlag 1112 } else { 1113 sc.Nodes[info.Name].NumaChgFlag = schedulingapi.NumaInfoLessFlag 1114 } 1115 1116 sc.Nodes[info.Name].NumaInfo = newLocalInfo 1117 } 1118 1119 for resName, NumaResInfo := range sc.Nodes[info.Name].NumaInfo.NumaResMap { 1120 klog.V(3).Infof("resource %s Allocatable %v on node[%s] into cache", resName, NumaResInfo, info.Name) 1121 } 1122 1123 klog.V(3).Infof("Policies %v on node[%s] into cache, change= %v", 1124 sc.Nodes[info.Name].NumaInfo.Policies, info.Name, sc.Nodes[info.Name].NumaChgFlag) 1125 return nil 1126 } 1127 1128 // Assumes that lock is already acquired. 1129 func (sc *SchedulerCache) deleteNumaInfo(info *nodeinfov1alpha1.Numatopology) { 1130 if sc.Nodes[info.Name] != nil { 1131 sc.Nodes[info.Name].NumaInfo = nil 1132 sc.Nodes[info.Name].NumaChgFlag = schedulingapi.NumaInfoResetFlag 1133 klog.V(3).Infof("delete numainfo in cahce for node<%s>", info.Name) 1134 } 1135 } 1136 1137 // AddNumaInfoV1alpha1 add numa information to scheduler cache 1138 func (sc *SchedulerCache) AddNumaInfoV1alpha1(obj interface{}) { 1139 ss, ok := obj.(*nodeinfov1alpha1.Numatopology) 1140 if !ok { 1141 klog.Errorf("Cannot convert oldObj to *nodeinfov1alpha1.Numatopology: %v", obj) 1142 return 1143 } 1144 1145 sc.Mutex.Lock() 1146 defer sc.Mutex.Unlock() 1147 1148 sc.addNumaInfo(ss) 1149 } 1150 1151 // UpdateNumaInfoV1alpha1 update numa information to scheduler cache 1152 func (sc *SchedulerCache) UpdateNumaInfoV1alpha1(oldObj, newObj interface{}) { 1153 ss, ok := newObj.(*nodeinfov1alpha1.Numatopology) 1154 if !ok { 1155 klog.Errorf("Cannot convert oldObj to *nodeinfov1alpha1.Numatopology: %v", newObj) 1156 return 1157 } 1158 1159 sc.Mutex.Lock() 1160 defer sc.Mutex.Unlock() 1161 sc.addNumaInfo(ss) 1162 klog.V(3).Infof("update numaInfo<%s> in cahce, with spec: Policy: %v, resMap: %v", ss.Name, ss.Spec.Policies, ss.Spec.NumaResMap) 1163 } 1164 1165 // DeleteNumaInfoV1alpha1 delete numa information from scheduler cache 1166 func (sc *SchedulerCache) DeleteNumaInfoV1alpha1(obj interface{}) { 1167 var ss *nodeinfov1alpha1.Numatopology 1168 switch t := obj.(type) { 1169 case *nodeinfov1alpha1.Numatopology: 1170 ss = t 1171 case cache.DeletedFinalStateUnknown: 1172 var ok bool 1173 ss, ok = t.Obj.(*nodeinfov1alpha1.Numatopology) 1174 if !ok { 1175 klog.Errorf("Cannot convert to Numatopo: %v", t.Obj) 1176 return 1177 } 1178 default: 1179 klog.Errorf("Cannot convert to Numatopo: %v", t) 1180 return 1181 } 1182 1183 sc.Mutex.Lock() 1184 defer sc.Mutex.Unlock() 1185 1186 sc.deleteNumaInfo(ss) 1187 klog.V(3).Infof("Delete numaInfo<%s> from cahce, with spec: Policy: %v, resMap: %v", ss.Name, ss.Spec.Policies, ss.Spec.NumaResMap) 1188 } 1189 1190 // AddJob add job to scheduler cache 1191 func (sc *SchedulerCache) AddJob(obj interface{}) { 1192 job, ok := obj.(*schedulingapi.JobInfo) 1193 if !ok { 1194 klog.Errorf("Cannot convert to *api.JobInfo: %v", obj) 1195 return 1196 } 1197 sc.Mutex.Lock() 1198 defer sc.Mutex.Unlock() 1199 sc.Jobs[job.UID] = job 1200 } 1201 1202 func (sc *SchedulerCache) setCSIResourceOnNode(csiNode *sv1.CSINode, node *v1.Node) { 1203 if csiNode == nil || node == nil { 1204 return 1205 } 1206 1207 csiResources := make(map[v1.ResourceName]resource.Quantity) 1208 for i := range csiNode.Spec.Drivers { 1209 d := csiNode.Spec.Drivers[i] 1210 k := v1.ResourceName(volumeutil.GetCSIAttachLimitKey(d.Name)) 1211 if d.Allocatable != nil && d.Allocatable.Count != nil { 1212 csiResources[k] = *resource.NewScaledQuantity(int64(*d.Allocatable.Count), -3) 1213 } else { 1214 // Count all csi volumes in cache, because the storage may change from unattachable volumes to attachable volumes after 1215 // the cache set up, in this case it is very difficult to refresh all task caches. 1216 // For unattachable volume, set the limits number to a very large value, in this way, scheduling will never 1217 // be limited due to insufficient quantity of it. 1218 csiResources[k] = *resource.NewScaledQuantity(DefaultAttachableVolumeQuantity, -3) 1219 } 1220 } 1221 1222 if len(csiResources) == 0 { 1223 return 1224 } 1225 1226 if node.Status.Allocatable == nil { 1227 node.Status.Allocatable = make(map[v1.ResourceName]resource.Quantity) 1228 } 1229 1230 if node.Status.Capacity == nil { 1231 node.Status.Capacity = make(map[v1.ResourceName]resource.Quantity) 1232 } 1233 1234 for resourceName, quantity := range csiResources { 1235 node.Status.Allocatable[resourceName] = quantity 1236 node.Status.Capacity[resourceName] = quantity 1237 } 1238 }