|
8 | 8 | "os" |
9 | 9 | "os/signal" |
10 | 10 | "path" |
| 11 | + "strconv" |
| 12 | + "strings" |
11 | 13 | "sync" |
12 | 14 | "syscall" |
13 | 15 | "time" |
@@ -500,18 +502,27 @@ func (d *daemon) processNetworkGUID(networkID string, spec *utils.IbSriovCniSpec |
500 | 502 | return err |
501 | 503 | } |
502 | 504 | } else { |
503 | | - guidAddr, err = d.guidPool.GenerateGUID() |
504 | | - // TODO(Nik): This error handling is funky. We sync the GUID pool but then don't |
505 | | - // re-assign a guidAddr so we're left with a 0 guid |
506 | | - if err != nil { |
507 | | - switch { |
508 | | - case errors.Is(err, guid.ErrGUIDPoolExhausted): |
509 | | - err = syncGUIDPool(d.smClient, d.guidPool) |
510 | | - if err != nil { |
511 | | - return err |
| 505 | + if d.config.PreallocatedMode { |
| 506 | + // Pre-allocated mode: the GUID is a deterministic function of |
| 507 | + // (pkey, hca index, node) rather than a free GUID pulled from the pool. |
| 508 | + guidAddr, err = d.LookupPredeterminedGUID(spec, networkID, pi) |
| 509 | + if err != nil { |
| 510 | + return fmt.Errorf("failed to lookup predetermined GUID for pod ID %s, with error: %v", pi.pod.UID, err) |
| 511 | + } |
| 512 | + } else { |
| 513 | + guidAddr, err = d.guidPool.GenerateGUID() |
| 514 | + // TODO(Nik): This error handling is funky. We sync the GUID pool but then don't |
| 515 | + // re-assign a guidAddr so we're left with a 0 guid |
| 516 | + if err != nil { |
| 517 | + switch { |
| 518 | + case errors.Is(err, guid.ErrGUIDPoolExhausted): |
| 519 | + err = syncGUIDPool(d.smClient, d.guidPool) |
| 520 | + if err != nil { |
| 521 | + return err |
| 522 | + } |
| 523 | + default: |
| 524 | + return fmt.Errorf("failed to generate GUID for pod ID %s, with error: %v", pi.pod.UID, err) |
512 | 525 | } |
513 | | - default: |
514 | | - return fmt.Errorf("failed to generate GUID for pod ID %s, with error: %v", pi.pod.UID, err) |
515 | 526 | } |
516 | 527 | } |
517 | 528 |
|
@@ -540,6 +551,73 @@ func (d *daemon) processNetworkGUID(networkID string, spec *utils.IbSriovCniSpec |
540 | 551 | return nil |
541 | 552 | } |
542 | 553 |
|
| 554 | +// LookupPredeterminedGUID returns the deterministic GUID for a pod's network attachment |
| 555 | +// in pre-allocated mode. The GUID is a pure function of the tenant pkey, the HCA (rail) |
| 556 | +// index, and the node the pod is scheduled on, so it never needs to be allocated or |
| 557 | +// tracked. Used when we are not the UFM fabric admin and GUIDs are pre-provisioned. |
| 558 | +func (d *daemon) LookupPredeterminedGUID( |
| 559 | + spec *utils.IbSriovCniSpec, networkID string, pi *podNetworkInfo, |
| 560 | +) (guid.GUID, error) { |
| 561 | + // pkey: the tenant partition, taken straight from the CNI spec. |
| 562 | + pKey, err := utils.ParsePKey(spec.PKey) |
| 563 | + if err != nil { |
| 564 | + return 0, fmt.Errorf("failed to parse pkey %q: %v", spec.PKey, err) |
| 565 | + } |
| 566 | + |
| 567 | + // hca index: which rail this attachment is for, derived from the network id. |
| 568 | + hcaIndex, err := hcaIndexFromNetworkID(networkID) |
| 569 | + if err != nil { |
| 570 | + return 0, fmt.Errorf("failed to derive hca index from networkID %q: %v", networkID, err) |
| 571 | + } |
| 572 | + |
| 573 | + // node index: the physical node the pod landed on, derived from its node name. |
| 574 | + nodeName := pi.pod.Spec.NodeName |
| 575 | + if nodeName == "" { |
| 576 | + return 0, fmt.Errorf("pod %s is not scheduled to a node yet", pi.pod.UID) |
| 577 | + } |
| 578 | + nodeIndex, err := nodeIndexFromName(nodeName) |
| 579 | + if err != nil { |
| 580 | + return 0, fmt.Errorf("failed to derive node index from node name %q: %v", nodeName, err) |
| 581 | + } |
| 582 | + |
| 583 | + return computePredeterminedGUID(pKey, hcaIndex, nodeIndex) |
| 584 | +} |
| 585 | + |
| 586 | +// nodeIndexSuffixLen is the number of trailing characters of the node name that |
| 587 | +// encode the node index (e.g. "...-042" -> 42). |
| 588 | +const nodeIndexSuffixLen = 3 |
| 589 | + |
| 590 | +// nodeIndexFromName derives the node index from the last nodeIndexSuffixLen |
| 591 | +// characters of the node name. |
| 592 | +func nodeIndexFromName(nodeName string) (int, error) { |
| 593 | + if len(nodeName) < nodeIndexSuffixLen { |
| 594 | + return 0, fmt.Errorf("node name %q is shorter than the %d-char index suffix", nodeName, nodeIndexSuffixLen) |
| 595 | + } |
| 596 | + suffix := nodeName[len(nodeName)-nodeIndexSuffixLen:] |
| 597 | + idx, err := strconv.Atoi(suffix) |
| 598 | + if err != nil { |
| 599 | + return 0, fmt.Errorf("node name %q does not end in a numeric index: %v", nodeName, err) |
| 600 | + } |
| 601 | + return idx, nil |
| 602 | +} |
| 603 | + |
| 604 | +// hcaIndexFromNetworkID derives the HCA (rail) index from the trailing number of |
| 605 | +// the network id (e.g. "...-ib-net-3" -> 3). |
| 606 | +func hcaIndexFromNetworkID(networkID string) (int, error) { |
| 607 | + parts := strings.Split(networkID, "-") |
| 608 | + idx, err := strconv.Atoi(parts[len(parts)-1]) |
| 609 | + if err != nil { |
| 610 | + return 0, fmt.Errorf("networkID %q does not end in a numeric rail index: %v", networkID, err) |
| 611 | + } |
| 612 | + return idx, nil |
| 613 | +} |
| 614 | + |
| 615 | +// computePredeterminedGUID maps (pkey, hca index, node index) to a deterministic GUID. |
| 616 | +// TODO(TCL-7011): implement the agreed deterministic hashing/encoding scheme. |
| 617 | +func computePredeterminedGUID(_ int, _ int, _ int) (guid.GUID, error) { |
| 618 | + return 0, fmt.Errorf("computePredeterminedGUID is not yet implemented") |
| 619 | +} |
| 620 | + |
543 | 621 | func syncGUIDPool(smClient plugins.SubnetManagerClient, guidPool guid.Pool) error { |
544 | 622 | usedGuids, err := smClient.ListGuidsInUse() |
545 | 623 | if err != nil { |
|
0 commit comments