diff --git a/pkg/daemon/daemon/daemon.go b/pkg/daemon/daemon/daemon.go index 6ed5a7e4..573099cf 100644 --- a/pkg/daemon/daemon/daemon.go +++ b/pkg/daemon/daemon/daemon.go @@ -480,7 +480,7 @@ func processStatus(processName, messageTag string, status int64) { } func (p *ptpProcess) updateClockClass(c *net.Conn) { - if _, matches, e := pmc.RunPMCExp(p.configName, pmc.CmdParentDataSet, pmc.ClockClassChangeRegEx); e == nil { + if _, matches, e := pmc.RunPMCExp(p.configName, pmc.CmdGetParentDataSet, pmc.ClockClassChangeRegEx); e == nil { //regex: 'gm.ClockClass[[:space:]]+(\d+)' //match 1: 'gm.ClockClass 135' //match 2: '135' diff --git a/pkg/daemon/pmc/pmc.go b/pkg/daemon/pmc/pmc.go index 74105ad6..fc76fc0c 100644 --- a/pkg/daemon/pmc/pmc.go +++ b/pkg/daemon/pmc/pmc.go @@ -2,31 +2,30 @@ package pmc import ( "fmt" - "github.com/golang/glog" - "github.com/google/goexpect" "regexp" + "strings" "time" + + "github.com/golang/glog" + expect "github.com/google/goexpect" + "github.com/openshift/k8snetworkplumbingwg/pkg/protocol" ) var ( - ClockClassChangeRegEx = regexp.MustCompile(`gm.ClockClass[[:space:]]+(\d+)`) - ClockClassUpdateRegEx = regexp.MustCompile(`clockClass[[:space:]]+(\d+)`) - CmdParentDataSet = "GET PARENT_DATA_SET" - CmdUpdateGMClass_LOCKED = `SET GRANDMASTER_SETTINGS_NP clockClass %d \ - clockAccuracy 0x21 offsetScaledLogVariance 0x436a \ - currentUtcOffset 37 leap61 0 leap59 0 currentUtcOffsetValid 1 \ - ptpTimescale 1 timeTraceable 0 frequencyTraceable 0 - timeSource 0xa0` - CmdUpdateGMClass_FREERUN = `SET GRANDMASTER_SETTINGS_NP clockClass %d \ - clockAccuracy 0xFE offsetScaledLogVariance 0x436a \ - currentUtcOffset 37 leap61 0 leap59 0 currentUtcOffsetValid 1 \ - ptpTimescale 1 timeTraceable 0 frequencyTraceable 0 - timeSource 0xa0` - cmdTimeout = 500 * time.Millisecond + ClockClassChangeRegEx = regexp.MustCompile(`gm.ClockClass[[:space:]]+(\d+)`) + ClockClassUpdateRegEx = regexp.MustCompile(`clockClass[[:space:]]+(\d+)`) + GetGMSettingsRegEx = regexp.MustCompile(`clockClass[[:space:]]+(\d+)[[:space:]]+clockAccuracy[[:space:]]+(0x\d+)`) + CmdGetParentDataSet = "GET PARENT_DATA_SET" + CmdGetGMSettings = "GET GRANDMASTER_SETTINGS_NP" + CmdSetGMSettings = "SET GRANDMASTER_SETTINGS_NP" + // GET GRANDMASTER_SETTINGS_NP sometimes takes more than 2 seconds + cmdTimeout = 5000 * time.Millisecond ) // RunPMCExp ... go expect to run PMC util cmd func RunPMCExp(configFileName, cmdStr string, promptRE *regexp.Regexp) (result string, matches []string, err error) { + glog.Infof("pmc read config from /var/run/%s", configFileName) + glog.Infof("pmc run command: %s", cmdStr) e, _, err := expect.Spawn(fmt.Sprintf("pmc -u -b 1 -f /var/run/%s", configFileName), -1) if err != nil { return "", []string{}, err @@ -38,24 +37,55 @@ func RunPMCExp(configFileName, cmdStr string, promptRE *regexp.Regexp) (result s glog.Errorf("pmc result match error %s", err) return } + glog.Infof("pmc result: %s", result) err = e.Send("\x03") } return } -// RunUpdatePMCExp ... run pmc update command -func RunUpdatePMCExp(configFileName, cmdStr string, promptRE *regexp.Regexp) (result string, matches []string, err error) { +// RunPMCExpGetGMSettings ... get current GRANDMASTER_SETTINGS_NP +func RunPMCExpGetGMSettings(configFileName string) (g protocol.GrandmasterSettings, err error) { + cmdStr := CmdGetGMSettings + glog.Infof("pmc read config from /var/run/%s", configFileName) + glog.Infof("pmc run command: %s", cmdStr) e, _, err := expect.Spawn(fmt.Sprintf("pmc -u -b 1 -f /var/run/%s", configFileName), -1) if err != nil { - return "", []string{}, err + return g, err } defer e.Close() if err = e.Send(cmdStr + "\n"); err == nil { - result, matches, err = e.Expect(promptRE, cmdTimeout) + result, matches, err := e.Expect(regexp.MustCompile(g.RegEx()), cmdTimeout) if err != nil { - glog.Errorf("pmc result match error %s", err) - return + fmt.Printf("pmc result match error %s\n", err) + return g, err + } + glog.Infof("pmc result: %s", result) + for i, m := range matches[1:] { + g.Update(g.Keys()[i], m) + } + err = e.Send("\x03") + } + return +} + +// RunPMCExpSetGMSettings ... set GRANDMASTER_SETTINGS_NP +func RunPMCExpSetGMSettings(configFileName string, g protocol.GrandmasterSettings) (err error) { + cmdStr := CmdSetGMSettings + cmdStr += strings.Replace(g.String(), "\n", " ", -1) + glog.Infof("pmc read config from /var/run/%s", configFileName) + glog.Infof("pmc run command: %s", cmdStr) + e, _, err := expect.Spawn(fmt.Sprintf("pmc -u -b 1 -f /var/run/%s", configFileName), -1) + if err != nil { + return err + } + defer e.Close() + if err = e.Send(cmdStr + "\n"); err == nil { + result, _, err := e.Expect(regexp.MustCompile(g.RegEx()), cmdTimeout) + if err != nil { + fmt.Printf("pmc result match error %s\n", err) + return err } + glog.Infof("pmc result: %s", result) err = e.Send("\x03") } return diff --git a/pkg/protocol/type.go b/pkg/protocol/type.go new file mode 100644 index 00000000..0fe53fdc --- /dev/null +++ b/pkg/protocol/type.go @@ -0,0 +1,153 @@ +package protocol + +import ( + "fmt" + "strconv" + "strings" + + "github.com/facebook/time/ptp/protocol" +) + +// extend fbprotocol.ClockClass according to https://www.itu.int/rec/T-REC-G.8275.1-202211-I/en section 6.4 table 3 +const ( + ClockClassFreerun protocol.ClockClass = 248 +) + +type GrandmasterSettings struct { + ClockQuality protocol.ClockQuality + TimePropertiesDS TimePropertiesDS +} + +type TimePropertiesDS struct { + CurrentUtcOffset int32 + CurrentUtcOffsetValid bool + Leap59 bool + Leap61 bool + TimeTraceable bool + FrequencyTraceable bool + PtpTimescale bool + TimeSource protocol.TimeSource +} + +func (g *GrandmasterSettings) String() string { + if g == nil { + return "" + + } + result := fmt.Sprintf(" clockClass %d\n", g.ClockQuality.ClockClass) + result += fmt.Sprintf(" clockAccuracy 0x%x\n", g.ClockQuality.ClockAccuracy) + result += fmt.Sprintf(" offsetScaledLogVariance 0x%x\n", g.ClockQuality.OffsetScaledLogVariance) + result += fmt.Sprintf(" currentUtcOffset %d\n", g.TimePropertiesDS.CurrentUtcOffset) + result += fmt.Sprintf(" leap61 %d\n", btoi(g.TimePropertiesDS.Leap61)) + result += fmt.Sprintf(" leap59 %d\n", btoi(g.TimePropertiesDS.Leap59)) + result += fmt.Sprintf(" currentUtcOffsetValid %d\n", btoi(g.TimePropertiesDS.CurrentUtcOffsetValid)) + result += fmt.Sprintf(" ptpTimescale %d\n", btoi(g.TimePropertiesDS.PtpTimescale)) + result += fmt.Sprintf(" timeTraceable %d\n", btoi(g.TimePropertiesDS.TimeTraceable)) + result += fmt.Sprintf(" frequencyTraceable %d\n", btoi(g.TimePropertiesDS.FrequencyTraceable)) + result += fmt.Sprintf(" timeSource 0x%x\n", uint(g.TimePropertiesDS.TimeSource)) + return result +} + +// Keys returns variables names in order of pmc command results +func (g *GrandmasterSettings) Keys() []string { + return []string{"clockClass", "clockAccuracy", "offsetScaledLogVariance", + "currentUtcOffset", "leap61", "leap59", "currentUtcOffsetValid", + "ptpTimescale", "timeTraceable", "frequencyTraceable", "timeSource"} +} + +func (g *GrandmasterSettings) ValueRegEx() map[string]string { + return map[string]string{ + "clockClass": `(\d+)`, + "clockAccuracy": `(0x[\da-f]+)`, + "offsetScaledLogVariance": `(0x[\da-f]+)`, + "currentUtcOffset": `(\d+)`, + "currentUtcOffsetValid": `([01])`, + "leap59": `([01])`, + "leap61": `([01])`, + "timeTraceable": `([01])`, + "frequencyTraceable": `([01])`, + "ptpTimescale": `([01])`, + "timeSource": `(0x[\da-f]+)`, + } +} + +func (g *GrandmasterSettings) RegEx() string { + result := "" + for _, k := range g.Keys() { + result += `[[:space:]]+` + k + `[[:space:]]+` + g.ValueRegEx()[k] + } + return result +} + +func (g *GrandmasterSettings) Update(key string, value string) { + switch key { + case "clockClass": + g.ClockQuality.ClockClass = protocol.ClockClass(stou8(value)) + case "clockAccuracy": + g.ClockQuality.ClockAccuracy = protocol.ClockAccuracy(stou8h(value)) + case "offsetScaledLogVariance": + g.ClockQuality.OffsetScaledLogVariance = stou16h(value) + case "currentUtcOffset": + g.TimePropertiesDS.CurrentUtcOffset = stoi32(value) + case "currentUtcOffsetValid": + g.TimePropertiesDS.CurrentUtcOffsetValid = stob(value) + case "leap59": + g.TimePropertiesDS.Leap59 = stob(value) + case "leap61": + g.TimePropertiesDS.Leap61 = stob(value) + case "timeTraceable": + g.TimePropertiesDS.TimeTraceable = stob(value) + case "frequencyTraceable": + g.TimePropertiesDS.FrequencyTraceable = stob(value) + case "ptpTimescale": + g.TimePropertiesDS.PtpTimescale = stob(value) + case "timeSource": + g.TimePropertiesDS.TimeSource = protocol.TimeSource(stou8h(value)) + } +} + +func btoi(b bool) uint8 { + if b { + return 1 + } + return 0 +} + +func stob(s string) bool { + if s == "1" { + return true + } + return false +} + +func stou8(s string) uint8 { + uint64Value, err := strconv.ParseUint(s, 10, 8) + if err != nil { + fmt.Printf("%v\n", err) + } + return uint8(uint64Value) +} + +func stou8h(s string) uint8 { + uint64Value, err := strconv.ParseUint(strings.Replace(s, "0x", "", 1), 16, 8) + if err != nil { + fmt.Printf("%v\n", err) + } + return uint8(uint64Value) +} + +func stou16h(s string) uint16 { + uint64Value, err := strconv.ParseUint(strings.Replace(s, "0x", "", 1), 16, 16) + if err != nil { + fmt.Printf("%v\n", err) + } + return uint16(uint64Value) +} + +func stoi32(s string) int32 { + int64Value, err := strconv.ParseInt(s, 10, 32) + if err != nil { + fmt.Printf("%v\n", err) + } + return int32(int64Value) +}