Skip to content

Commit 9d23b85

Browse files
authored
Merge pull request k8snetworkplumbingwg#151 from vitus133/perla4-integ
CNF-21360: Add Dell XR8720t settings
2 parents cc41a1c + 7ef85b7 commit 9d23b85

File tree

7 files changed

+710
-167
lines changed

7 files changed

+710
-167
lines changed

pkg/linuxptp-daemon/pkg/hardwareconfig/clockchain_resolution_test.go

Lines changed: 189 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -88,183 +88,207 @@ func loadPtpConfigFromFile(filename string) (*ptpv1.PtpConfig, error) {
8888
}
8989

9090
// TestClockChainResolution tests that a minimal hardwareconfig with clockType
91-
// gets resolved with structure and behavior derived from ptpconfig and templates
91+
// gets resolved with structure and behavior derived from ptpconfig and templates.
92+
// It covers multiple hardware vendors (intel/e825, dell/XR8720t).
9293
func TestClockChainResolution(t *testing.T) {
93-
// Load minimal hardwareconfig
94-
hwConfig, err := loadHardwareConfigFromFile("testdata/gnrd-hwconfig-minimal.yaml")
95-
assert.NoError(t, err)
96-
assert.NotNil(t, hwConfig)
97-
if hwConfig == nil {
98-
t.Fatal("hwConfig is nil")
94+
testCases := []struct {
95+
name string
96+
hwConfigFile string
97+
hwDefPath string
98+
expectedPtpInput string
99+
expectedGnssInput string
100+
}{
101+
{
102+
name: "intel/e825",
103+
hwConfigFile: "testdata/gnrd-hwconfig-minimal.yaml",
104+
hwDefPath: "intel/e825",
105+
expectedPtpInput: "GNR-D_SDP0",
106+
expectedGnssInput: "GNSS_1PPS_IN",
107+
},
108+
{
109+
name: "dell/XR8720t",
110+
hwConfigFile: "testdata/gnrd-hwconfig-minimal-perla4.yaml",
111+
hwDefPath: "dell/XR8720t",
112+
expectedPtpInput: "ETH01_SDP_TIMESYNC_0",
113+
expectedGnssInput: "GNSS_1PPS_IN",
114+
},
99115
}
100116

101-
// Verify it's minimal (no DPLL/Ethernet in structure)
102-
assert.NotNil(t, hwConfig.Spec.Profile.ClockType)
103-
assert.Equal(t, "T-BC", *hwConfig.Spec.Profile.ClockType)
104-
assert.NotNil(t, hwConfig.Spec.Profile.ClockChain)
105-
assert.Len(t, hwConfig.Spec.Profile.ClockChain.Structure, 1)
106-
107-
subsystem := hwConfig.Spec.Profile.ClockChain.Structure[0]
108-
assert.Equal(t, "leader", subsystem.Name)
109-
assert.Equal(t, "intel/e825", subsystem.HardwareSpecificDefinitions)
110-
111-
// Before resolution: DPLL and Ethernet should be empty/omitted
112-
assert.Empty(t, subsystem.DPLL.NetworkInterface)
113-
assert.Empty(t, subsystem.DPLL.PhaseInputs)
114-
assert.Empty(t, subsystem.Ethernet)
115-
assert.Nil(t, hwConfig.Spec.Profile.ClockChain.Behavior)
116-
117-
// Load ptpconfig
118-
ptpConfig, err := loadPtpConfigFromFile("testdata/tbc-gnrd.yaml")
119-
assert.NoError(t, err)
120-
assert.NotNil(t, ptpConfig)
121-
if ptpConfig == nil {
122-
t.Fatal("ptpConfig is nil")
123-
}
117+
for _, tc := range testCases {
118+
t.Run(tc.name, func(t *testing.T) {
119+
// Load minimal hardwareconfig
120+
hwConfig, err := loadHardwareConfigFromFile(tc.hwConfigFile)
121+
assert.NoError(t, err)
122+
assert.NotNil(t, hwConfig)
123+
if hwConfig == nil {
124+
t.Fatal("hwConfig is nil")
125+
}
124126

125-
// Find the matching profile
126-
var ptpProfile *ptpv1.PtpProfile
127-
for i := range ptpConfig.Spec.Profile {
128-
if ptpConfig.Spec.Profile[i].Name != nil &&
129-
*ptpConfig.Spec.Profile[i].Name == hwConfig.Spec.RelatedPtpProfileName {
130-
ptpProfile = &ptpConfig.Spec.Profile[i]
131-
break
132-
}
133-
}
134-
assert.NotNil(t, ptpProfile, "PTP profile %s not found", hwConfig.Spec.RelatedPtpProfileName)
135-
if ptpProfile == nil {
136-
t.Fatalf("PTP profile %s not found", hwConfig.Spec.RelatedPtpProfileName)
137-
}
127+
// Verify it's minimal (no DPLL/Ethernet in structure)
128+
assert.NotNil(t, hwConfig.Spec.Profile.ClockType)
129+
assert.Equal(t, "T-BC", *hwConfig.Spec.Profile.ClockType)
130+
assert.NotNil(t, hwConfig.Spec.Profile.ClockChain)
131+
assert.Len(t, hwConfig.Spec.Profile.ClockChain.Structure, 1)
132+
133+
subsystem := hwConfig.Spec.Profile.ClockChain.Structure[0]
134+
assert.Equal(t, "leader", subsystem.Name)
135+
assert.Equal(t, tc.hwDefPath, subsystem.HardwareSpecificDefinitions)
136+
137+
// Before resolution: DPLL and Ethernet should be empty/omitted
138+
assert.Empty(t, subsystem.DPLL.NetworkInterface)
139+
assert.Empty(t, subsystem.DPLL.PhaseInputs)
140+
assert.Empty(t, subsystem.Ethernet)
141+
assert.Nil(t, hwConfig.Spec.Profile.ClockChain.Behavior)
142+
143+
// Load ptpconfig
144+
ptpConfig, err := loadPtpConfigFromFile("testdata/tbc-gnrd.yaml")
145+
assert.NoError(t, err)
146+
assert.NotNil(t, ptpConfig)
147+
if ptpConfig == nil {
148+
t.Fatal("ptpConfig is nil")
149+
}
138150

139-
// Extract upstream ports from ptpconfig
140-
upstreamPorts := extractUpstreamPortsFromPtpProfile(ptpProfile)
141-
assert.NotEmpty(t, upstreamPorts, "Should find at least one upstream port")
142-
if len(upstreamPorts) == 0 {
143-
t.Fatal("Should find at least one upstream port")
144-
}
145-
assert.Contains(t, upstreamPorts, "eno2", "Should find eno2 as upstream port")
146-
147-
// Set up fake ConfigMap loader for tests (reused later for hcm)
148-
fakeClient := fake.NewSimpleClientset()
149-
loader := NewBoardLabelMapLoader(fakeClient, "default")
150-
151-
// Load behavior profile template
152-
behaviorTemplate, err := LoadBehaviorProfile("intel/e825", *hwConfig.Spec.Profile.ClockType, loader)
153-
assert.NoError(t, err)
154-
assert.NotNil(t, behaviorTemplate, "Behavior template should be loaded")
155-
if behaviorTemplate == nil {
156-
t.Fatal("Behavior template should be loaded")
157-
}
151+
// Find the matching profile
152+
var ptpProfile *ptpv1.PtpProfile
153+
for i := range ptpConfig.Spec.Profile {
154+
if ptpConfig.Spec.Profile[i].Name != nil &&
155+
*ptpConfig.Spec.Profile[i].Name == hwConfig.Spec.RelatedPtpProfileName {
156+
ptpProfile = &ptpConfig.Spec.Profile[i]
157+
break
158+
}
159+
}
160+
assert.NotNil(t, ptpProfile, "PTP profile %s not found", hwConfig.Spec.RelatedPtpProfileName)
161+
if ptpProfile == nil {
162+
t.Fatalf("PTP profile %s not found", hwConfig.Spec.RelatedPtpProfileName)
163+
}
158164

159-
// Verify template has pinRoles
160-
assert.NotEmpty(t, behaviorTemplate.PinRoles)
161-
assert.Equal(t, "GNR-D_SDP0", behaviorTemplate.PinRoles["ptpInputPin"])
162-
assert.Equal(t, "GNSS_1PPS_IN", behaviorTemplate.PinRoles["gnssInputPin"])
163-
assert.Equal(t, "GNSS_1PPS_IN", behaviorTemplate.PinRoles["gnssInputPin"])
164-
165-
// Set up mock leading interface resolver
166-
mockResolver := newMockLeadingInterfaceResolver()
167-
// Mock: eno2 -> PHC 0 -> PCI 0000:13:00.0 -> eno5
168-
mockResolver.phcIDs["eno2"] = "/dev/ptp0"
169-
mockResolver.symlinks["/sys/class/ptp/ptp0/device"] = "../../../0000:13:00.0"
170-
mockResolver.dirEntries["/sys/bus/pci/devices/0000:13:00.0/net"] = []os.DirEntry{
171-
&mockDirEntry{name: "eno5", isDir: false},
172-
}
165+
// Extract upstream ports from ptpconfig
166+
upstreamPorts := extractUpstreamPortsFromPtpProfile(ptpProfile)
167+
assert.NotEmpty(t, upstreamPorts, "Should find at least one upstream port")
168+
if len(upstreamPorts) == 0 {
169+
t.Fatal("Should find at least one upstream port")
170+
}
171+
assert.Contains(t, upstreamPorts, "eno2", "Should find eno2 as upstream port")
172+
173+
// Set up fake ConfigMap loader for tests (reused later for hcm)
174+
fakeClient := fake.NewSimpleClientset()
175+
loader := NewBoardLabelMapLoader(fakeClient, "default")
176+
177+
// Load behavior profile template
178+
behaviorTemplate, err := LoadBehaviorProfile(tc.hwDefPath, *hwConfig.Spec.Profile.ClockType, loader)
179+
assert.NoError(t, err)
180+
assert.NotNil(t, behaviorTemplate, "Behavior template should be loaded")
181+
if behaviorTemplate == nil {
182+
t.Fatal("Behavior template should be loaded")
183+
}
173184

174-
// Inject mock resolver
175-
SetLeadingInterfaceResolver(mockResolver)
176-
defer ResetLeadingInterfaceResolver()
177-
178-
// Resolve clock chain (this is what we're testing)
179-
hcm := NewHardwareConfigManager(fakeClient, "default")
180-
resolvedConfig, err := hcm.ResolveClockChain(hwConfig, ptpConfig)
181-
assert.NoError(t, err)
182-
assert.NotNil(t, resolvedConfig)
183-
if resolvedConfig == nil {
184-
t.Fatal("resolvedConfig is nil")
185-
}
185+
// Verify template has pinRoles
186+
assert.NotEmpty(t, behaviorTemplate.PinRoles)
187+
assert.Equal(t, tc.expectedPtpInput, behaviorTemplate.PinRoles["ptpInputPin"])
188+
assert.Equal(t, tc.expectedGnssInput, behaviorTemplate.PinRoles["gnssInputPin"])
189+
190+
// Set up mock leading interface resolver
191+
mockResolver := newMockLeadingInterfaceResolver()
192+
// Mock: eno2 -> PHC 0 -> PCI 0000:13:00.0 -> eno5
193+
mockResolver.phcIDs["eno2"] = "/dev/ptp0"
194+
mockResolver.symlinks["/sys/class/ptp/ptp0/device"] = "../../../0000:13:00.0"
195+
mockResolver.dirEntries["/sys/bus/pci/devices/0000:13:00.0/net"] = []os.DirEntry{
196+
&mockDirEntry{name: "eno5", isDir: false},
197+
}
186198

187-
// Verify structure was derived
188-
resolvedSubsystem := resolvedConfig.Spec.Profile.ClockChain.Structure[0]
189-
190-
// NetworkInterface should be derived (leading interface found via PHC -> PCI -> net)
191-
assert.NotEmpty(t, resolvedSubsystem.DPLL.NetworkInterface)
192-
assert.Equal(t, "eno5", resolvedSubsystem.DPLL.NetworkInterface,
193-
"NetworkInterface should be derived from upstream port eno2 via PHC -> PCI -> net path")
194-
195-
// PhaseInputs should be derived from pinRoles (ptpInputPin -> GNR-D_SDP0)
196-
assert.NotEmpty(t, resolvedSubsystem.DPLL.PhaseInputs)
197-
ptpInputPin, exists := resolvedSubsystem.DPLL.PhaseInputs["GNR-D_SDP0"]
198-
assert.True(t, exists, "PhaseInputs should contain ptpInputPin from template")
199-
assert.NotNil(t, ptpInputPin.Frequency)
200-
assert.Equal(t, int64(1), *ptpInputPin.Frequency, "PTP input should be 1 PPS")
201-
202-
// Ethernet ports should be derived (all upstream ports)
203-
assert.NotEmpty(t, resolvedSubsystem.Ethernet)
204-
assert.Len(t, resolvedSubsystem.Ethernet, 1)
205-
assert.Equal(t, upstreamPorts, resolvedSubsystem.Ethernet[0].Ports,
206-
"Ethernet ports should match upstream ports")
207-
208-
// Verify behavior derivation
209-
assert.NotNil(t, resolvedConfig.Spec.Profile.ClockChain.Behavior)
210-
behavior := resolvedConfig.Spec.Profile.ClockChain.Behavior
211-
212-
// Sources should be instantiated with resolved variables
213-
assert.NotEmpty(t, behavior.Sources)
214-
ptpSource := findSourceByName(behavior.Sources, "PTP")
215-
assert.NotNil(t, ptpSource, "PTP source should be present")
216-
if ptpSource == nil {
217-
t.Fatal("PTP source should be present")
218-
}
219-
assert.Equal(t, "ptpTimeReceiver", ptpSource.SourceType)
220-
assert.Equal(t, "leader", ptpSource.Subsystem, "Subsystem should be resolved")
221-
assert.Equal(t, "GNR-D_SDP0", ptpSource.BoardLabel, "BoardLabel should be resolved from pinRoles")
222-
assert.Equal(t, upstreamPorts, ptpSource.PTPTimeReceivers,
223-
"PTPTimeReceivers should match upstream ports")
224-
225-
// Conditions should be instantiated with resolved variables
226-
assert.NotEmpty(t, behavior.Conditions)
227-
initCondition := findConditionByName(behavior.Conditions, "Initialize T-BC")
228-
assert.NotNil(t, initCondition, "Initialize T-BC condition should be present")
229-
if initCondition == nil {
230-
t.Fatal("Initialize T-BC condition should be present")
231-
}
232-
// Init condition should use GNSS input pin for DPLL
233-
if len(initCondition.DesiredStates) > 0 && initCondition.DesiredStates[0].DPLL != nil {
234-
assert.Equal(t, "GNSS_1PPS_IN", initCondition.DesiredStates[0].DPLL.BoardLabel,
235-
"Init condition DPLL should use gnssInputPin from template")
236-
}
199+
// Inject mock resolver
200+
SetLeadingInterfaceResolver(mockResolver)
201+
defer ResetLeadingInterfaceResolver()
202+
203+
// Resolve clock chain (this is what we're testing)
204+
hcm := NewHardwareConfigManager(fakeClient, "default")
205+
resolvedConfig, err := hcm.ResolveClockChain(hwConfig, ptpConfig)
206+
assert.NoError(t, err)
207+
assert.NotNil(t, resolvedConfig)
208+
if resolvedConfig == nil {
209+
t.Fatal("resolvedConfig is nil")
210+
}
237211

238-
lockedCondition := findConditionByName(behavior.Conditions, "PTP Source Locked")
239-
assert.NotNil(t, lockedCondition, "PTP Source Locked condition should be present")
240-
if lockedCondition == nil {
241-
t.Fatal("PTP Source Locked condition should be present")
242-
}
243-
// Locked condition should use PTP input pin for DPLL
244-
if len(lockedCondition.DesiredStates) > 0 && lockedCondition.DesiredStates[0].DPLL != nil {
245-
assert.Equal(t, "GNR-D_SDP0", lockedCondition.DesiredStates[0].DPLL.BoardLabel,
246-
"Locked condition DPLL should use ptpInputPin from template")
247-
}
212+
// Verify structure was derived
213+
resolvedSubsystem := resolvedConfig.Spec.Profile.ClockChain.Structure[0]
214+
215+
// NetworkInterface should be derived (leading interface found via PHC -> PCI -> net)
216+
assert.NotEmpty(t, resolvedSubsystem.DPLL.NetworkInterface)
217+
assert.Equal(t, "eno5", resolvedSubsystem.DPLL.NetworkInterface,
218+
"NetworkInterface should be derived from upstream port eno2 via PHC -> PCI -> net path")
219+
220+
// PhaseInputs should be derived from pinRoles (ptpInputPin)
221+
assert.NotEmpty(t, resolvedSubsystem.DPLL.PhaseInputs)
222+
ptpInputPin, exists := resolvedSubsystem.DPLL.PhaseInputs[tc.expectedPtpInput]
223+
assert.True(t, exists, "PhaseInputs should contain ptpInputPin %s from template", tc.expectedPtpInput)
224+
assert.NotNil(t, ptpInputPin.Frequency)
225+
assert.Equal(t, int64(1), *ptpInputPin.Frequency, "PTP input should be 1 PPS")
226+
227+
// Ethernet ports should be derived (all upstream ports)
228+
assert.NotEmpty(t, resolvedSubsystem.Ethernet)
229+
assert.Len(t, resolvedSubsystem.Ethernet, 1)
230+
assert.Equal(t, upstreamPorts, resolvedSubsystem.Ethernet[0].Ports,
231+
"Ethernet ports should match upstream ports")
232+
233+
// Verify behavior derivation
234+
assert.NotNil(t, resolvedConfig.Spec.Profile.ClockChain.Behavior)
235+
behavior := resolvedConfig.Spec.Profile.ClockChain.Behavior
236+
237+
// Sources should be instantiated with resolved variables
238+
assert.NotEmpty(t, behavior.Sources)
239+
ptpSource := findSourceByName(behavior.Sources, "PTP")
240+
assert.NotNil(t, ptpSource, "PTP source should be present")
241+
if ptpSource == nil {
242+
t.Fatal("PTP source should be present")
243+
}
244+
assert.Equal(t, "ptpTimeReceiver", ptpSource.SourceType)
245+
assert.Equal(t, "leader", ptpSource.Subsystem, "Subsystem should be resolved")
246+
assert.Equal(t, tc.expectedPtpInput, ptpSource.BoardLabel, "BoardLabel should be resolved from pinRoles")
247+
assert.Equal(t, upstreamPorts, ptpSource.PTPTimeReceivers,
248+
"PTPTimeReceivers should match upstream ports")
249+
250+
// Conditions should be instantiated with resolved variables
251+
assert.NotEmpty(t, behavior.Conditions)
252+
initCondition := findConditionByName(behavior.Conditions, "Initialize T-BC")
253+
assert.NotNil(t, initCondition, "Initialize T-BC condition should be present")
254+
if initCondition == nil {
255+
t.Fatal("Initialize T-BC condition should be present")
256+
}
257+
// Init condition should use GNSS input pin for DPLL
258+
if len(initCondition.DesiredStates) > 0 && initCondition.DesiredStates[0].DPLL != nil {
259+
assert.Equal(t, tc.expectedGnssInput, initCondition.DesiredStates[0].DPLL.BoardLabel,
260+
"Init condition DPLL should use gnssInputPin from template")
261+
}
248262

249-
lostCondition := findConditionByName(behavior.Conditions, "PTP Source Lost - Leader Holdover")
250-
assert.NotNil(t, lostCondition, "PTP Source Lost condition should be present")
251-
if lostCondition == nil {
252-
t.Fatal("PTP Source Lost condition should be present")
253-
}
263+
lockedCondition := findConditionByName(behavior.Conditions, "PTP Source Locked")
264+
assert.NotNil(t, lockedCondition, "PTP Source Locked condition should be present")
265+
if lockedCondition == nil {
266+
t.Fatal("PTP Source Locked condition should be present")
267+
}
268+
// Locked condition should use PTP input pin for DPLL
269+
if len(lockedCondition.DesiredStates) > 0 && lockedCondition.DesiredStates[0].DPLL != nil {
270+
assert.Equal(t, tc.expectedPtpInput, lockedCondition.DesiredStates[0].DPLL.BoardLabel,
271+
"Locked condition DPLL should use ptpInputPin from template")
272+
}
254273

255-
// Verify template variables were resolved in conditions
256-
// Check that {subsystem} was replaced with "leader"
257-
// Check that {ptpInputPin} was replaced with "GNR-D_SDP0"
258-
// Check that {interface} was replaced with "eno5" (leading interface, not upstream port)
259-
for _, condition := range behavior.Conditions {
260-
for _, desiredState := range condition.DesiredStates {
261-
if desiredState.DPLL != nil {
262-
assert.NotEqual(t, "{subsystem}", desiredState.DPLL.Subsystem,
263-
"Subsystem variable should be resolved")
264-
assert.NotEqual(t, "{ptpInputPin}", desiredState.DPLL.BoardLabel,
265-
"ptpInputPin variable should be resolved")
274+
lostCondition := findConditionByName(behavior.Conditions, "PTP Source Lost - Leader Holdover")
275+
assert.NotNil(t, lostCondition, "PTP Source Lost condition should be present")
276+
if lostCondition == nil {
277+
t.Fatal("PTP Source Lost condition should be present")
266278
}
267-
}
279+
280+
// Verify template variables were resolved in conditions
281+
for _, condition := range behavior.Conditions {
282+
for _, desiredState := range condition.DesiredStates {
283+
if desiredState.DPLL != nil {
284+
assert.NotEqual(t, "{subsystem}", desiredState.DPLL.Subsystem,
285+
"Subsystem variable should be resolved")
286+
assert.NotEqual(t, "{ptpInputPin}", desiredState.DPLL.BoardLabel,
287+
"ptpInputPin variable should be resolved")
288+
}
289+
}
290+
}
291+
})
268292
}
269293
}
270294

0 commit comments

Comments
 (0)