Skip to content

Commit 5fcd462

Browse files
Trusha Suresh Talaticlaude
authored andcommitted
Add PreallocatedMode skeleton for predetermined GUID lookup
In pre-allocated mode the GUID for a pod's IB network is a deterministic function of (pkey, hca index, node index) rather than a free GUID pulled from the pool. This is for substrates where we are not the UFM fabric admin and GUIDs/PKeys are pre-provisioned by the provider (IREN B300s). - Add PREALLOCATED_MODE config flag (DaemonConfig), default false so existing substrates are unchanged. - Branch in processNetworkGUID: use LookupPredeterminedGUID when the flag is set, else GenerateGUID (unchanged). - LookupPredeterminedGUID extracts pkey (CNI spec), hca index (networkID trailing number) and node index (node name suffix). The deterministic encoding itself is stubbed (TCL-7011) and returns not-implemented. - Unit tests for the hca/node index parsers. Refs TCL-6978 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b86fd05 commit 5fcd462

3 files changed

Lines changed: 131 additions & 11 deletions

File tree

pkg/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ type DaemonConfig struct {
2727
// with UTC timestamps from GetServerTime().
2828
// Example: "America/New_York", "America/Los_Angeles", "UTC" (default).
2929
SMTimezone string `env:"SM_TIMEZONE" envDefault:"UTC"`
30+
// Use deterministic pre-allocated GUIDs (lookup) instead of allocating from the pool.
31+
// Enabled on substrates where we are not the UFM fabric admin and GUIDs/PKeys are
32+
// pre-provisioned by the provider (e.g. IREN B300s).
33+
PreallocatedMode bool `env:"PREALLOCATED_MODE" envDefault:"false"`
3034
}
3135

3236
type GUIDPoolConfig struct {

pkg/daemon/daemon.go

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"os"
99
"os/signal"
1010
"path"
11+
"strconv"
12+
"strings"
1113
"sync"
1214
"syscall"
1315
"time"
@@ -500,18 +502,27 @@ func (d *daemon) processNetworkGUID(networkID string, spec *utils.IbSriovCniSpec
500502
return err
501503
}
502504
} 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)
512525
}
513-
default:
514-
return fmt.Errorf("failed to generate GUID for pod ID %s, with error: %v", pi.pod.UID, err)
515526
}
516527
}
517528

@@ -540,6 +551,73 @@ func (d *daemon) processNetworkGUID(networkID string, spec *utils.IbSriovCniSpec
540551
return nil
541552
}
542553

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+
543621
func syncGUIDPool(smClient plugins.SubnetManagerClient, guidPool guid.Pool) error {
544622
usedGuids, err := smClient.ListGuidsInUse()
545623
if err != nil {

pkg/daemon/daemon_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,4 +844,42 @@ var _ = Describe("Daemon", func() {
844844
Expect(exists4).To(BeFalse(), "network4 should be removed from deleteMap")
845845
})
846846
})
847+
848+
Context("nodeIndexFromName", func() {
849+
It("parses the trailing index from a node name", func() {
850+
idx, err := nodeIndexFromName("gpu-dp-node-042")
851+
Expect(err).ToNot(HaveOccurred())
852+
Expect(idx).To(Equal(42))
853+
})
854+
It("treats the suffix as the index even with leading zeros", func() {
855+
idx, err := nodeIndexFromName("node-007")
856+
Expect(err).ToNot(HaveOccurred())
857+
Expect(idx).To(Equal(7))
858+
})
859+
It("fails when the suffix is not numeric", func() {
860+
_, err := nodeIndexFromName("gpu-dp-zsjr4-dkznf")
861+
Expect(err).To(HaveOccurred())
862+
})
863+
It("fails when the node name is shorter than the index suffix", func() {
864+
_, err := nodeIndexFromName("ab")
865+
Expect(err).To(HaveOccurred())
866+
})
867+
})
868+
869+
Context("hcaIndexFromNetworkID", func() {
870+
It("parses the trailing rail index from the network id", func() {
871+
idx, err := hcaIndexFromNetworkID("ns_cluster-gpu-dp-mlnx-hca-ib-net-3")
872+
Expect(err).ToNot(HaveOccurred())
873+
Expect(idx).To(Equal(3))
874+
})
875+
It("parses a multi-digit rail index", func() {
876+
idx, err := hcaIndexFromNetworkID("ns_cluster-ib-net-12")
877+
Expect(err).ToNot(HaveOccurred())
878+
Expect(idx).To(Equal(12))
879+
})
880+
It("fails when the network id does not end in a number", func() {
881+
_, err := hcaIndexFromNetworkID("ns_cluster-ib-net-foo")
882+
Expect(err).To(HaveOccurred())
883+
})
884+
})
847885
})

0 commit comments

Comments
 (0)