diff --git a/plugins/ptp_operator/event/event.go b/plugins/ptp_operator/event/event.go index febaa914..65f653f4 100644 --- a/plugins/ptp_operator/event/event.go +++ b/plugins/ptp_operator/event/event.go @@ -38,11 +38,14 @@ const ( DPLL ClockSourceType = "dpll" ) +// DependingClockState ... +type DependingClockState []*ClockState + // PTPEventState ... type PTPEventState struct { sync.Mutex CurrentPTPStateEvent ptp.SyncState - DependsOn map[string]*ClockState + DependsOn map[string]DependingClockState // [dpll][ens2f0]State Type ptp.EventType } @@ -56,7 +59,7 @@ type ClockState struct { Value map[string]int64 Metric map[string]*PMetric NodeName string - HelpText string + HelpText map[string]string } // PMetric ... @@ -66,6 +69,28 @@ type PMetric struct { metricCounter *prometheus.Counter } +func (dt *DependingClockState) hasClockState(iface string) (int, *ClockState) { + for index, dep := range *dt { + if *dep.IFace == iface { + return index, dep + } + } + return -1, nil +} + +func (dt *DependingClockState) hasMetric() map[string]*PMetric { + for _, dep := range *dt { + return dep.Metric + } + return nil +} +func (dt *DependingClockState) hasMetricHelp() map[string]string { + for _, dep := range *dt { + return dep.HelpText + } + return nil +} + // UpdateCurrentEventState ... func (p *PTPEventState) UpdateCurrentEventState(c ClockState) ptp.SyncState { p.Lock() @@ -73,8 +98,15 @@ func (p *PTPEventState) UpdateCurrentEventState(c ClockState) ptp.SyncState { p.Unlock() }() // if the current state is not locked and the new state is locked then update the current state - if d, ok := p.DependsOn[c.Process]; ok { + if dep, ok := p.DependsOn[c.Process]; ok { + index, d := dep.hasClockState(*c.IFace) + // update the clock state if found or else + if index == -1 { + d = &ClockState{} + dep = append(dep, d) + } d.Offset = c.Offset + d.IFace = c.IFace d.State = c.State d.Process = c.Process d.Value = c.Value @@ -83,13 +115,17 @@ func (p *PTPEventState) UpdateCurrentEventState(c ClockState) ptp.SyncState { if c.Offset != nil { d.Offset = pointer.Float64(*c.Offset) } - p.DependsOn[c.Process] = d + for k, v := range c.Value { iface := "" if d.IFace != nil { iface = *d.IFace } - if d.Metric[k].metricGauge == nil { + if d.Metric == nil { + d.Metric = dep.hasMetric() + d.HelpText = dep.hasMetricHelp() + } + if d.Metric[k].metricGauge != nil { d.Metric[k].metricGauge.With(map[string]string{"from": d.Process, "process": d.Process, "node": d.NodeName, "iface": iface}).Set(float64(v)) } @@ -117,13 +153,18 @@ func (p *PTPEventState) UpdateCurrentEventState(c ClockState) ptp.SyncState { Namespace: ptpNamespace, Subsystem: ptpSubsystem, Name: k, - Help: c.HelpText, + Help: func() string { + if h, ok2 := c.HelpText[k]; ok2 { + return h + } + return "" + }(), }, []string{"from", "process", "node", "iface"}), metricCounter: nil, } err := prometheus.Register(metrics[k].metricGauge) if err != nil { - log.Info("failed to register metric, metric is already registered") + log.Info("ignore, metric is already registered") } iface := "" if clockState.IFace != nil { @@ -135,7 +176,7 @@ func (p *PTPEventState) UpdateCurrentEventState(c ClockState) ptp.SyncState { "node": clockState.NodeName, "iface": alias}).Set(float64(v)) } clockState.Metric = metrics - p.DependsOn[c.Process] = clockState + p.DependsOn[c.Process] = []*ClockState{clockState} } // if all locked then its locked @@ -143,14 +184,16 @@ func (p *PTPEventState) UpdateCurrentEventState(c ClockState) ptp.SyncState { // if anyone FREERUN then freerun var currentState ptp.SyncState for _, v := range p.DependsOn { - if v.State != ptp.LOCKED { - if v.State == ptp.HOLDOVER { - currentState = v.State - break + for _, dd := range v { + if dd.State != ptp.LOCKED { + if dd.State == ptp.HOLDOVER { + currentState = dd.State + break + } + currentState = dd.State + } else if currentState == "" { + currentState = dd.State } - currentState = v.State - } else if currentState == "" { - currentState = v.State } } p.CurrentPTPStateEvent = currentState @@ -162,9 +205,11 @@ func (p *PTPEventState) UnRegisterMetrics(processName string) { // write a functions to unregister metric from dependence on object if d, ok := p.DependsOn[processName]; ok { // write loop d.Metric - if d.Metric != nil { - for _, v := range d.Metric { - prometheus.Unregister(v.metricGauge) + for _, dd := range d { + if dd.Metric != nil { + for _, v := range dd.Metric { + prometheus.Unregister(v.metricGauge) + } } } } @@ -180,11 +225,13 @@ func (p *PTPEventState) UnRegisterAllMetrics() { } for _, d := range p.DependsOn { // write loop d.Metric - if d.Metric != nil { - // unregister metric - for _, v := range d.Metric { - prometheus.Unregister(v.metricGauge) - delete(p.DependsOn, d.Process) + for _, dd := range d { + if dd.Metric != nil { + // unregister metric + for _, v := range dd.Metric { + prometheus.Unregister(v.metricGauge) + delete(p.DependsOn, dd.Process) + } } } } @@ -200,15 +247,17 @@ func (p *PTPEventState) DeleteAllMetrics() { } for _, d := range p.DependsOn { // write loop d.Metric - if d.Metric != nil { - // unregister metric - for _, v := range d.Metric { - if v.metricGauge != nil && d.IFace != nil { - v.metricGauge.Delete(prometheus.Labels{"process": d.Process, "iface": *d.IFace, "node": d.NodeName}) - prometheus.Unregister(v.metricGauge) + for _, dd := range d { + if dd.Metric != nil { + // unregister metric + for _, v := range dd.Metric { + if v.metricGauge != nil && dd.IFace != nil { + v.metricGauge.Delete(prometheus.Labels{"process": dd.Process, "iface": *dd.IFace, "node": dd.NodeName}) + prometheus.Unregister(v.metricGauge) + } } } + delete(p.DependsOn, dd.Process) } - delete(p.DependsOn, d.Process) } } diff --git a/plugins/ptp_operator/event/event_test.go b/plugins/ptp_operator/event/event_test.go index 17bc103e..41b2bd5d 100644 --- a/plugins/ptp_operator/event/event_test.go +++ b/plugins/ptp_operator/event/event_test.go @@ -41,16 +41,33 @@ var testCase = []eventTestCase{{ eventStateObject: &event.PTPEventState{ CurrentPTPStateEvent: ptp.FREERUN, Type: ptp.PtpStateChange, - DependsOn: map[string]*event.ClockState{"GNSS": { - State: ptp.FREERUN, - Offset: pointer.Float64(0), - Process: "GNSS", - }, "DPLL": { - State: ptp.FREERUN, - Offset: pointer.Float64(45670), - Process: "DPLL", + + DependsOn: map[string]event.DependingClockState{ + "TS2phc": { + &event.ClockState{ + State: ptp.FREERUN, + Offset: pointer.Float64(01), + IFace: nil, + Process: "GNSS", + ClockSource: "", + Value: nil, + Metric: nil, + NodeName: "", + HelpText: nil, + }, + &event.ClockState{ + State: ptp.FREERUN, + Offset: pointer.Float64(01), + IFace: nil, + Process: "DPLL", + ClockSource: "", + Value: nil, + Metric: nil, + NodeName: "", + HelpText: nil, + }, + }, }}, - }, input: inputState{ state: ptp.LOCKED, process: "GNSS", @@ -62,47 +79,49 @@ var testCase = []eventTestCase{{ eventStateObject: &event.PTPEventState{ CurrentPTPStateEvent: ptp.FREERUN, Type: ptp.PtpStateChange, - DependsOn: map[string]*event.ClockState{"GNSS": { - State: ptp.LOCKED, - Offset: pointer.Float64(1), - Process: "GNSS", - }, "DPLL": { - State: ptp.LOCKED, - Offset: pointer.Float64(2), - Process: "DPLL", - }, "ptp4l": { - State: ptp.FREERUN, - Offset: pointer.Float64(99902), - Process: "ptp4l", + DependsOn: map[string]event.DependingClockState{ + "TS2phc": { + &event.ClockState{ + State: ptp.LOCKED, + Offset: pointer.Float64(01), + IFace: nil, + Process: "GNSS", + ClockSource: "", + Value: nil, + Metric: nil, + NodeName: "", + HelpText: nil, + }, + &event.ClockState{ + State: ptp.LOCKED, + Offset: pointer.Float64(01), + IFace: nil, + Process: "DPLL", + ClockSource: "", + Value: nil, + Metric: nil, + NodeName: "", + HelpText: nil, + }, + &event.ClockState{ + State: ptp.FREERUN, + Offset: pointer.Float64(99902), + IFace: nil, + Process: "ptp4l", + ClockSource: "", + Value: nil, + Metric: nil, + NodeName: "", + HelpText: nil, + }, + }, }}, - }, input: inputState{ state: ptp.LOCKED, process: "ptp4l", offset: pointer.Float64(05), }, - expectedState: ptp.LOCKED, - }, - { - eventStateObject: &event.PTPEventState{ - CurrentPTPStateEvent: ptp.HOLDOVER, - Type: ptp.PtpStateChange, - DependsOn: map[string]*event.ClockState{"pch2sys": { - State: ptp.LOCKED, - Offset: pointer.Float64(01), - Process: "phc2sys", - }, "ts2phc": { - State: ptp.HOLDOVER, - Offset: pointer.Float64(02), - Process: "ts2phc", - }}, - }, - input: inputState{ - state: ptp.LOCKED, - process: "ts2phc", - offset: pointer.Float64(03), - }, - expectedState: ptp.LOCKED, + expectedState: ptp.FREERUN, }, } diff --git a/plugins/ptp_operator/metrics/logparser.go b/plugins/ptp_operator/metrics/logparser.go index 6c6ba058..f82fc1c5 100644 --- a/plugins/ptp_operator/metrics/logparser.go +++ b/plugins/ptp_operator/metrics/logparser.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/event" - "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/ptp4lconf" "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/stats" "k8s.io/utils/pointer" @@ -15,7 +14,9 @@ import ( log "github.com/sirupsen/logrus" ) -var ptpProcessStatusIdentifier = "PTP_PROCESS_STATUS" +var ( + ptpProcessStatusIdentifier = "PTP_PROCESS_STATUS" +) func extractSummaryMetrics(processName, output string) (iface string, ptpOffset, maxPtpOffset, frequencyAdjustment, delay float64) { // remove everything before the rms string @@ -96,9 +97,9 @@ func extractRegularMetrics(processName, output string) (interfaceName string, pt // remove everything before the rms string // This makes the out to equal // ptp4l[5196819.100]: [ptp4l.0.config] master offset -2162130 s2 freq +22451884 path delay 374976 - // phc2sys[4268818.286]: [] CLOCK_REALTIME phc offset -62 s0 freq -78368 delay 1100 - // phc2sys[4268818.287]: [] ens5f1 phc offset -92 s0 freq -890 delay 2464 ( this is down) - // phc2sys[4268818.287]: [] ens5f0 phc offset -47 s2 freq -2047 delay 2438 + // phc2sys[4268818.286]: [ptp4l.0.config] CLOCK_REALTIME phc offset -62 s0 freq -78368 delay 1100 + // phc2sys[4268818.287]: [ptp4l.0.config] ens5f1 phc offset -92 s0 freq -890 delay 2464 ( this is down) + // phc2sys[4268818.287]: [ptp4l.0.config] ens5f0 phc offset -47 s2 freq -2047 delay 2438 // ts2phc[82674.465]: [ts2phc.0.cfg] nmea delay: 88403525 ns // ts2phc[82674.465]: [ts2phc.0.cfg] ens2f1 extts index 0 at 1673031129.000000000 corr 0 src 1673031129.911642976 diff 0 // ts2phc[82674.465]: [ts2phc.0.cfg] ens2f1 master offset 0 s2 freq -0 @@ -123,6 +124,8 @@ func extractRegularMetrics(processName, output string) (interfaceName string, pt // ptp4l.0.config master offset -2162130 s2 freq +22451884 delay 374976 // ts2phc.0.cfg ens2f1 master offset 0 s2 freq -0 // (ts2phc.0.cfg master offset 0 s2 freq -0) + // 0 1 2 3 4 5 6 7 8 + // ptp4l.0.config CLOCK_REALTIME offset -62 s0 freq -78368 delay 1100 if len(fields) < 7 { return } @@ -337,7 +340,7 @@ func parsePTPStatus(output string, fields []string) (int64, error) { // ParseGMLogs ... parse logs for various events func (p *PTPEventManager) ParseGMLogs(processName, configName, output string, fields []string, - ptpStats map[types.IFace]*stats.Stats) { + ptpStats stats.PTPStats) { //GM[1689282762]:[ts2phc.0.config] ens2f1 T-GM-STATUS s0 // 0 1 2 3 4 5 // GM 1689014436 ts2phc.0.config ens2f1 T-GM-STATUS s0 @@ -346,15 +349,14 @@ func (p *PTPEventManager) ParseGMLogs(processName, configName, output string, fi log.Errorf("GM Status is not in right format %s", output) return } - if _, found := ptpStats[master]; !found { - ptpStats[master] = stats.NewStats(configName) - ptpStats[master].SetProcessName(ts2phcProcessName) // master offset always reported by ts2phc - } + ptpStats.CheckSource(master, configName, ts2phcProcessName) } else { return } iface := fields[3] syncState := fields[5] + masterType := types.IFace(MasterClockType) + clockState := event.ClockState{ State: GetSyncState(syncState), IFace: pointer.String(iface), @@ -363,17 +365,26 @@ func (p *PTPEventManager) ParseGMLogs(processName, configName, output string, fi Value: nil, Metric: nil, NodeName: ptpNodeName, - HelpText: "0 = FREERUN, 1 = LOCKED, 2 = HOLDOVER", } alias := getAlias(iface) + SyncState.With(map[string]string{"process": processName, "node": ptpNodeName, "iface": alias}).Set(GetSyncStateID(syncState)) // status metrics - ptpStats[master].SetPtpDependentEventState(clockState) + ptpStats[masterType].SetPtpDependentEventState(clockState) + ptpStats[masterType].SetLastSyncState(clockState.State) + + // If GM is locked/Freerun/Holdover then ptp state change event + masterResource := fmt.Sprintf("%s/%s", alias, MasterClockType) + + // When GM is enabled there is only event happening at GM level for now + p.GenPTPEvent(processName, ptpStats[masterType], masterResource, 0, clockState.State, ptp.PtpStateChange) + ptpStats[masterType].SetLastSyncState(clockState.State) + UpdateSyncStateMetrics(processName, alias, ptpStats[masterType].LastSyncState()) } // ParseDPLLLogs ... parse logs for various events func (p *PTPEventManager) ParseDPLLLogs(processName, configName, output string, fields []string, - ptpStats map[types.IFace]*stats.Stats) { + ptpStats stats.PTPStats) { // dpll[1700598434]:[ts2phc.0.config] ens2f0 frequency_status 3 offset 0 phase_status 3 pps_status 1 s2 // 0 1 2 3 4 5 6 7 8 9 10 11 12 // dpll 1700598434 ts2phc.0.config ens2f0 frequency_status 3 offset 0 phase_status 3 pps_status 1 s2 @@ -382,21 +393,19 @@ func (p *PTPEventManager) ParseDPLLLogs(processName, configName, output string, log.Errorf("DPLL Status is not in right format %s", output) return } - if _, found := ptpStats[master]; !found { - ptpStats[master] = stats.NewStats(configName) - ptpStats[master].SetProcessName(ts2phcProcessName) // master offset always reported by ts2phc - } } else { return } - - var phaseStatus int64 + var phaseStatus float64 var frequencyStatus int64 var dpllOffset float64 var ppsStatus float64 var err error iface := pointer.String(fields[3]) syncState := fields[12] + ifaceType := types.IFace(*iface) + //TODO: try to init once + ptpStats.CheckSource(ifaceType, configName, ts2phcProcessName) logStatusLoop: // read 4, 6, 8 and 10 for i := 4; i < 11; i = i + 2 { // the order need to be fixed in linux ptp daemon , this is workaround @@ -407,7 +416,7 @@ logStatusLoop: break logStatusLoop } case "phase_status": - if phaseStatus, err = strconv.ParseInt(fields[i+1], 10, 64); err != nil { + if phaseStatus, err = strconv.ParseFloat(fields[i+1], 64); err != nil { log.Error("error parsing phaseStatus") // exit from loop if error break logStatusLoop @@ -427,31 +436,32 @@ logStatusLoop: if err == nil { alias := getAlias(*iface) - ptpStats[master].SetPtpDependentEventState(event.ClockState{ - State: GetSyncState(syncState), - Offset: pointer.Float64(dpllOffset), - Process: dpllProcessName, - IFace: iface, - Value: map[string]int64{"frequency_status": frequencyStatus, "phase_status": phaseStatus}, + ptpStats[ifaceType].SetPtpDependentEventState(event.ClockState{ + State: GetSyncState(syncState), + Offset: pointer.Float64(dpllOffset), + Process: dpllProcessName, + IFace: iface, + Value: map[string]int64{"frequency_status": frequencyStatus, "phase_status": int64(phaseStatus), + "pps_status": int64(ppsStatus)}, ClockSource: DPLL, NodeName: ptpNodeName, - HelpText: "-1=UNKNOWN, 0=INVALID, 1=FREERUN, 2=LOCKED, 3=LOCKED_HO_ACQ, 4=HOLDOVER", + HelpText: map[string]string{ + "frequency_status": "-1=UNKNOWN, 0=INVALID, 1=FREERUN, 2=LOCKED, 3=LOCKED_HO_ACQ, 4=HOLDOVER", + "phase_status": "-1=UNKNOWN, 0=INVALID, 1=FREERUN, 2=LOCKED, 3=LOCKED_HO_ACQ, 4=HOLDOVER", + "pps_status": "0=UNAVAILABLE, 1=AVAILABLE", + }, }) - masterResource := fmt.Sprintf("%s/%s", alias, MasterClockType) - interfaceType := types.IFace(master) SyncState.With(map[string]string{"process": processName, "node": ptpNodeName, "iface": alias}).Set(GetSyncStateID(syncState)) - if ppsStatus == UNAVAILABLE { - p.GenPTPEvent(processName, ptpStats[interfaceType], masterResource, int64(dpllOffset), ptp.FREERUN, ptp.PtpStateChange) - } else { - UpdatePTPOffsetMetrics(processName, processName, alias, dpllOffset) - } + UpdatePTPOffsetMetrics(processName, processName, alias, dpllOffset) UpdatePpsStatusMetrics(processName, alias, ppsStatus) + } else { + log.Errorf("error parsing dpll %s", err.Error()) } } // ParseGNSSLogs ... parse logs for various events func (p *PTPEventManager) ParseGNSSLogs(processName, configName, output string, fields []string, - ptp4lCfg *ptp4lconf.PTP4lConfig, ptpStats map[types.IFace]*stats.Stats) { + ptpStats stats.PTPStats) { //gnss[1689014431]:[ts2phc.0.config] ens2f1 gnss_status 5 offset 0 s0 // 0 1 2 3 4 5 6 7 8 // gnss 1689014431 ts2phc.0.config ens2f1 gnss_status 5 offset 0 s0 @@ -460,20 +470,18 @@ func (p *PTPEventManager) ParseGNSSLogs(processName, configName, output string, log.Errorf("GNSS Status is not in right format %s", output) return } - if _, found := ptpStats[master]; !found { - ptpStats[master] = stats.NewStats(configName) - ptpStats[master].SetProcessName(ts2phcProcessName) // master offset always reported by ts2phc - } } else { return } - var gnssState int64 var gnssOffset float64 var err error // 0 1 2 3 4 5 6 7 8 // ParseGNSSLogs: gnss 1692639234 ts2phc.2.config ens7f0 gnss_status 3 offset 5 s2 iface := pointer.String(fields[3]) + ifaceType := types.IFace(*iface) + //TODO: try to init once + ptpStats.CheckSource(ifaceType, configName, ts2phcProcessName) syncState := fields[8] if gnssState, err = strconv.ParseInt(fields[5], 10, 64); err != nil { log.Errorf("error parsing gnss state %s", processName) @@ -486,12 +494,13 @@ func (p *PTPEventManager) ParseGNSSLogs(processName, configName, output string, //openshift_ptp_offset_ns{from="gnss",iface="ens2f1",node="cnfde21.ptp.lab.eng.bos.redhat.com",process="gnss"} 0 if err == nil { alias := getAlias(*iface) - lastState, errState := ptpStats[master].GetStateState(processName) + // last state of GNSS + lastState, errState := ptpStats[ifaceType].GetStateState(processName, iface) pLabels := map[string]string{"from": processName, "node": ptpNodeName, "process": processName, "iface": alias} PtpOffset.With(pLabels).Set(gnssOffset) SyncState.With(map[string]string{"process": processName, "node": ptpNodeName, "iface": alias}).Set(GetSyncStateID(syncState)) - ptpStats[master].SetPtpDependentEventState(event.ClockState{ + ptpStats[ifaceType].SetPtpDependentEventState(event.ClockState{ State: GetSyncState(syncState), Offset: pointer.Float64(gnssOffset), Process: processName, @@ -499,18 +508,12 @@ func (p *PTPEventManager) ParseGNSSLogs(processName, configName, output string, Value: map[string]int64{"gnss_status": gnssState}, ClockSource: GNSS, NodeName: ptpNodeName, - HelpText: "0=NOFIX, 1=Dead Reckoning Only, 2=2D-FIX, 3=3D-FIX, 4=GPS+dead reckoning fix, 5=Time only fix", + HelpText: map[string]string{"gnss_status": "0=NOFIX, 1=Dead Reckoning Only, 2=2D-FIX, 3=3D-FIX, 4=GPS+dead reckoning fix, 5=Time only fix"}, }) + // reduce noise ; if state changed then send events if lastState != GetSyncState(syncState) || errState != nil { log.Infof("%s last state %s and current state %s", processName, lastState, GetSyncState(syncState)) - var masterAlias string - if m, ok := ptpStats[master]; ok { - masterAlias = m.Alias() - } - if alias == "" { - masterAlias, _ = ptp4lCfg.GetUnknownAlias() - } - masterResource := fmt.Sprintf("%s/%s", masterAlias, MasterClockType) + masterResource := fmt.Sprintf("%s/%s", alias, MasterClockType) p.publishGNSSEvent(gnssState, gnssOffset, masterResource, ptp.GnssStateChange) } } diff --git a/plugins/ptp_operator/metrics/logparser_test.go b/plugins/ptp_operator/metrics/logparser_test.go new file mode 100644 index 00000000..66acece4 --- /dev/null +++ b/plugins/ptp_operator/metrics/logparser_test.go @@ -0,0 +1,141 @@ +package metrics_test + +import ( + "strings" + "testing" + + "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/ptp4lconf" + + "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/metrics" + "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/stats" + "github.com/redhat-cne/cloud-event-proxy/plugins/ptp_operator/types" + "github.com/redhat-cne/sdk-go/pkg/event/ptp" + "github.com/stretchr/testify/assert" + "k8s.io/utils/pointer" +) + +var ( + configName = "ptp4l.0.config" + ptp4lConfig = &ptp4lconf.PTP4lConfig{ + Name: "ptp4l.0.config", + Profile: "grandmaster", + Interfaces: []*ptp4lconf.PTPInterface{ + { + Name: "ens2f0", + PortID: 1, + PortName: "port 1", + Role: 2, //master + }, + { + Name: "ens7f0", + PortID: 2, + PortName: "port 3", + Role: 2, // master + }, + }, + } +) + +type testCase struct { + processName string + output string + expectedState ptp.SyncState + interfaceName string +} + +// InitPubSubTypes ... initialize types of publishers for ptp operator +func initPubSubTypes() map[ptp.EventType]*types.EventPublisherType { + InitPubs := make(map[ptp.EventType]*types.EventPublisherType) + InitPubs[ptp.OsClockSyncStateChange] = &types.EventPublisherType{ + EventType: ptp.OsClockSyncStateChange, + Resource: ptp.OsClockSyncState, + } + InitPubs[ptp.PtpClockClassChange] = &types.EventPublisherType{ + EventType: ptp.PtpClockClassChange, + Resource: ptp.PtpClockClass, + } + InitPubs[ptp.PtpStateChange] = &types.EventPublisherType{ + EventType: ptp.PtpStateChange, + Resource: ptp.PtpLockState, + } + InitPubs[ptp.GnssStateChange] = &types.EventPublisherType{ + EventType: ptp.GnssStateChange, + Resource: ptp.GnssSyncStatus, + } + return InitPubs +} +func Test_ParseGNSSLogs(t *testing.T) { + var ptpEventManager *metrics.PTPEventManager + tc := []testCase{ + { + processName: "gnss", + output: "gnss[1689014431]:[ts2phc.0.config] ens2f1 gnss_status 5 offset 0 s0", + expectedState: ptp.FREERUN, + interfaceName: "ens2f1", + }, + { + processName: "gnss", + output: "gnss[1689014431]:[ts2phc.0.config] ens2f1 gnss_status 5 offset 0 s2", + expectedState: ptp.LOCKED, + interfaceName: "ens2f1", + }, + } + ptpEventManager = metrics.NewPTPEventManager("", initPubSubTypes(), "tetsnode", nil) + ptpEventManager.MockTest(true) + ptpEventManager.Stats[types.ConfigName(ptp4lConfig.Name)] = make(stats.PTPStats) + ptpStats := ptpEventManager.GetStats(types.ConfigName(configName)) + replacer := strings.NewReplacer("[", " ", "]", " ", ":", " ") + for _, tt := range tc { + output := replacer.Replace(tt.output) + fields := strings.Fields(output) + ptpEventManager.ParseGNSSLogs(tt.processName, configName, output, fields, ptpStats) + lastState, errState := ptpStats[types.IFace(tt.interfaceName)].GetStateState(tt.processName, pointer.String(tt.interfaceName)) + assert.Equal(t, errState, nil) + assert.Equal(t, tt.expectedState, lastState) + } +} + +func TestPTPEventManager_ParseDPLLLogs(t *testing.T) { + var ptpEventManager *metrics.PTPEventManager + tc := []testCase{ + { + processName: "dpll", + output: "dpll[1700598434]:[ts2phc.0.config] ens2f0 frequency_status 3 offset 0 phase_status 3 pps_status 1 s0", + expectedState: ptp.FREERUN, + interfaceName: "ens2f0", + }, + { + processName: "dpll", + output: "dpll[1700598434]:[ts2phc.0.config] ens1f0 frequency_status 3 offset 0 phase_status 3 pps_status 1 s2", + expectedState: ptp.LOCKED, + interfaceName: "ens1f0", + }, + { + processName: "dpll", + output: "dpll[1700598434]:[ts2phc.0.config] ens2f0 frequency_status 3 offset 0 phase_status 3 pps_status 1 s0", + expectedState: ptp.FREERUN, + interfaceName: "ens2f0", + }, + { + processName: "dpll", + output: "dpll[1700598434]:[ts2phc.0.config] ens1f0 frequency_status 3 offset 0 phase_status 3 pps_status 1 s2", + expectedState: ptp.LOCKED, + interfaceName: "ens1f0", + }, + } + + ptpEventManager = metrics.NewPTPEventManager("", initPubSubTypes(), "tetsnode", nil) + ptpEventManager.Stats[types.ConfigName(ptp4lConfig.Name)] = make(stats.PTPStats) + ptpStats := ptpEventManager.GetStats(types.ConfigName(configName)) + ptpEventManager.MockTest(true) + replacer := strings.NewReplacer("[", " ", "]", " ", ":", " ") + for _, tt := range tc { + output := replacer.Replace(tt.output) + fields := strings.Fields(output) + ptpEventManager.ParseDPLLLogs(tt.processName, configName, output, fields, ptpStats) + + lastState, errState := ptpStats[types.IFace(tt.interfaceName)].GetStateState(tt.processName, pointer.String(tt.interfaceName)) + assert.Equal(t, errState, nil) + assert.Equal(t, tt.expectedState, lastState) + } +} diff --git a/plugins/ptp_operator/metrics/manager.go b/plugins/ptp_operator/metrics/manager.go index 5950d195..abfe228c 100644 --- a/plugins/ptp_operator/metrics/manager.go +++ b/plugins/ptp_operator/metrics/manager.go @@ -23,7 +23,7 @@ type PTPEventManager struct { nodeName string scConfig *common.SCConfiguration lock sync.RWMutex - Stats map[types.ConfigName]map[types.IFace]*stats.Stats + Stats map[types.ConfigName]stats.PTPStats mock bool mockEvent ptp.EventType // PtpConfigMapUpdates holds ptp-configmap updated details @@ -41,7 +41,7 @@ func NewPTPEventManager(resourcePrefix string, publisherTypes map[ptp.EventType] nodeName: nodeName, scConfig: config, lock: sync.RWMutex{}, - Stats: make(map[types.ConfigName]map[types.IFace]*stats.Stats), + Stats: map[types.ConfigName]stats.PTPStats{}, Ptp4lConfigInterfaces: make(map[types.ConfigName]*ptp4lconf.PTP4lConfig), mock: false, } @@ -124,7 +124,7 @@ func (p *PTPEventManager) GetPTPConfig(configName types.ConfigName) *ptp4lconf.P func (p *PTPEventManager) GetStatsForInterface(name types.ConfigName, iface types.IFace) *stats.Stats { var found bool if _, found = p.Stats[name]; !found { - p.Stats[name] = make(map[types.IFace]*stats.Stats) + p.Stats[name] = make(stats.PTPStats) p.Stats[name][iface] = stats.NewStats(string(name)) } else if _, found = p.Stats[name][iface]; !found { p.Stats[name][iface] = stats.NewStats(string(name)) @@ -133,9 +133,9 @@ func (p *PTPEventManager) GetStatsForInterface(name types.ConfigName, iface type } // GetStats ... get stats -func (p *PTPEventManager) GetStats(name types.ConfigName) map[types.IFace]*stats.Stats { +func (p *PTPEventManager) GetStats(name types.ConfigName) stats.PTPStats { if _, found := p.Stats[name]; !found { - p.Stats[name] = make(map[types.IFace]*stats.Stats) + p.Stats[name] = make(stats.PTPStats) } return p.Stats[name] } diff --git a/plugins/ptp_operator/metrics/metrics.go b/plugins/ptp_operator/metrics/metrics.go index 187c8af7..b71dc4e4 100644 --- a/plugins/ptp_operator/metrics/metrics.go +++ b/plugins/ptp_operator/metrics/metrics.go @@ -139,7 +139,7 @@ func (p *PTPEventManager) ExtractMetrics(msg string) { } switch processName { case gnssProcessName: - p.ParseGNSSLogs(processName, configName, output, fields, ptp4lCfg, ptpStats) + p.ParseGNSSLogs(processName, configName, output, fields, ptpStats) case dpllProcessName: p.ParseDPLLLogs(processName, configName, output, fields, ptpStats) case gmProcessName: @@ -174,18 +174,14 @@ func (p *PTPEventManager) ExtractMetrics(msg string) { interfaceName, status, ptpOffset, syncState := extractNmeaMetrics(processName, output) offsetSource := master - interfaceType := types.IFace(master) + interfaceType := types.IFace(interfaceName) // ts2phc return actual interface name unlike ptp4l ptpInterface = ptp4lconf.PTPInterface{Name: interfaceName} alias := getAlias(interfaceName) - ptpStats[master].SetAlias(alias) - masterResource := fmt.Sprintf("%s/%s", alias, MasterClockType) - if status == UNAVAILABLE { - p.GenPTPEvent(profileName, ptpStats[interfaceType], masterResource, int64(ptpOffset), ptp.FREERUN, ptp.PtpStateChange) - } else { - UpdatePTPOffsetMetrics(offsetSource, processName, alias, ptpOffset) - } + ptpStats[interfaceType].SetAlias(alias) + // no event for nmeas status , change in GM will manage ptp events + UpdatePTPOffsetMetrics(offsetSource, processName, alias, ptpOffset) UpdateSyncStateMetrics(processName, alias, syncState) UpdateNmeaStatusMetrics(processName, alias, status) } else if strings.Contains(output, " offset ") && @@ -198,11 +194,20 @@ func (p *PTPEventManager) ExtractMetrics(msg string) { // ts2phc[82674.465]: [ts2phc.0.cfg] ens2f1 extts index 0 at 1673031129.000000000 corr 0 src 1673031129.911642976 diff 0 // ts2phc[82674.465]: [ts2phc.0.cfg] ens2f1 master offset 0 s2 freq -0 // Use threshold to CLOCK_REALTIME==SLAVE, rest send clock state to metrics no events - + // db | oc/bc/dual | ts2phc wo/GNSS | ts2phc w/GNSS | two card + // -------------------------------------------------------------------------------------------- + // stats[master] | No deps |has ts2phc state/of | has GNSS deps+state | same + // stats[CLOCK_REALTIME] | no deps |has clock_realtime | same | same + // stats[ens01] | NA | NA | has dpll deps + ts2phc offset + sate | same + // | ineterfacename | Process + // | master | ptp4l + // | CLOCK_REALTIME | phc2sys + // | ens01 | ts2phc (mostly) interfaceName, ptpOffset, _, frequencyAdjustment, delay, syncState := extractRegularMetrics(processName, output) if interfaceName == "" { return // don't do if iface not known } + // only ts2phc process will return actual interface name- allow all ts2phcprocess or iface in (master or clock realtime) if processName != ts2phcProcessName && !(interfaceName == master || interfaceName == ClockRealTime) { return // only master and clock_realtime are supported } @@ -213,76 +218,90 @@ func (p *PTPEventManager) ExtractMetrics(msg string) { } else if strings.Contains(output, "phc offset") { offsetSource = phc } - // if it was ts2phc then it was considered as master offset - interfaceType := types.IFace(master) + + // here interfaceName will be master , clock_realtime and ens2f0 + // interfaceType will be of only two kind master and clock_realtime + // ts2phc process always reports interface as of now if processName == ts2phcProcessName { // if current offset is read from ts2phc // ts2phc return actual interface name unlike ptp4l ptpInterface = ptp4lconf.PTPInterface{Name: interfaceName} + // create GM interfaces + ptpStats.CheckSource(master, configName, processName) + // update processname in master since pch2sys looks for it + ptpStats[master].SetProcessName(processName) + ptpStats[master].SetOffsetSource(offsetSource) } else { // for ts2phc there is no slave interface configuration // fort pt4l find the slave configured - interfaceType = types.IFace(interfaceName) // this will be CLOCK_REALTIME or master ptpInterface, _ = ptp4lCfg.ByRole(types.SLAVE) } - // copy ClockRealTime value to current slave interface - if _, found := ptpStats[interfaceType]; !found { - ptpStats[interfaceType] = stats.NewStats(configName) - } - // update metrics - ptpStats[interfaceType].SetOffsetSource(offsetSource) - // Process Name will get Updated when masteroffset is ts2phc for master - ptpStats[interfaceType].SetProcessName(processName) - ptpStats[interfaceType].SetFrequencyAdjustment(int64(frequencyAdjustment)) - ptpStats[interfaceType].SetDelay(int64(delay)) - // Handling GM clock state + ptpStats.CheckSource(types.IFace(interfaceName), configName, processName) + + ptpStats[types.IFace(interfaceName)].SetOffsetSource(offsetSource) + // Process Name will get Updated when master offset is ts2phc for master + ptpStats[types.IFace(interfaceName)].SetProcessName(processName) + ptpStats[types.IFace(interfaceName)].SetFrequencyAdjustment(int64(frequencyAdjustment)) + ptpStats[types.IFace(interfaceName)].SetDelay(int64(delay)) + + // Handling GM clock state- syncState is used for events + // IF its GM then synState is last syncState of GM // TODO: understand if the config is GM /BC /OC - if processName == ts2phcProcessName && interfaceName == MasterClockType { - // update ts2phc sync state to GM state if available,since GM State is identifies PTP state - // This identifies sync state of GM and adds ts2phc offset to verify if it has to stay in GM state or set new state - // based on ts2phc offset threshold : Which is unnecessary but to avoid breaking existing logic - // let the check happen again : GM state published by linuxptpdaemon already have checked ts2phc offset - if gmState, er := ptpStats[interfaceType].GetStateState(gmProcessName); er == nil { - // set sync state as gm state - syncState = gmState - } - } - switch interfaceName { + + switch interfaceName { //note: this is not interface type case ClockRealTime: // CLOCK_REALTIME is active slave interface - if masterOffsetSource == ts2phcProcessName { // once masterOffset is set in stats , it should not change - //TODO: once ts2phc events are identified we need to add that here, meaning if GM state is FREERUN then set OSClock as FREERUN - p.GenPTPEvent(profileName, ptpStats[interfaceType], interfaceName, int64(ptpOffset), syncState, ptp.OsClockSyncStateChange) - } else if r, ok := ptpStats[master]; ok && r.Role() == types.SLAVE { // publish event only if the master role is active + if r, ok := ptpStats[master]; ok && r.Role() == types.SLAVE { // publish event only if the master role is active // when related slave is faulty the holdover will make clock clear time as FREERUN - p.GenPTPEvent(profileName, ptpStats[interfaceType], interfaceName, int64(ptpOffset), syncState, ptp.OsClockSyncStateChange) + p.GenPTPEvent(profileName, ptpStats[ClockRealTime], interfaceName, int64(ptpOffset), syncState, ptp.OsClockSyncStateChange) + } else if masterOffsetSource == ts2phcProcessName { + //TODO: once ts2phc events are identified we need to add that here, meaning if GM state is FREERUN then set OSClock as FREERUN + // right now we are not managing os clock state based on GM state + p.GenPTPEvent(profileName, ptpStats[ClockRealTime], interfaceName, int64(ptpOffset), syncState, ptp.OsClockSyncStateChange) } // continue to update metrics regardless and stick to last sync state - UpdateSyncStateMetrics(processName, interfaceName, ptpStats[interfaceType].LastSyncState()) - UpdatePTPMetrics(offsetSource, processName, interfaceName, ptpOffset, float64(ptpStats[interfaceType].MaxAbs()), frequencyAdjustment, delay) + UpdateSyncStateMetrics(processName, interfaceName, ptpStats[ClockRealTime].LastSyncState()) + UpdatePTPMetrics(offsetSource, processName, interfaceName, ptpOffset, float64(ptpStats[ClockRealTime].MaxAbs()), frequencyAdjustment, delay) case MasterClockType: // this ptp4l[5196819.100]: [ptp4l.0.config] master offset -2162130 s2 freq +22451884 path delay // Report events for master by masking the index number of the slave interface if ptpInterface.Name != "" { - alias := ptpStats[interfaceType].Alias() + alias := ptpStats[types.IFace(interfaceName)].Alias() if alias == "" { alias = getAlias(ptpInterface.Name) - ptpStats[interfaceType].SetAlias(alias) + ptpStats[types.IFace(interfaceName)].SetAlias(alias) } masterResource := fmt.Sprintf("%s/%s", alias, MasterClockType) - p.GenPTPEvent(profileName, ptpStats[interfaceType], masterResource, int64(ptpOffset), syncState, ptp.PtpStateChange) - UpdateSyncStateMetrics(processName, alias, ptpStats[interfaceType].LastSyncState()) - UpdatePTPMetrics(offsetSource, processName, alias, ptpOffset, float64(ptpStats[interfaceType].MaxAbs()), + p.GenPTPEvent(profileName, ptpStats[types.IFace(interfaceName)], masterResource, int64(ptpOffset), syncState, ptp.PtpStateChange) + UpdateSyncStateMetrics(processName, alias, ptpStats[types.IFace(interfaceName)].LastSyncState()) + UpdatePTPMetrics(offsetSource, processName, alias, ptpOffset, float64(ptpStats[types.IFace(interfaceName)].MaxAbs()), frequencyAdjustment, delay) - ptpStats[interfaceType].AddValue(int64(ptpOffset)) + ptpStats[types.IFace(interfaceName)].AddValue(int64(ptpOffset)) } - default: + default: // for ts2phc the master stats are not updated at all, so rely on interface if masterOffsetSource == ts2phcProcessName { - alias := getAlias(interfaceName) - ptpStats[master].SetAlias(alias) + alias := ptpStats[types.IFace(interfaceName)].Alias() + if alias == "" { + alias = getAlias(ptpInterface.Name) + ptpStats[types.IFace(interfaceName)].SetAlias(alias) + } + // update ts2phc sync state to GM state if available,since GM State is identifies PTP state + // This identifies sync state of GM and adds ts2phc offset to verify if it has to stay in GM state or set new state + // based on ts2phc offset threshold : Which is unnecessary but to avoid breaking existing logic + // let the check happen again : GM state published by linuxptpdaemon already have checked ts2phc offset + // TO GM State we need to know GM interface ; here MASTER stats will hold data of GM + // and GM state will be held as dependant of master key masterResource := fmt.Sprintf("%s/%s", alias, MasterClockType) - p.GenPTPEvent(profileName, ptpStats[interfaceType], masterResource, int64(ptpOffset), syncState, ptp.PtpStateChange) - UpdateSyncStateMetrics(processName, alias, ptpStats[interfaceType].LastSyncState()) - UpdatePTPMetrics(offsetSource, processName, alias, ptpOffset, float64(ptpStats[interfaceType].MaxAbs()), + // use gm state to identify synstate + // master will hold multiple ts2phc state as one state based on GM state + // HANDLE case where there is no GM status but only ts2phc + if !ptpStats[master].HasProcessEnabled(gmProcessName) { + p.GenPTPEvent(profileName, ptpStats[types.IFace(interfaceName)], masterResource, int64(ptpOffset), syncState, ptp.PtpStateChange) + } else { + ptpStats[types.IFace(interfaceName)].SetLastSyncState(syncState) + ptpStats[types.IFace(interfaceName)].SetLastOffset(int64(ptpOffset)) + ptpStats[types.IFace(interfaceName)].AddValue(int64(ptpOffset)) + } + UpdateSyncStateMetrics(processName, alias, ptpStats[types.IFace(interfaceName)].LastSyncState()) + UpdatePTPMetrics(offsetSource, processName, alias, ptpOffset, float64(ptpStats[types.IFace(interfaceName)].MaxAbs()), frequencyAdjustment, delay) - ptpStats[interfaceType].AddValue(int64(ptpOffset)) } } } @@ -292,7 +311,7 @@ func (p *PTPEventManager) ExtractMetrics(msg string) { ptpInterface, ptp4lCfg, ptpStats) } -func (p *PTPEventManager) processDownEvent(profileName, processName string, ptpStats map[types.IFace]*stats.Stats) { +func (p *PTPEventManager) processDownEvent(profileName, processName string, ptpStats stats.PTPStats) { // if the process is responsible to set master offset if masterOffsetSource == processName { if ptpStats[master].Alias() != "" { diff --git a/plugins/ptp_operator/metrics/metrics_test.go b/plugins/ptp_operator/metrics/metrics_test.go index ee62e2b4..4f45a91e 100644 --- a/plugins/ptp_operator/metrics/metrics_test.go +++ b/plugins/ptp_operator/metrics/metrics_test.go @@ -44,7 +44,7 @@ const ( CLEANUP = -1 ) -var ptp4lConfig = &ptp4lconf.PTP4lConfig{ +var logPtp4lConfig = &ptp4lconf.PTP4lConfig{ Name: "ptp4l.0.config", Profile: "grandmaster", Interfaces: []*ptp4lconf.PTPInterface{ @@ -117,6 +117,7 @@ func (tc *TestCase) init() { tc.expectedSyncState = SKIP tc.expectedNmeaStatus = SKIP tc.expectedPpsStatus = SKIP + tc.expectedClockClassMetrics = SKIP tc.expectedEvent = "" } @@ -133,9 +134,13 @@ func (tc *TestCase) String() string { func (tc *TestCase) cleanupMetrics() { metrics.PtpOffset.With(map[string]string{"from": tc.from, "process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) + metrics.PtpMaxOffset.With(map[string]string{"from": tc.from, "process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) + metrics.PtpFrequencyAdjustment.With(map[string]string{"from": tc.from, "process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) + metrics.PtpDelay.With(map[string]string{"from": tc.from, "process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) metrics.SyncState.With(map[string]string{"process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) - metrics.PpsStatus.With(map[string]string{"process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) metrics.NmeaStatus.With(map[string]string{"process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) + metrics.PpsStatus.With(map[string]string{"process": tc.process, "node": tc.node, "iface": tc.iface}).Set(CLEANUP) + metrics.ClockClassMetrics.With(map[string]string{"process": tc.process, "node": tc.node}).Set(CLEANUP) ptpEventManager.ResetMockEvent() } @@ -143,7 +148,7 @@ func setLastSyncState(iface string, state ptp.SyncState) { if iface != metrics.ClockRealTime { iface = "master" } - s := ptpEventManager.GetStatsForInterface(types.ConfigName(ptp4lConfig.Name), types.IFace(iface)) + s := ptpEventManager.GetStatsForInterface(types.ConfigName(logPtp4lConfig.Name), types.IFace(iface)) s.SetLastSyncState(state) } @@ -151,7 +156,7 @@ func statsAddValue(iface string, val int64) { if iface != metrics.ClockRealTime { iface = "master" } - s := ptpEventManager.GetStatsForInterface(types.ConfigName(ptp4lConfig.Name), types.IFace(iface)) + s := ptpEventManager.GetStatsForInterface(types.ConfigName(logPtp4lConfig.Name), types.IFace(iface)) s.AddValue(val) } @@ -184,7 +189,7 @@ var testCases = []TestCase{ expectedSyncState: s0, expectedNmeaStatus: SKIP, expectedPpsStatus: 0, - expectedEvent: ptp.PtpStateChange, + expectedEvent: "", expectedClockClassMetrics: SKIP, }, { @@ -216,10 +221,10 @@ var testCases = []TestCase{ expectedNmeaStatus: 0, expectedPpsStatus: SKIP, expectedClockClassMetrics: SKIP, - expectedEvent: ptp.PtpStateChange, + expectedEvent: "", }, { - log: "ts2phc[1000000210]:[ts2phc.0.config] ens2fx nmea_status 1 offset 0 s2", + log: "ts2phc[1000000210]:[ts2phc.0.config] ens2f0 nmea_status 1 offset 0 s2", from: "master", process: "ts2phc", iface: "ens2fx", @@ -231,6 +236,7 @@ var testCases = []TestCase{ expectedNmeaStatus: 1, expectedPpsStatus: SKIP, expectedClockClassMetrics: SKIP, + expectedEvent: "", }, { log: "ts2phc[1000000300]: [ts2phc.0.config] ens2f0 master offset 0 s2 freq -0", @@ -359,22 +365,24 @@ func setup() { ptpEventManager = metrics.NewPTPEventManager(resourcePrefix, InitPubSubTypes(), "tetsnode", scConfig) ptpEventManager.MockTest(true) - ptpEventManager.AddPTPConfig(types.ConfigName(ptp4lConfig.Name), ptp4lConfig) + ptpEventManager.AddPTPConfig(types.ConfigName(logPtp4lConfig.Name), logPtp4lConfig) - stats_master := stats.NewStats(ptp4lConfig.Name) + stats_master := stats.NewStats(logPtp4lConfig.Name) stats_master.SetOffsetSource("master") stats_master.SetProcessName("ts2phc") stats_master.SetAlias("ens2fx") - stats_slave := stats.NewStats(ptp4lConfig.Name) + stats_slave := stats.NewStats(logPtp4lConfig.Name) stats_slave.SetOffsetSource("phc") stats_slave.SetProcessName("phc2sys") stats_slave.SetLastSyncState("LOCKED") stats_slave.SetClockClass(0) - ptpEventManager.Stats[types.ConfigName(ptp4lConfig.Name)] = make(map[types.IFace]*stats.Stats) - ptpEventManager.Stats[types.ConfigName(ptp4lConfig.Name)][types.IFace("master")] = stats_master - ptpEventManager.Stats[types.ConfigName(ptp4lConfig.Name)][types.IFace("CLOCK_REALTIME")] = stats_slave + ptpEventManager.Stats[types.ConfigName(logPtp4lConfig.Name)] = make(stats.PTPStats) + ptpEventManager.Stats[types.ConfigName(logPtp4lConfig.Name)][types.IFace("master")] = stats_master + ptpEventManager.Stats[types.ConfigName(logPtp4lConfig.Name)][types.IFace("CLOCK_REALTIME")] = stats_slave + ptpEventManager.Stats[types.ConfigName(logPtp4lConfig.Name)][types.IFace("ens2f0")] = stats_master + ptpEventManager.Stats[types.ConfigName(logPtp4lConfig.Name)][types.IFace("ens7f0")] = stats_slave ptpEventManager.PtpConfigMapUpdates = config.NewLinuxPTPConfUpdate() metrics.RegisterMetrics("mynode") diff --git a/plugins/ptp_operator/metrics/ptp4lParse.go b/plugins/ptp_operator/metrics/ptp4lParse.go index 612380ea..daf9d3b8 100644 --- a/plugins/ptp_operator/metrics/ptp4lParse.go +++ b/plugins/ptp_operator/metrics/ptp4lParse.go @@ -21,17 +21,14 @@ var gmStatusIdentifier = "T-GM-STATUS" // ParsePTP4l ... parse ptp4l for various events func (p *PTPEventManager) ParsePTP4l(processName, configName, profileName, output string, fields []string, - ptpInterface ptp4lconf.PTPInterface, ptp4lCfg *ptp4lconf.PTP4lConfig, ptpStats map[types.IFace]*stats.Stats) { + ptpInterface ptp4lconf.PTPInterface, ptp4lCfg *ptp4lconf.PTP4lConfig, ptpStats stats.PTPStats) { var err error if strings.Contains(output, classChangeIdentifier) { if len(fields) < 5 { log.Errorf("clock class not in right format %s", output) return } - if _, found := ptpStats[master]; !found { - ptpStats[master] = stats.NewStats(configName) - ptpStats[master].SetProcessName(ptp4lProcessName) - } + ptpStats.CheckSource(master, configName, ptp4lProcessName) // ptp4l 1646672953 ptp4l.0.config CLOCK_CLASS_CHANGE 165.000000 var clockClass float64 clockClass, err = strconv.ParseFloat(fields[4], 64) @@ -68,14 +65,8 @@ func (p *PTPEventManager) ParsePTP4l(processName, configName, profileName, outpu if role == types.SLAVE || masterOffsetSource == ts2phcProcessName { // initialize - if _, found := ptpStats[ClockRealTime]; !found { - ptpStats[ClockRealTime] = stats.NewStats(configName) - ptpStats[ClockRealTime].SetProcessName(phc2sysProcessName) - } - if _, found := ptpStats[master]; !found { - ptpStats[master] = stats.NewStats(configName) - ptpStats[master].SetProcessName(masterOffsetSource) - } + ptpStats.CheckSource(ClockRealTime, configName, phc2sysProcessName) + ptpStats.CheckSource(master, configName, masterOffsetSource) ptpStats[master].SetRole(role) } diff --git a/plugins/ptp_operator/stats/stats.go b/plugins/ptp_operator/stats/stats.go index 086f26ef..ad50ad80 100644 --- a/plugins/ptp_operator/stats/stats.go +++ b/plugins/ptp_operator/stats/stats.go @@ -13,6 +13,9 @@ import ( "github.com/redhat-cne/sdk-go/pkg/event/ptp" ) +// PTPStats ... +type PTPStats map[types.IFace]*Stats + // Stats calculates stats nolint:unused type Stats struct { configName string @@ -206,31 +209,50 @@ func (s *Stats) SetPtpDependentEventState(e event.ClockState) { s.ptpDependentEventState = &event.PTPEventState{ Mutex: sync.Mutex{}, CurrentPTPStateEvent: "", - DependsOn: map[string]*event.ClockState{}, + DependsOn: map[string]event.DependingClockState{}, Type: "", } } s.ptpDependentEventState.UpdateCurrentEventState(e) } -// GetStateValue ... get state value -func (s *Stats) GetStateValue(processName string) (map[string]int64, error) { +// GetStateState ... get state +func (s *Stats) GetStateState(processName string, iface *string) (ptp.SyncState, error) { if s.ptpDependentEventState != nil && s.ptpDependentEventState.DependsOn != nil { - if _, ok := s.ptpDependentEventState.DependsOn[processName]; ok { - return s.ptpDependentEventState.DependsOn[processName].Value, nil + if d, ok := s.ptpDependentEventState.DependsOn[processName]; ok { + if iface == nil && len(d) > 0 { + return d[0].State, nil + } + for _, state := range d { + if *state.IFace == *iface { + return state.State, nil + } + } } } - return map[string]int64{}, fmt.Errorf("sync state not found %s", processName) + return ptp.FREERUN, fmt.Errorf("sync state not found %s", processName) } -// GetStateState ... get state -func (s *Stats) GetStateState(processName string) (ptp.SyncState, error) { +// HasProcessEnabled ... check if process is enabled +func (s *Stats) HasProcessEnabled(processName string) bool { if s.ptpDependentEventState != nil && s.ptpDependentEventState.DependsOn != nil { if _, ok := s.ptpDependentEventState.DependsOn[processName]; ok { - return s.ptpDependentEventState.DependsOn[processName].State, nil + return true } } - return ptp.FREERUN, fmt.Errorf("sync state not found %s", processName) + return false +} + +// GetInterfaceByIndex ... get iface +func (s *Stats) GetInterfaceByIndex(processName string, index int) *string { + if s.ptpDependentEventState != nil && s.ptpDependentEventState.DependsOn != nil { + if d, ok := s.ptpDependentEventState.DependsOn[processName]; ok { + if len(d) >= index { + return d[index].IFace + } + } + } + return nil } func (s *Stats) String() string { @@ -250,3 +272,17 @@ func (s *Stats) DeleteAllMetrics() { s.ptpDependentEventState.DeleteAllMetrics() } } + +// CheckSource ... check key +func (ps PTPStats) CheckSource(k types.IFace, configName, processName string) { + if _, found := ps[k]; !found { + ps[k] = NewStats(configName) + ps[k].SetProcessName(processName) + } +} + +// New ... +func (ps PTPStats) New() PTPStats { + ptpStats := make(map[types.IFace]*Stats) + return ptpStats +}