diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 8b4a83e55..bdd5f3b47 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "syscall" "time" @@ -108,6 +109,17 @@ type ProcessManager struct { ptpEventHandler *event.EventHandler } +// findProcessesByName returns a list of processes with the given name +func (p *ProcessManager) findProcessesByName(name string) []*ptpProcess { + var procs []*ptpProcess + for _, proc := range p.process { + if proc != nil && proc.name == name { + procs = append(procs, proc) + } + } + return procs +} + // NewProcessManager is used by unit tests func NewProcessManager() *ProcessManager { processPTP := &ptpProcess{} @@ -251,6 +263,7 @@ type ptpProcess struct { dn *Daemon cmdSetEnabledMutex sync.Mutex offset float64 + skipInitialStartup string } func (p *ptpProcess) Stopped() bool { @@ -303,6 +316,9 @@ type Daemon struct { // Allow vendors to include plugins pluginManager plugin.PluginManager + + delayedPhc2sys atomic.Bool + delayedPhc2sysMu sync.Mutex // protects skipInitialStartup on phc2sys processes } // New LinuxPTP is called by daemon to generate new linuxptp instance @@ -467,9 +483,7 @@ func (dn *Daemon) applyNodePTPProfiles() error { if p != nil { p.eventCh = dn.processManager.eventChannel // start ptp4l process early , it doesn't have - if p.depProcess == nil { - go p.cmdRun(dn.stdoutToSocket, &dn.pluginManager) - } else { + if p.depProcess != nil { for _, d := range p.depProcess { if d != nil { time.Sleep(3 * time.Second) @@ -491,11 +505,24 @@ func (dn *Daemon) applyNodePTPProfiles() error { glog.Infof("enabling dep process %s with Max %d Min %d Holdover %d", d.Name(), p.ptpClockThreshold.MaxOffsetThreshold, p.ptpClockThreshold.MinOffsetThreshold, p.ptpClockThreshold.HoldOverTimeout) } } - go p.cmdRun(dn.stdoutToSocket, &dn.pluginManager) } + if p.skipInitialStartup != "" { + glog.Infof("Delaying %s startup: %s", p.name, p.skipInitialStartup) + continue + } + go p.cmdRun(dn.stdoutToSocket, &dn.pluginManager) dn.pluginManager.AfterRunPTPCommand(&p.nodeProfile, p.name) } } + // Arm the delayed-phc2sys flag now that the startup loop is complete. + // Keeping it false during the loop ensures HandleDelayedPhc2sysStartup + // cannot clear skipInitialStartup and race with the loop's skip check. + for _, p := range dn.processManager.process { + if p != nil && p.skipInitialStartup != "" { + dn.delayedPhc2sys.Store(true) + break + } + } dn.pluginManager.PopulateHwConfig(dn.hwconfigs) *dn.refreshNodePtpDevice = true dn.readyTracker.setConfig(true) @@ -792,6 +819,13 @@ func (dn *Daemon) applyNodePtpProfile(runID int, nodeProfile *ptpv1.PtpProfile) // TODO addScheduling dprocess.depProcess = append(dprocess.depProcess, pmcProcess) } + } else if pProcess == phc2sysProcessName { + glog.Infof("Setting up phc2sys (%s)", clockType) + // Delay phc2sys startup until the clock source has synchronized. + dn.delayedPhc2sysMu.Lock() + dprocess.skipInitialStartup = "waiting for PHC synchronization before adjusting system time" + dn.delayedPhc2sysMu.Unlock() + glog.Infof("Delaying phc2sys startup: %s", dprocess.skipInitialStartup) } else if pProcess == ts2phcProcessName { //& if the x plugin is enabled if clockType == event.GM { if output.gnss_serial_port == "" { @@ -1302,6 +1336,46 @@ func (p *ptpProcess) MonitorEvent(offset float64, clockState string) { // not implemented } +// HandleDelayedPhc2sysStartup checks if phc2sys was delayed and if the current offset is within the 1s threshold, starts it. +func (dn *Daemon) HandleDelayedPhc2sysStartup(source string, offset float64, profileName *string) { + if profileName == nil { + return + } + if !dn.delayedPhc2sys.Load() { + return + } + if math.Abs(offset) < 1000000000 { + dn.delayedPhc2sysMu.Lock() + defer dn.delayedPhc2sysMu.Unlock() + if !dn.delayedPhc2sys.Load() { // re-check under lock + return + } + for _, proc := range dn.processManager.findProcessesByName(phc2sysProcessName) { + if proc.skipInitialStartup == "" || proc.nodeProfile.Name == nil { + continue + } + // Match if the reporting process is in the same profile as phc2sys, + // or in one of phc2sys's HA-linked profiles. The latter handles the + // case where phc2sys is in a dedicated profile with no ptp4l of its + // own (e.g. test-dual-nic-bc-ha with haProfiles=master1,master2). + _, linkedByHA := proc.haProfile[*profileName] + if *proc.nodeProfile.Name == *profileName || linkedByHA { + glog.Infof("%s offset is %f (sub-second); enabling %s", source, offset, proc.name) + proc.skipInitialStartup = "" + proc.cmdSetEnabled(true) + dn.pluginManager.AfterRunPTPCommand(&proc.nodeProfile, proc.name) + } + } + // Only clear the daemon-wide flag once no phc2sys processes remain delayed. + for _, proc := range dn.processManager.findProcessesByName(phc2sysProcessName) { + if proc.skipInitialStartup != "" { + return + } + } + dn.delayedPhc2sys.Store(false) + } +} + func (p *ptpProcess) ProcessTs2PhcEvents(ptpOffset float64, source string, iface string, state event.PTPState, extraValue map[event.ValueType]interface{}) { var ptpState event.PTPState ptpState = state @@ -1313,6 +1387,9 @@ func (p *ptpProcess) ProcessTs2PhcEvents(ptpOffset float64, source string, iface } if source == ts2phcProcessName { // for ts2phc send it to event to create metrics and events + if p.dn != nil { + p.dn.HandleDelayedPhc2sysStartup(source, ptpOffset, p.nodeProfile.Name) + } var values = make(map[event.ValueType]interface{}) values[event.OFFSET] = ptpOffsetInt64 diff --git a/pkg/daemon/daemon_internal_test.go b/pkg/daemon/daemon_internal_test.go index 76bae6fe3..ba8560bf3 100644 --- a/pkg/daemon/daemon_internal_test.go +++ b/pkg/daemon/daemon_internal_test.go @@ -5,6 +5,7 @@ package daemon import ( "os" "strings" + "sync" "testing" "time" @@ -16,6 +17,10 @@ import ( "sigs.k8s.io/yaml" ) +const ( + testSkipStartupReason = "delayed" +) + func loadProfile(path string) (*ptpv1.PtpProfile, error) { profileData, err := os.ReadFile(path) if err != nil { @@ -110,9 +115,19 @@ func Test_applyProfile_synce(t *testing.T) { func Test_applyProfile_TBC(t *testing.T) { defer clean(t) - testDataFiles := []string{ - "testdata/profile-tbc-tt.yaml", - "testdata/profile-tbc-tr.yaml", + + tests := []struct { + dataFile string + expectedProcesses []string + }{ + { + dataFile: "testdata/profile-tbc-tt.yaml", + expectedProcesses: []string{ptp4lProcessName}, + }, + { + dataFile: "testdata/profile-tbc-tr.yaml", + expectedProcesses: []string{ptp4lProcessName, ptp4lProcessName, ts2phcProcessName, phc2sysProcessName}, + }, } stopCh := make(<-chan struct{}) assert.NoError(t, leap.MockLeapFile()) @@ -141,17 +156,101 @@ func Test_applyProfile_TBC(t *testing.T) { ) assert.NotNil(t, dn) - for i := range len(testDataFiles) { + for _, test := range tests { mkPath(t) - profile, err := loadProfile(testDataFiles[i]) + profile, err := loadProfile(test.dataFile) assert.NoError(t, err) // Will assert inside in case of error: err = dn.applyNodePtpProfile(0, profile) assert.NoError(t, err) + + // Ensure for T-BC that phc2sys is selected for delayed-start + actualProcesses := []string{} + for _, p := range dn.processManager.process { + actualProcesses = append(actualProcesses, p.name) + if p.name == phc2sysProcessName { + assert.NotEmpty(t, p.skipInitialStartup, "Ensure phc2sys is startup-delayed for T-BC") + } + } + assert.ElementsMatch(t, test.expectedProcesses, actualProcesses, "Ensure T-BC has the required processes prepared (%s)", test.dataFile) clean(t) } } +func Test_applyProfile_TGM(t *testing.T) { + defer clean(t) + mkPath(t) + + stopCh := make(<-chan struct{}) + assert.NoError(t, leap.MockLeapFile()) + defer func() { + close(leap.LeapMgr.Close) + time.Sleep(100 * time.Millisecond) + assert.Nil(t, leap.LeapMgr) + }() + dn := New( + "test-node-name", + "openshift-ptp", + false, + nil, + &LinuxPTPConfUpdate{ + UpdateCh: make(chan bool), + NodeProfiles: []ptpv1.PtpProfile{}, + }, + stopCh, + []string{"e810"}, + &[]ptpv1.HwConfig{}, + nil, + make(chan bool), + 30, + &ReadyTracker{}, + ) + assert.NotNil(t, dn) + + profile, err := loadProfile("testdata/profile-tgm.yaml") + assert.NoError(t, err) + + err = dn.applyNodePtpProfile(0, profile) + assert.NoError(t, err) + + var ts2phcProc *ptpProcess + var ptp4lProc *ptpProcess + for _, p := range dn.processManager.process { + switch p.name { + case ts2phcProcessName: + ts2phcProc = p + case ptp4lProcessName: + ptp4lProc = p + case phc2sysProcessName: + assert.NotEmpty(t, p.skipInitialStartup, "Ensure phc2sys is startup-delayed for T-GM") + } + } + + // 1. ts2phc must have both gpsd and gpspipe as dependent processes. + if assert.NotNil(t, ts2phcProc, "ts2phc process should exist for T-GM profile") { + depNames := make([]string, len(ts2phcProc.depProcess)) + for i, d := range ts2phcProc.depProcess { + depNames[i] = d.Name() + } + assert.Contains(t, depNames, GPSD_PROCESSNAME, "gpsd must be a dependent process of ts2phc for T-GM") + assert.Contains(t, depNames, GPSPIPE_PROCESSNAME, "gpspipe must be a dependent process of ts2phc for T-GM") + } + + // 2. ptp4l should NOT have a PMC dependent process for GM profiles. + if assert.NotNil(t, ptp4lProc, "ptp4l process should exist for T-GM profile") { + for _, d := range ptp4lProc.depProcess { + assert.NotEqual(t, "pmc", d.Name(), "ptp4l should not have PMC as a dependent process for T-GM") + } + } + + // 3. ts2phc opts must include holdover and servo parameters for GM. + // These are auto-appended by applyNodePtpProfile for GM clock types. + ts2phcOpts := *profile.Ts2PhcOpts + assert.Contains(t, ts2phcOpts, "--ts2phc.holdover", "ts2phc opts must include holdover timeout for T-GM") + assert.Contains(t, ts2phcOpts, "--servo_offset_threshold", "ts2phc opts must include servo offset threshold for T-GM") + assert.Contains(t, ts2phcOpts, "--servo_num_offset_values 10", "ts2phc opts must include servo num offset values for T-GM") +} + func TestGetPTPClockId_ValidInput(t *testing.T) { p := &ptpProcess{ nodeProfile: ptpv1.PtpProfile{ @@ -663,3 +762,234 @@ func TestEmitClockClassLogs_EmitsWithNilParentDS(t *testing.T) { pm.EmitClockClassLogs() }, "EmitClockClassLogs should not panic with nil parentDS") } + +// --- ReadyTracker.Ready() unit tests --- + +func makeReadyTracker(processes []*ptpProcess) *ReadyTracker { + return &ReadyTracker{ + config: true, + processManager: &ProcessManager{ + process: processes, + }, + } +} + +func TestReady_NoProcesses(t *testing.T) { + rt := makeReadyTracker(nil) + ok, msg := rt.Ready() + assert.False(t, ok) + assert.Contains(t, msg, "No processes") +} + +func TestReady_AllRunningWithMetrics(t *testing.T) { + rt := makeReadyTracker([]*ptpProcess{ + {name: ptp4lProcessName, stopped: false, hasCollectedMetrics: true}, + {name: phc2sysProcessName, stopped: false, hasCollectedMetrics: true}, + }) + ok, msg := rt.Ready() + assert.True(t, ok, msg) +} + +func TestReady_StoppedProcessReportsNotReady(t *testing.T) { + rt := makeReadyTracker([]*ptpProcess{ + {name: ptp4lProcessName, stopped: false, hasCollectedMetrics: true}, + {name: phc2sysProcessName, stopped: true}, + }) + ok, msg := rt.Ready() + assert.False(t, ok) + assert.Contains(t, msg, "Stopped") + assert.Contains(t, msg, phc2sysProcessName) +} + +func TestReady_DelayedPhc2sysNotReportedAsStopped(t *testing.T) { + // phc2sys is intentionally delayed (skipInitialStartup set): the pod + // should be considered ready without it. + rt := makeReadyTracker([]*ptpProcess{ + {name: ptp4lProcessName, stopped: false, hasCollectedMetrics: true}, + {name: phc2sysProcessName, stopped: true, skipInitialStartup: testSkipStartupReason}, + }) + ok, msg := rt.Ready() + assert.True(t, ok, msg) +} + +func TestReady_NilProcessEntrySkipped(t *testing.T) { + // nil slots in the process slice must not panic. + rt := makeReadyTracker([]*ptpProcess{ + {name: ptp4lProcessName, stopped: false, hasCollectedMetrics: true}, + nil, + }) + ok, msg := rt.Ready() + assert.True(t, ok, msg) +} + +func TestReady_AllProcessesDelayed(t *testing.T) { + // If every process has skipInitialStartup set (e.g. a phc2sys-only HA profile + // where ptp4l lives in separate profiles), the pod must not report ready. + rt := makeReadyTracker([]*ptpProcess{ + {name: phc2sysProcessName, stopped: true, skipInitialStartup: testSkipStartupReason}, + }) + ok, msg := rt.Ready() + assert.False(t, ok) + assert.Contains(t, msg, "No processes") +} + +func TestDelayedPhc2sysStartup(t *testing.T) { + profileName := "test-profile" + nodeProfile := ptpv1.PtpProfile{ + Name: &profileName, + } + + pm := &ProcessManager{ + process: []*ptpProcess{}, + } + + dn := &Daemon{ + processManager: pm, + } + + phc2sys := &ptpProcess{ + name: phc2sysProcessName, + skipInitialStartup: testSkipStartupReason, + nodeProfile: nodeProfile, + dn: dn, + execMutex: sync.Mutex{}, + stopped: true, // Simulated stopped state + } + + ts2phc := &ptpProcess{ + name: ts2phcProcessName, + nodeProfile: nodeProfile, + dn: dn, + eventCh: make(chan event.EventChannel, 10), + ptpClockThreshold: &ptpv1.PtpClockThreshold{ + MaxOffsetThreshold: 1000, + MinOffsetThreshold: -1000, + }, + } + + pm.process = append(pm.process, phc2sys, ts2phc) + + // 1. Simulate large offset (> 1s) + dn.delayedPhc2sys.Store(true) + largeOffset := 37000000000.0 // 37s + ts2phc.ProcessTs2PhcEvents(largeOffset, ts2phcProcessName, "eth0", event.PTP_FREERUN, nil) + + // Verify phc2sys is still delayed + assert.Equal(t, testSkipStartupReason, phc2sys.skipInitialStartup) + + // 2. Exact boundary (== 1s): should NOT clear the delay (condition is strictly <) + phc2sys.skipInitialStartup = testSkipStartupReason + dn.delayedPhc2sys.Store(true) + boundaryOffset := 1000000000.0 // exactly 1s + ts2phc.ProcessTs2PhcEvents(boundaryOffset, ts2phcProcessName, "eth0", event.PTP_FREERUN, nil) + assert.Equal(t, testSkipStartupReason, phc2sys.skipInitialStartup, "At the 1s boundary phc2sys should remain delayed") + assert.True(t, dn.delayedPhc2sys.Load()) + + // 3. Negative sub-second offset (-0.5s): math.Abs should clear the delay + phc2sys.skipInitialStartup = testSkipStartupReason + dn.delayedPhc2sys.Store(true) + negSmallOffset := -500000000.0 // -0.5s + ts2phc.ProcessTs2PhcEvents(negSmallOffset, ts2phcProcessName, "eth0", event.PTP_LOCKED, nil) + assert.Equal(t, "", phc2sys.skipInitialStartup, "Negative sub-second offset should clear the delay") + assert.False(t, dn.delayedPhc2sys.Load()) + + // 4. Negative super-second offset (-2s): should NOT clear the delay + phc2sys.skipInitialStartup = testSkipStartupReason + dn.delayedPhc2sys.Store(true) + negLargeOffset := -2000000000.0 // -2s + ts2phc.ProcessTs2PhcEvents(negLargeOffset, ts2phcProcessName, "eth0", event.PTP_FREERUN, nil) + assert.Equal(t, testSkipStartupReason, phc2sys.skipInitialStartup, "Negative super-second offset should keep the delay") + assert.True(t, dn.delayedPhc2sys.Load()) + + // 5. Simulate sub-second offset (< 1s): original passing case + phc2sys.skipInitialStartup = testSkipStartupReason + dn.delayedPhc2sys.Store(true) + smallOffset := 500000000.0 // 0.5s + ts2phc.ProcessTs2PhcEvents(smallOffset, ts2phcProcessName, "eth0", event.PTP_LOCKED, nil) + assert.Equal(t, "", phc2sys.skipInitialStartup) + assert.False(t, dn.delayedPhc2sys.Load()) +} + +// TestDelayedPhc2sysStartup_HAProfile verifies that a phc2sys process in a +// dedicated HA profile (with no ptp4l of its own) is correctly started when +// a sub-second offset is reported by a ptp4l in one of its haProfile entries. +func TestDelayedPhc2sysStartup_HAProfile(t *testing.T) { + phc2sysProfileName := "test-dual-nic-bc-ha" + master1ProfileName := "test-bc-master1" + master2ProfileName := "test-bc-master2" + + phc2sysNodeProfile := ptpv1.PtpProfile{Name: &phc2sysProfileName} + master1NodeProfile := ptpv1.PtpProfile{Name: &master1ProfileName} + master2NodeProfile := ptpv1.PtpProfile{Name: &master2ProfileName} + + pm := &ProcessManager{process: []*ptpProcess{}} + dn := &Daemon{processManager: pm} + + phc2sys := &ptpProcess{ + name: phc2sysProcessName, + skipInitialStartup: testSkipStartupReason, + nodeProfile: phc2sysNodeProfile, + haProfile: map[string][]string{master1ProfileName: {"ens1f1"}, master2ProfileName: {"ens2f0"}}, + dn: dn, + execMutex: sync.Mutex{}, + stopped: true, + } + + ptp4lMaster1 := &ptpProcess{ + name: ptp4lProcessName, + nodeProfile: master1NodeProfile, + dn: dn, + eventCh: make(chan event.EventChannel, 10), + ptpClockThreshold: &ptpv1.PtpClockThreshold{ + MaxOffsetThreshold: 1000, + MinOffsetThreshold: -1000, + }, + } + + ptp4lMaster2 := &ptpProcess{ + name: ptp4lProcessName, + nodeProfile: master2NodeProfile, + dn: dn, + eventCh: make(chan event.EventChannel, 10), + ptpClockThreshold: &ptpv1.PtpClockThreshold{ + MaxOffsetThreshold: 1000, + MinOffsetThreshold: -1000, + }, + } + + pm.process = append(pm.process, phc2sys, ptp4lMaster1, ptp4lMaster2) + + // A large offset from master1 must NOT start phc2sys. + dn.delayedPhc2sys.Store(true) + dn.HandleDelayedPhc2sysStartup(ptp4lProcessName, 37000000000.0, &master1ProfileName) + assert.Equal(t, testSkipStartupReason, phc2sys.skipInitialStartup, + "large offset from HA-linked profile should not start phc2sys") + + // A sub-second offset from master2 (different profile from phc2sys) MUST start it. + dn.delayedPhc2sys.Store(true) + dn.HandleDelayedPhc2sysStartup(ptp4lProcessName, 500000000.0, &master2ProfileName) + assert.Equal(t, "", phc2sys.skipInitialStartup, + "sub-second offset from HA-linked profile should start phc2sys") + assert.False(t, dn.delayedPhc2sys.Load()) +} + +func TestFindProcessesByName(t *testing.T) { + pm := &ProcessManager{ + process: []*ptpProcess{ + {name: "ptp4l"}, + {name: "phc2sys"}, + {name: "ptp4l"}, + }, + } + + procs := pm.findProcessesByName("ptp4l") + assert.Equal(t, 2, len(procs)) + assert.Equal(t, "ptp4l", procs[0].name) + assert.Equal(t, "ptp4l", procs[1].name) + + procs = pm.findProcessesByName("phc2sys") + assert.Equal(t, 1, len(procs)) + + procs = pm.findProcessesByName("nonexistent") + assert.Equal(t, 0, len(procs)) +} diff --git a/pkg/daemon/log_parsing.go b/pkg/daemon/log_parsing.go index eeb681d86..7f8848fe8 100644 --- a/pkg/daemon/log_parsing.go +++ b/pkg/daemon/log_parsing.go @@ -119,7 +119,13 @@ func processParsedMetrics(process *ptpProcess, ptpMetrics *parser.Metrics) { if ptpMetrics.Iface != "" && configName != "" { masterOffsetIface.set(configName, ptpMetrics.Iface) } + if ptpMetrics.Source == "master" && process.dn != nil { + process.dn.HandleDelayedPhc2sysStartup(process.name, ptpMetrics.Offset, process.nodeProfile.Name) + } case ts2phcProcessName: + if process.dn != nil { + process.dn.HandleDelayedPhc2sysStartup(process.name, ptpMetrics.Offset, process.nodeProfile.Name) + } // Send event for ts2phc eventSource := process.ifaces.GetEventSource(process.ifaces.GetPhcID2IFace(ptpMetrics.Iface)) values := map[event.ValueType]interface{}{ diff --git a/pkg/daemon/ready.go b/pkg/daemon/ready.go index 7038c7ba9..7454e7771 100644 --- a/pkg/daemon/ready.go +++ b/pkg/daemon/ready.go @@ -32,7 +32,12 @@ func (rt *ReadyTracker) Ready() (bool, string) { notRunning := strings.Builder{} noMetrics := strings.Builder{} + activeCount := 0 for _, p := range rt.processManager.process { + if p == nil || p.skipInitialStartup != "" { + continue + } + activeCount++ if p.Stopped() { if notRunning.Len() > 0 { notRunning.WriteString(", ") @@ -46,6 +51,10 @@ func (rt *ReadyTracker) Ready() (bool, string) { } } + if activeCount == 0 { + return false, "No processes have started" + } + if notRunning.Len() > 0 { return false, "Stopped process(es): " + notRunning.String() } diff --git a/pkg/daemon/testdata/profile-tbc-tr.yaml b/pkg/daemon/testdata/profile-tbc-tr.yaml index a2babe046..52bebda7c 100644 --- a/pkg/daemon/testdata/profile-tbc-tr.yaml +++ b/pkg/daemon/testdata/profile-tbc-tr.yaml @@ -6,30 +6,31 @@ ptpSettings: clockId[ens8f0]: 5799633565432596448 inSyncConditionThreshold: 10 inSyncConditionTimes: 2 + clockType: "T-BC" phc2sysOpts: -r -n 24 -N 8 -u 0 -s ens4f0 plugins: e810: enableDefaultConfig: false interconnections: - - id: ens4f0 - part: E810-XXVDA4T - gnssInput: false - upstreamPort: ens4f0 - phaseOutputConnectors: - - SMA1 - - SMA2 - - id: ens5f0 - part: E810-XXVDA4T - inputConnector: - connector: SMA1 - delayPs: 920 - - id: ens8f0 - part: E810-XXVDA4T - inputConnector: - connector: SMA1 - delayPs: 920 - phaseOutputConnectors: - - U.FL1 + - id: ens4f0 + part: E810-XXVDA4T + gnssInput: false + upstreamPort: ens4f0 + phaseOutputConnectors: + - SMA1 + - SMA2 + - id: ens5f0 + part: E810-XXVDA4T + inputConnector: + connector: SMA1 + delayPs: 920 + - id: ens8f0 + part: E810-XXVDA4T + inputConnector: + connector: SMA1 + delayPs: 920 + phaseOutputConnectors: + - U.FL1 pins: ens4f0: SMA1: 2 1 @@ -173,4 +174,4 @@ ts2phcConf: | ts2phc.extts_polarity rising ts2phc.extts_correction 0 ts2phc.master 0 -ts2phcOpts: '-s generic -a --ts2phc.rh_external_pps 1' +ts2phcOpts: "-s generic -a --ts2phc.rh_external_pps 1" diff --git a/pkg/daemon/testdata/profile-tbc-tt.yaml b/pkg/daemon/testdata/profile-tbc-tt.yaml index 613cf97c2..4803b991a 100644 --- a/pkg/daemon/testdata/profile-tbc-tt.yaml +++ b/pkg/daemon/testdata/profile-tbc-tt.yaml @@ -5,6 +5,7 @@ ptpSettings: clockId[ens5f0]: 5799633565433967128 clockId[ens8f0]: 5799633565432596448 controllingProfile: 01-tbc-tr + clockType: "T-BC" ptp4lConf: | [ens4f0] masterOnly 1 diff --git a/pkg/daemon/testdata/profile-tgm.yaml b/pkg/daemon/testdata/profile-tgm.yaml new file mode 100644 index 000000000..4fa5273aa --- /dev/null +++ b/pkg/daemon/testdata/profile-tgm.yaml @@ -0,0 +1,237 @@ +name: grandmaster +ptpSettings: + unitTest: /tmp/test + clockId[ens4f0]: 5799633565432596414 + clockId[ens5f0]: 5799633565433967128 + clockId[ens8f0]: 5799633565432596448 +phc2sysOpts: -a -r -n 24 -N 8 -R 16 -u 0 +plugins: + e810: + enableDefaultConfig: false + interconnections: + - id: ens4f0 + part: E810-XXVDA4T + gnssInput: true + phaseOutputConnectors: + - SMA1 + - SMA2 + - id: ens5f0 + part: E810-XXVDA4T + inputConnector: + connector: SMA1 + delayPs: 920 + - id: ens8f0 + part: E810-XXVDA4T + inputConnector: + connector: SMA1 + delayPs: 920 + phaseOutputConnectors: + - U.FL1 + pins: + ens4f0: + SMA1: 2 1 + SMA2: 2 2 + U.FL1: 0 1 + U.FL2: 0 2 + ens5f0: + SMA1: 1 1 + SMA2: 0 2 + U.FL1: 0 1 + U.FL2: 0 2 + ens8f0: + SMA1: 1 1 + SMA2: 0 2 + U.FL1: 0 1 + U.FL2: 0 2 + settings: + LocalHoldoverTimeout: 14400 + LocalMaxHoldoverOffSet: 1500 + MaxInSpecOffset: 1500 + ublxCmds: + - args: + - -P + - "29.20" + - -z + - CFG-HW-ANT_CFG_VOLTCTRL,1 + reportOutput: false + - args: + - -P + - "29.20" + - -e + - GPS + reportOutput: false + - args: + - -P + - "29.20" + - -d + - Galileo + reportOutput: false + - args: + - -P + - "29.20" + - -d + - GLONASS + reportOutput: false + - args: + - -P + - "29.20" + - -d + - BeiDou + reportOutput: false + - args: + - -P + - "29.20" + - -d + - SBAS + reportOutput: false + - args: + - -P + - "29.20" + - -t + - -w + - "5" + - -v + - "1" + - -e + - SURVEYIN,600,50000 + reportOutput: true + - args: + - -P + - "29.20" + - -p + - MON-HW + reportOutput: true + - args: + - -P + - "29.20" + - -p + - CFG-MSG,1,38,248 + reportOutput: true +ptp4lConf: | + [ens7f0] + masterOnly 1 + [ens7f1] + masterOnly 1 + [global] + # + # Default Data Set + # + twoStepFlag 1 + priority1 128 + priority2 128 + domainNumber 24 + #utc_offset 37 + clockClass 6 + clockAccuracy 0x27 + offsetScaledLogVariance 0xFFFF + free_running 0 + freq_est_interval 1 + dscp_event 0 + dscp_general 0 + dataset_comparison G.8275.x + G.8275.defaultDS.localPriority 128 + # + # Port Data Set + # + logAnnounceInterval -3 + logSyncInterval -4 + logMinDelayReqInterval -4 + logMinPdelayReqInterval 0 + announceReceiptTimeout 3 + syncReceiptTimeout 0 + delayAsymmetry 0 + fault_reset_interval 4 + neighborPropDelayThresh 20000000 + masterOnly 0 + G.8275.portDS.localPriority 128 + # + # Run time options + # + assume_two_step 0 + logging_level 6 + path_trace_enabled 0 + follow_up_info 0 + hybrid_e2e 0 + inhibit_multicast_service 0 + net_sync_monitor 0 + tc_spanning_tree 0 + tx_timestamp_timeout 50 + unicast_listen 0 + unicast_master_table 0 + unicast_req_duration 3600 + use_syslog 1 + verbose 0 + summary_interval -4 + kernel_leap 1 + check_fup_sync 0 + # + # Servo Options + # + pi_proportional_const 0.0 + pi_integral_const 0.0 + pi_proportional_scale 0.0 + pi_proportional_exponent -0.3 + pi_proportional_norm_max 0.7 + pi_integral_scale 0.0 + pi_integral_exponent 0.4 + pi_integral_norm_max 0.3 + step_threshold 0.0 + first_step_threshold 0.00002 + clock_servo pi + sanity_freq_limit 200000000 + ntpshm_segment 0 + # + # Transport options + # + transportSpecific 0x0 + ptp_dst_mac 01:1B:19:00:00:00 + p2p_dst_mac 01:80:C2:00:00:0E + udp_ttl 1 + udp6_scope 0x0E + uds_address /var/run/ptp4l + # + # Default interface options + # + clock_type BC + network_transport L2 + delay_mechanism E2E + time_stamping hardware + tsproc_mode filter + delay_filter moving_median + delay_filter_length 10 + egressLatency 0 + ingressLatency 0 + boundary_clock_jbod 0 + # + # Clock description + # + productDescription ;; + revisionData ;; + manufacturerIdentity 00:00:00 + userDescription ; + timeSource 0x20 +ptp4lOpts: -2 --summary_interval -4 +ptpSchedulingPolicy: SCHED_FIFO +ptpSchedulingPriority: 10 +ts2phcConf: | + [nmea] + ts2phc.master 1 + [global] + use_syslog 0 + verbose 1 + logging_level 7 + ts2phc.pulsewidth 100000000 + leapfile /usr/share/zoneinfo/leap-seconds.list + [ens7f0] + ts2phc.master 0 + ts2phc.extts_polarity rising + ts2phc.extts_correction 0 + [ens4f0] + ts2phc.master 0 + ts2phc.extts_polarity rising + ts2phc.extts_correction 0 + [ens2f0] + ts2phc.master 0 + ts2phc.extts_polarity rising + ts2phc.extts_correction 0 +ts2phcOpts: ' '