support custom /proc path

This commit is contained in:
Neo Zhuo
2021-07-20 11:19:47 +08:00
parent 78c11c4ceb
commit 11ddb5e6bf
12 changed files with 42 additions and 37 deletions

View File

@@ -46,13 +46,17 @@ type cpuCollector struct {
mSystemInterruptsTotal *metrics.Int64Metric
mSystemCPUStat *metrics.Float64Metric // per-cpu time from /proc/stats
config *ssmtypes.CPUStatsConfig
config *ssmtypes.CPUStatsConfig
procPath string
lastUsageTime map[string]float64
}
func NewCPUCollectorOrDie(cpuConfig *ssmtypes.CPUStatsConfig) *cpuCollector {
cc := cpuCollector{config: cpuConfig}
func NewCPUCollectorOrDie(cpuConfig *ssmtypes.CPUStatsConfig, procPath string) *cpuCollector {
cc := cpuCollector{
config: cpuConfig,
procPath: procPath,
}
var err error
cc.mRunnableTaskCount, err = metrics.NewFloat64Metric(

View File

@@ -52,7 +52,6 @@ func (cc *cpuCollector) recordLoad() {
}
func (cc *cpuCollector) recordSystemStats() {
// don't collect the load metrics if the configs are not present.
if cc.mSystemCPUStat == nil && cc.mSystemInterruptsTotal == nil &&
cc.mSystemProcessesTotal == nil && cc.mSystemProcsBlocked == nil &&
@@ -60,7 +59,7 @@ func (cc *cpuCollector) recordSystemStats() {
return
}
fs, err := procfs.NewFS("/proc")
fs, err := procfs.NewFS(cc.procPath)
stats, err := fs.Stat()
if err != nil {
glog.Errorf("Failed to retrieve cpu/process stats: %v", err)

View File

@@ -67,6 +67,6 @@ func TestCpuCollector(t *testing.T) {
if err := json.Unmarshal([]byte(fakeCPUConfig), cfg); err != nil {
t.Fatalf("cannot load cpu config: %s", err)
}
mc := NewCPUCollectorOrDie(cfg)
mc := NewCPUCollectorOrDie(cfg, "/proc")
mc.collect()
}

View File

@@ -28,12 +28,14 @@ import (
type netCollector struct {
config *ssmtypes.NetStatsConfig
procPath string
recorder *ifaceStatRecorder
}
func NewNetCollectorOrDie(netConfig *ssmtypes.NetStatsConfig) *netCollector {
func NewNetCollectorOrDie(netConfig *ssmtypes.NetStatsConfig, procPath string) *netCollector {
nc := netCollector{
config: netConfig,
procPath: procPath,
recorder: newIfaceStatRecorder(),
}
@@ -207,7 +209,7 @@ func (nc *netCollector) mustRegisterMetric(metricID metrics.MetricID, descriptio
}
func (nc *netCollector) recordNetDev() {
fs, err := procfs.NewFS("/proc")
fs, err := procfs.NewFS(nc.procPath)
stats, err := fs.NetDev()
if err != nil {
glog.Errorf("Failed to retrieve net dev stat: %v", err)

View File

@@ -16,6 +16,7 @@ package systemstatsmonitor
import (
"encoding/json"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
@@ -27,11 +28,15 @@ import (
type osFeatureCollector struct {
config *ssmtypes.OSFeatureStatsConfig
procPath string
osFeature *metrics.Int64Metric
}
func NewOsFeatureCollectorOrDie(osFeatureConfig *ssmtypes.OSFeatureStatsConfig) *osFeatureCollector {
oc := osFeatureCollector{config: osFeatureConfig}
func NewOsFeatureCollectorOrDie(osFeatureConfig *ssmtypes.OSFeatureStatsConfig, procPath string) *osFeatureCollector {
oc := osFeatureCollector{
config: osFeatureConfig,
procPath: procPath,
}
var err error
// Use metrics.Last aggregation method to ensure the metric is a guage metric.
if osFeatureConfig.MetricsConfigs["system/os_feature"].DisplayName != "" {
@@ -145,12 +150,12 @@ func (ofc *osFeatureCollector) collect() {
if ofc == nil || ofc.osFeature == nil {
return
}
cmdlineArgs, err := system.CmdlineArgs()
cmdlineArgs, err := system.CmdlineArgs(filepath.Join(ofc.procPath, "/cmdline"))
if err != nil {
glog.Fatalf("Error retrieving cmdline args: %v", err)
}
ofc.recordFeaturesFromCmdline(cmdlineArgs)
modules, err := system.Modules()
modules, err := system.Modules(filepath.Join(ofc.procPath, "/modules"))
if err != nil {
glog.Fatalf("Error retrieving kernel modules: %v", err)
}

View File

@@ -78,7 +78,7 @@ func NewSystemStatsMonitorOrDie(configPath string) types.Monitor {
}
if len(ssm.config.CPUConfig.MetricsConfigs) > 0 {
ssm.cpuCollector = NewCPUCollectorOrDie(&ssm.config.CPUConfig)
ssm.cpuCollector = NewCPUCollectorOrDie(&ssm.config.CPUConfig, ssm.config.ProcPath)
}
if len(ssm.config.DiskConfig.MetricsConfigs) > 0 {
ssm.diskCollector = NewDiskCollectorOrDie(&ssm.config.DiskConfig)
@@ -96,10 +96,10 @@ func NewSystemStatsMonitorOrDie(configPath string) types.Monitor {
ssm.config.OsFeatureConfig.KnownModulesConfigPath = filepath.Join(filepath.Dir(configPath),
ssm.config.OsFeatureConfig.KnownModulesConfigPath)
}
ssm.osFeatureCollector = NewOsFeatureCollectorOrDie(&ssm.config.OsFeatureConfig)
ssm.osFeatureCollector = NewOsFeatureCollectorOrDie(&ssm.config.OsFeatureConfig, ssm.config.ProcPath)
}
if len(ssm.config.NetConfig.MetricsConfigs) > 0 {
ssm.netCollector = NewNetCollectorOrDie(&ssm.config.NetConfig)
ssm.netCollector = NewNetCollectorOrDie(&ssm.config.NetConfig, ssm.config.ProcPath)
}
return &ssm
}

View File

@@ -18,6 +18,7 @@ package types
import (
"fmt"
"os"
"time"
)
@@ -25,6 +26,7 @@ var (
defaultInvokeIntervalString = (60 * time.Second).String()
defaultlsblkTimeoutString = (5 * time.Second).String()
defaultKnownModulesConfigPath = "guestosconfig/known-modules.json"
defaultProcPath = "/proc"
)
type MetricConfig struct {
@@ -69,6 +71,7 @@ type SystemStatsConfig struct {
NetConfig NetStatsConfig `json:"net"`
InvokeIntervalString string `json:"invokeInterval"`
InvokeInterval time.Duration `json:"-"`
ProcPath string `json:"procPath"`
}
// ApplyConfiguration applies default configurations.
@@ -76,6 +79,9 @@ func (ssc *SystemStatsConfig) ApplyConfiguration() error {
if ssc.InvokeIntervalString == "" {
ssc.InvokeIntervalString = defaultInvokeIntervalString
}
if ssc.ProcPath == "" {
ssc.ProcPath = defaultProcPath
}
if ssc.DiskConfig.LsblkTimeoutString == "" {
ssc.DiskConfig.LsblkTimeoutString = defaultlsblkTimeoutString
}
@@ -101,6 +107,9 @@ func (ssc *SystemStatsConfig) Validate() error {
if ssc.InvokeInterval <= time.Duration(0) {
return fmt.Errorf("InvokeInterval %v must be above 0s", ssc.InvokeInterval)
}
if _, err := os.Stat(ssc.ProcPath); err != nil {
return fmt.Errorf("ProcPath %v check failed: %s", ssc.ProcPath, err)
}
if ssc.DiskConfig.LsblkTimeout <= time.Duration(0) {
return fmt.Errorf("LsblkTimeout %v must be above 0s", ssc.DiskConfig.LsblkTimeout)
}

View File

@@ -48,6 +48,7 @@ func TestApplyConfiguration(t *testing.T) {
},
InvokeIntervalString: "60s",
InvokeInterval: 60 * time.Second,
ProcPath: defaultProcPath,
},
},
{
@@ -66,6 +67,7 @@ func TestApplyConfiguration(t *testing.T) {
},
InvokeIntervalString: "1m0s",
InvokeInterval: 60 * time.Second,
ProcPath: defaultProcPath,
},
},
{

View File

@@ -19,8 +19,6 @@ import (
"strings"
)
var cmdlineFilePath = "/proc/cmdline"
type CmdlineArg struct {
Key string `json:"key"`
Value string `json:"value"`
@@ -52,7 +50,7 @@ func splitAfterSpace(inputChar rune) bool {
}
// CmdlineArgs returns all the kernel cmdline. It is read from cat /proc/cmdline.
func CmdlineArgs() ([]CmdlineArg, error) {
func CmdlineArgs(cmdlineFilePath string) ([]CmdlineArg, error) {
lines, err := ReadFileIntoLines(cmdlineFilePath)
if err != nil {
return nil, fmt.Errorf("error reading the file %s, %v", cmdlineFilePath, err)

View File

@@ -69,15 +69,9 @@ func TestCmdlineStats(t *testing.T) {
}
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
originalCmdlineFilePath := cmdlineFilePath
defer func() {
cmdlineFilePath = originalCmdlineFilePath
}()
cmdlineFilePath = test.fakeCmdlineFilePath
cmdlineArgs, err := CmdlineArgs()
cmdlineArgs, err := CmdlineArgs(test.fakeCmdlineFilePath)
if err != nil {
t.Errorf("Unexpected error retrieving cmdlineArgs: %v\nCmdlineArgsFilePath: %s\n", err, cmdlineFilePath)
t.Errorf("Unexpected error retrieving cmdlineArgs: %v\nCmdlineArgsFilePath: %s\n", err, test.fakeCmdlineFilePath)
}
for _, expectedCmdlineArg := range test.expectedCmdlineArgs {
assert.Contains(t, cmdlineArgs, expectedCmdlineArg, "Failed to find cmdlineArgs: %v\n", expectedCmdlineArg)

View File

@@ -20,8 +20,6 @@ import (
"strings"
)
var modulesFilePath = "/proc/modules"
type Module struct {
ModuleName string `json:"moduleName"`
Instances uint64 `json:"instances"`
@@ -37,7 +35,7 @@ func (d Module) String() string {
// Module returns all the kernel modules and their
// usage. It is read from cat /proc/modules.
func Modules() ([]Module, error) {
func Modules(modulesFilePath string) ([]Module, error) {
lines, err := ReadFileIntoLines(modulesFilePath)
if err != nil {
return nil, fmt.Errorf("error reading the contents of %s: %s", modulesFilePath, err)

View File

@@ -97,15 +97,9 @@ func TestModules(t *testing.T) {
}
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
originalModuleFilePath := modulesFilePath
defer func() {
modulesFilePath = originalModuleFilePath
}()
modulesFilePath = test.fakeModuleFilePath
modules, err := Modules()
modules, err := Modules(test.fakeModuleFilePath)
if err != nil {
t.Errorf("Unexpected error retrieving modules: %v\nModulesFilePath: %s\n", err, modulesFilePath)
t.Errorf("Unexpected error retrieving modules: %v\nModulesFilePath: %s\n", err, test.fakeModuleFilePath)
}
assert.Equal(t, modules, test.expectedModules, "unpected modules retrieved: %v, expected: %v", modules, test.expectedModules)
})