diff --git a/collector/insight/chrony.go b/collector/insight/chrony.go new file mode 100644 index 0000000..0cad614 --- /dev/null +++ b/collector/insight/chrony.go @@ -0,0 +1,113 @@ +// Copyright 2021 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Use ntpq to get basic info of chrony on the system + +package insight + +import ( + "bytes" + "log" + "os/exec" + "strconv" + "strings" +) + +type ChronyStat struct { + ReferenceID string `json:"referenceid,omitempty"` + Stratum int `json:"stratum,omitempty"` + RefTime string `json:"ref_time,omitempty"` + LastOffset float64 `json:"last_offset,omitempty"` // millisecond + RMSOffset float64 `json:"rms_offset,omitempty"` // millisecond + Frequency float64 `json:"frequency,omitempty"` // millisecond + UpdateInterval float64 `json:"update_interval,omitempty"` // millisecond + LeapStatus string `json:"leap_status,omitempty"` +} + +func (cs *ChronyStat) getChronyInfo() { + // try common locations first, then search PATH, this could cover some + // contitions when PATH is not correctly set on calling `collector` + var syncdBinPaths = []string{"/usr/sbin/chronyc", "/usr/bin/chronyc", "chronyc"} + var syncd string + var err error + for _, syncdPath := range syncdBinPaths { + if syncd, err = exec.LookPath(syncdPath); err == nil { + // use the first found exec + break + } + cs.LeapStatus = err.Error() + } + // when no `ntpq` found, just return + if syncd == "" { + return + } + + cmd := exec.Command(syncd, "tracking") + var out bytes.Buffer + cmd.Stdout = &out + err = cmd.Run() + if err != nil { + log.Fatal(err) + } + + // set default sync status to none + cs.LeapStatus = "none" + + output := strings.FieldsFunc(out.String(), multi_split) + for _, kv := range output { + tmp := strings.Split(strings.TrimSpace(kv), " : ") + switch { + case strings.HasPrefix(tmp[0], "Reference ID"): + cs.ReferenceID = tmp[1] + case strings.HasPrefix(tmp[0], "Stratum"): + cs.Stratum, err = strconv.Atoi(tmp[1]) + if err != nil { + log.Fatal(err) + } + case strings.HasPrefix(tmp[0], "Ref time"): + cs.RefTime = tmp[1] + case strings.HasPrefix(tmp[0], "Last offset"): + cs.LastOffset, err = strconv.ParseFloat(strings.Split(tmp[1], " ")[0], 64) + if err != nil { + log.Fatal(err) + } + // second -> millisecond + cs.LastOffset = cs.LastOffset * 1000 + case strings.HasPrefix(tmp[0], "RMS offset"): + cs.RMSOffset, err = strconv.ParseFloat(strings.Split(tmp[1], " ")[0], 64) + if err != nil { + log.Fatal(err) + } + // second -> millisecond + cs.RMSOffset = cs.RMSOffset * 1000 + case strings.HasPrefix(tmp[0], "Frequency"): + cs.Frequency, err = strconv.ParseFloat(strings.Split(tmp[1], " ")[0], 64) + if err != nil { + log.Fatal(err) + } + // second -> millisecond + cs.Frequency = cs.Frequency * 1000 + case strings.HasPrefix(tmp[0], "Update interval"): + cs.UpdateInterval, err = strconv.ParseFloat(strings.Split(tmp[1], " ")[0], 64) + if err != nil { + log.Fatal(err) + } + cs.UpdateInterval = cs.UpdateInterval * 1000 + case strings.HasPrefix(tmp[0], "Leap status"): + // none, normal, close + cs.LeapStatus = strings.ToLower(tmp[1]) + default: + continue + } + } +} diff --git a/collector/insight/insight.go b/collector/insight/insight.go index cbb8c1a..453742b 100644 --- a/collector/insight/insight.go +++ b/collector/insight/insight.go @@ -42,6 +42,7 @@ type InsightInfo struct { Meta Meta `json:"meta"` SysInfo sysinfo.SysInfo `json:"sysinfo,omitempty"` NTP TimeStat `json:"ntp,omitempty"` + ChronyStat ChronyStat `json:"chrony,omitempty"` Partitions []BlockDev `json:"partitions,omitempty"` ProcStats []ProcessStat `json:"proc_stats,omitempty"` EpollExcl bool `json:"epoll_exclusive,omitempty"` @@ -70,6 +71,7 @@ func (info *InsightInfo) GetInfo(opts Options) { } else { info.SysInfo.GetSysInfo() info.NTP.getNTPInfo() + info.ChronyStat.getChronyInfo() info.Partitions = GetPartitionStats() switch runtime.GOOS { case "android",