Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCPBUGS-39173: [release-4.16] leap event backport #354

Open
wants to merge 1 commit into
base: release-4.16
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require github.com/openshift/ptp-operator v0.0.0-20230207052655-ede9197d99ca

require (
github.com/bigkevmcd/go-configparser v0.0.0-20240808124832-fc81059ea0bd
github.com/facebook/time v0.0.0-20230529151911-512b3b30ab23
github.com/golang/glog v1.0.0
github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f
Expand All @@ -19,6 +20,7 @@ require (
k8s.io/apimachinery v0.25.4
k8s.io/client-go v0.25.4
k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85
sigs.k8s.io/yaml v1.3.0
)

require (
Expand Down Expand Up @@ -88,5 +90,4 @@ require (
sigs.k8s.io/controller-runtime v0.13.0 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bigkevmcd/go-configparser v0.0.0-20240808124832-fc81059ea0bd h1:MsTk4yo6KVYdulsDscuH4AwiZN1CyuCJAg59EWE7HPQ=
github.com/bigkevmcd/go-configparser v0.0.0-20240808124832-fc81059ea0bd/go.mod h1:vzEQfW+A1T+AMJmTIX+SXNLNECHOM7GEinHhw0IjykI=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
Expand Down Expand Up @@ -217,6 +219,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down Expand Up @@ -247,7 +251,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
Expand Down Expand Up @@ -631,8 +634,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
Expand Down
10 changes: 7 additions & 3 deletions pkg/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

"github.com/openshift/linuxptp-daemon/pkg/config"
"github.com/openshift/linuxptp-daemon/pkg/dpll"
"github.com/openshift/linuxptp-daemon/pkg/leap"

"github.com/openshift/linuxptp-daemon/pkg/event"
ptpnetwork "github.com/openshift/linuxptp-daemon/pkg/network"
Expand Down Expand Up @@ -456,6 +457,7 @@ func (dn *Daemon) applyNodePtpProfile(runID int, nodeProfile *ptpv1.PtpProfile)
configFile = fmt.Sprintf("ts2phc.%d.config", runID)
configPath = fmt.Sprintf("/var/run/%s", configFile)
messageTag = fmt.Sprintf("[ts2phc.%d.config:{level}]", runID)
leap.LeapMgr.SetPtp4lConfigPath(fmt.Sprintf("ptp4l.%d.config", runID))
}

if configOpts == nil || *configOpts == "" {
Expand Down Expand Up @@ -887,9 +889,11 @@ func (p *ptpProcess) cmdRun(stdoutToSocket bool) {

// for ts2phc along with processing metrics need to identify event
func (p *ptpProcess) processPTPMetrics(output string) {
if p.name == ts2phcProcessName && (strings.Contains(output, NMEASourceDisabledIndicator) ||
strings.Contains(output, InvalidMasterTimestampIndicator) ||
strings.Contains(output, NMEASourceDisabledIndicator2)) { //TODO identify which interface lost nmea or 1pps
if p.name == ts2phcProcessName &&
!leap.LeapMgr.IsLeapInWindow(time.Now().UTC(), -2*time.Second, time.Second) &&
(strings.Contains(output, NMEASourceDisabledIndicator) ||
strings.Contains(output, InvalidMasterTimestampIndicator) ||
strings.Contains(output, NMEASourceDisabledIndicator2)) { //TODO identify which interface lost nmea or 1pps
iface := p.ifaces.GetGMInterface().Name
p.ProcessTs2PhcEvents(faultyOffset, ts2phcProcessName, iface, map[event.ValueType]interface{}{event.NMEA_STATUS: int64(0)})
glog.Error("nmea string lost") //TODO: add for 1pps lost
Expand Down
9 changes: 6 additions & 3 deletions pkg/daemon/daemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package daemon_test
import (
"flag"
"fmt"
ptpv1 "github.com/openshift/ptp-operator/api/v1"
"k8s.io/utils/pointer"
"os"
"strings"
"testing"

ptpv1 "github.com/openshift/ptp-operator/api/v1"
"k8s.io/utils/pointer"

"github.com/openshift/linuxptp-daemon/pkg/config"
"github.com/openshift/linuxptp-daemon/pkg/daemon"
"github.com/openshift/linuxptp-daemon/pkg/leap"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -231,7 +233,8 @@ func TestMain(m *testing.M) {
os.Exit(code)
}
func Test_ProcessPTPMetrics(t *testing.T) {

assert.NoError(t, leap.MockLeapFile())
defer close(leap.LeapMgr.Close)
assert := assert.New(t)
for _, tc := range testCases {
tc.node = MYNODE
Expand Down
3 changes: 2 additions & 1 deletion pkg/event/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ func TestEventHandler_ProcessEvents(t *testing.T) {
eventManager := event.Init("node", true, "/tmp/go.sock", eChannel, closeChn, nil, nil, nil)
eventManager.MockEnable()
go eventManager.ProcessEvents()
assert.NoError(t, mockLeap())
assert.NoError(t, leap.MockLeapFile())
defer close(leap.LeapMgr.Close)
time.Sleep(1 * time.Second)
for _, test := range tests {
select {
Expand Down
128 changes: 104 additions & 24 deletions pkg/leap/leap-file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import (
"time"

"github.com/golang/glog"
"github.com/openshift/linuxptp-daemon/pkg/pmc"
"github.com/openshift/linuxptp-daemon/pkg/ublox"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
fake "k8s.io/client-go/kubernetes/fake"
)

const (
Expand All @@ -29,6 +32,8 @@ const (
leapSourceGps = 2
leapConfigMapName = "leap-configmap"
MaintenancePeriod = time.Minute * 1
pmcWindowStartHours = 12
pmcWindowEndSeconds = 60
)

type LeapManager struct {
Expand All @@ -49,8 +54,10 @@ type LeapManager struct {
leapFilePath string
leapFileName string
// UTC offset and its validity time
utcOffset int
utcOffsetTime time.Time
utcOffset int
utcOffsetTime time.Time
ptp4lConfigPath string
pmcLeapSent bool
}

type LeapEvent struct {
Expand Down Expand Up @@ -93,27 +100,6 @@ func New(kubeclient kubernetes.Interface, namespace string) (*LeapManager, error
return LeapMgr, nil
}

func (l *LeapManager) Run() {
glog.Info("starting Leap file manager")
ticker := time.NewTicker(MaintenancePeriod)
defer ticker.Stop()
for {
select {
case v := <-l.UbloxLsInd:
l.handleLeapIndication(&v)
case <-l.Close:
LeapMgr = nil
return
case <-ticker.C:
if l.retryUpdate {
l.updateLeapConfigmap()
}
// TODO: if current time is within -12h ... +60s from leap event:
// Send PMC command
}
}
}

func GetUtcOffset() int {
if LeapMgr != nil {
if time.Now().UTC().After(LeapMgr.utcOffsetTime) {
Expand Down Expand Up @@ -172,6 +158,11 @@ func parseLeapFile(b []byte) (*LeapFile, error) {
return &l, nil
}

func (l *LeapManager) SetPtp4lConfigPath(path string) {
glog.Info("set Leap manager ptp4l config file name to ", path)
l.ptp4lConfigPath = path
}

func (l *LeapManager) renderLeapData() (*bytes.Buffer, error) {
templateStr := `# Do not edit
# This file is generated automatically by linuxptp-daemon
Expand Down Expand Up @@ -246,6 +237,55 @@ func (l *LeapManager) populateLeapData() error {
return nil
}

func (l *LeapManager) Run() {
glog.Info("starting Leap file manager")
ticker := time.NewTicker(MaintenancePeriod)
for {
select {
case v := <-l.UbloxLsInd:
l.handleLeapIndication(&v)
case <-l.Close:
LeapMgr = nil
return
case <-ticker.C:
if l.retryUpdate {
l.updateLeapConfigmap()
}
if l.IsLeapInWindow(time.Now().UTC(), -pmcWindowStartHours*time.Hour, -pmcWindowEndSeconds*time.Second) {
if !l.pmcLeapSent {
g, err := pmc.RunPMCExpGetGMSettings(l.ptp4lConfigPath)
if err != nil {
glog.Error("error in Leap:", err)
continue
}
leapDiff := l.leapFile.LeapEvents[len(l.leapFile.LeapEvents)-1].LeapSec - int(g.TimePropertiesDS.CurrentUtcOffset)
if leapDiff > 0 {
g.TimePropertiesDS.Leap59 = false
g.TimePropertiesDS.Leap61 = true
} else if leapDiff < 0 {
g.TimePropertiesDS.Leap59 = true
g.TimePropertiesDS.Leap61 = false
} else {
// No actual change in leap seconds, don't send anything
l.pmcLeapSent = true
continue
}
glog.Info("Sending PMC command in Leap window")
glog.Infof("Leap time properties: %++v", g.TimePropertiesDS)
err = pmc.RunPMCExpSetGMSettings(l.ptp4lConfigPath, g)
if err != nil {
glog.Error("failed to send PMC for Leap: ", err)
continue
}
l.pmcLeapSent = true
}
} else {
l.pmcLeapSent = false
}
}
}
}

// updateLeapFile updates a new leap event to the list of leap events, if provided
func (l *LeapManager) updateLeapFile(leapTime time.Time,
leapSec int, currentTime time.Time) {
Expand Down Expand Up @@ -366,7 +406,8 @@ func (l *LeapManager) processLeapIndication(data *ublox.TimeLs) (*leapIndResult,
return nil, fmt.Errorf("failed to parse time duration: Leap: %v", err)
}
if data.LsChange == 0 && data.TimeToLsEvent >= 0 {
result.leapTime = currentTime
// shift leap date out of pmc window, so no pmc commands will be sent
result.leapTime = currentTime.Add(-pmcWindowEndSeconds * time.Second)
} else {
result.leapTime = gpsStartTime.Add(deltaHours)
}
Expand All @@ -377,3 +418,42 @@ func (l *LeapManager) processLeapIndication(data *ublox.TimeLs) (*leapIndResult,
}
return nil, nil
}

// IsLeapInWindow() returns whether a leap event is occuring within the specified time window from now
func (l *LeapManager) IsLeapInWindow(now time.Time, startOffset, endOffset time.Duration) bool {
startTime := time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)
lastLeap := l.leapFile.LeapEvents[len(l.leapFile.LeapEvents)-1]
lastLeapTime, err := strconv.Atoi(lastLeap.LeapTime)
if err != nil {
return false
}
leapTime := startTime.Add(time.Second * time.Duration(lastLeapTime))
leapWindowStart := leapTime.Add(startOffset)
leapWindowEnd := leapTime.Add(endOffset)
if now.After(leapWindowStart) && now.Before(leapWindowEnd) {
glog.Info("Leap in window: ", startOffset, " ", endOffset)
return true
}
return false
}

func MockLeapFile() error {
cm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: "openshift-ptp", Name: "leap-configmap"},
Data: map[string]string{
"test-node-name": `# Do not edit
# This file is generated automatically by linuxptp-daemon
#$ 3927775672
#@ 4291747200
3692217600 37 # 1 Jan 2017`,
},
}
os.Setenv("NODE_NAME", "test-node-name")
client := fake.NewSimpleClientset(cm)
lm, err := New(client, "openshift-ptp")
if err != nil {
return err
}
go lm.Run()
return nil
}
46 changes: 45 additions & 1 deletion pkg/leap/leap-file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func Test_processLeapIndication_MissedLeapZero(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, res)
assert.WithinDuration(t, time.Now().UTC(), res.updateTime, 1*time.Second)
assert.WithinDuration(t, res.leapTime, time.Now().UTC(), 1*time.Second)
assert.WithinDuration(t, res.leapTime, time.Now().UTC().Add(-60*time.Second), 1*time.Second)
assert.Equal(t, int(ind.CurrLs+ind.LsChange+19), res.leapSec)
}

Expand Down Expand Up @@ -300,6 +300,50 @@ func Test_handleLeapIndication(t *testing.T) {
assert.Equal(t, "4291747200", lm.leapFile.ExpirationTime)
}

func Test_IsLeapInWindow_Pos(t *testing.T) {
now := time.Now().UTC()
startTime := time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)
diff := now.Sub(startTime)
leapTime := fmt.Sprintf("%d", int(diff.Seconds()))
lm := &LeapManager{

leapFile: LeapFile{
LeapEvents: []LeapEvent{
{
LeapTime: leapTime,
},
},
},
}
res := lm.IsLeapInWindow(now, -1*time.Second, time.Second)
assert.True(t, res)
}
func Test_IsLeapInWindow_Neg(t *testing.T) {
now := time.Now().UTC()
startTime := time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)
diff := now.Sub(startTime)
leapTime := fmt.Sprintf("%d", int(diff.Seconds())-1)
lm := &LeapManager{

leapFile: LeapFile{
LeapEvents: []LeapEvent{
{
LeapTime: leapTime,
},
},
},
}
res := lm.IsLeapInWindow(now, -1*time.Second, time.Second)
assert.False(t, res)
}

func Test_SetPtp4lConfigPath(t *testing.T) {
path := "test"
lm := &LeapManager{}
lm.SetPtp4lConfigPath(path)
assert.Equal(t, path, lm.ptp4lConfigPath)
}

func Test_New_Good(t *testing.T) {
cm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: "openshift-ptp", Name: "leap-configmap"},
Expand Down
19 changes: 19 additions & 0 deletions vendor/github.com/bigkevmcd/go-configparser/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading