Files
scrutiny/collector/pkg/config/config.go
T
Jason Kulatunga a53397210c adding mechanism to override the smartctl commands used by scrutiny for device scanning, device identification and smart data retrieval.
adding tests for command overrides.

rename GetScanOverrides() to GetDeviceOverrides()

fixes #255
2022-05-28 15:32:44 -07:00

188 lines
5.3 KiB
Go

package config
import (
"fmt"
"github.com/analogj/go-util/utils"
"github.com/analogj/scrutiny/collector/pkg/errors"
"github.com/analogj/scrutiny/collector/pkg/models"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"log"
"os"
"sort"
"strings"
)
// When initializing this class the following methods must be called:
// Config.New
// Config.Init
// This is done automatically when created via the Factory.
type configuration struct {
*viper.Viper
deviceOverrides []models.ScanOverride
}
//Viper uses the following precedence order. Each item takes precedence over the item below it:
// explicit call to Set
// flag
// env
// config
// key/value store
// default
func (c *configuration) Init() error {
c.Viper = viper.New()
//set defaults
c.SetDefault("host.id", "")
c.SetDefault("devices", []string{})
c.SetDefault("log.level", "INFO")
c.SetDefault("log.file", "")
c.SetDefault("api.endpoint", "http://localhost:8080")
c.SetDefault("commands.metrics_scan_args", "--scan --json")
c.SetDefault("commands.metrics_info_args", "--info --json")
c.SetDefault("commands.metrics_smart_args", "--xall --json")
//c.SetDefault("collect.short.command", "-a -o on -S on")
//if you want to load a non-standard location system config file (~/drawbridge.yml), use ReadConfig
c.SetConfigType("yaml")
//c.SetConfigName("drawbridge")
//c.AddConfigPath("$HOME/")
//CLI options will be added via the `Set()` function
return nil
}
func (c *configuration) ReadConfig(configFilePath string) error {
configFilePath, err := utils.ExpandPath(configFilePath)
if err != nil {
return err
}
if !utils.FileExists(configFilePath) {
log.Printf("No configuration file found at %v. Using Defaults.", configFilePath)
return errors.ConfigFileMissingError("The configuration file could not be found.")
}
//validate config file contents
//err = c.ValidateConfigFile(configFilePath)
//if err != nil {
// log.Printf("Config file at `%v` is invalid: %s", configFilePath, err)
// return err
//}
log.Printf("Loading configuration file: %s", configFilePath)
config_data, err := os.Open(configFilePath)
if err != nil {
log.Printf("Error reading configuration file: %s", err)
return err
}
err = c.MergeConfig(config_data)
if err != nil {
return err
}
return c.ValidateConfig()
}
// This function ensures that the merged config works correctly.
func (c *configuration) ValidateConfig() error {
//TODO:
// check that device prefix matches OS
// check that schema of config file is valid
// check that the collector commands are valid
commandArgStrings := map[string]string{
"commands.metrics_scan_args": c.GetString("commands.metrics_scan_args"),
"commands.metrics_info_args": c.GetString("commands.metrics_info_args"),
"commands.metrics_smart_args": c.GetString("commands.metrics_smart_args"),
}
errorStrings := []string{}
for configKey, commandArgString := range commandArgStrings {
args := strings.Split(commandArgString, " ")
//ensure that the args string contains `--json` or `-j` flag
containsJsonFlag := false
containsDeviceFlag := false
for _, flag := range args {
if strings.HasPrefix(flag, "--json") || strings.HasPrefix(flag, "-j") {
containsJsonFlag = true
}
if strings.HasPrefix(flag, "--device") || strings.HasPrefix(flag, "-d") {
containsDeviceFlag = true
}
}
if !containsJsonFlag {
errorStrings = append(errorStrings, fmt.Sprintf("configuration key '%s' is missing '--json' flag", configKey))
}
if containsDeviceFlag {
errorStrings = append(errorStrings, fmt.Sprintf("configuration key '%s' must not contain '--device' or '-d' flag", configKey))
}
}
//sort(errorStrings)
sort.Strings(errorStrings)
if len(errorStrings) == 0 {
return nil
} else {
return errors.ConfigValidationError(strings.Join(errorStrings, ", "))
}
}
func (c *configuration) GetDeviceOverrides() []models.ScanOverride {
// we have to support 2 types of device types.
// - simple device type (device_type: 'sat')
// and list of device types (type: \n- 3ware,0 \n- 3ware,1 \n- 3ware,2)
// GetString will return "" if this is a list of device types.
if c.deviceOverrides == nil {
overrides := []models.ScanOverride{}
c.UnmarshalKey("devices", &overrides, func(c *mapstructure.DecoderConfig) { c.WeaklyTypedInput = true })
c.deviceOverrides = overrides
}
return c.deviceOverrides
}
func (c *configuration) GetCommandMetricsInfoArgs(deviceName string) string {
overrides := c.GetDeviceOverrides()
for _, deviceOverrides := range overrides {
if strings.ToLower(deviceName) == strings.ToLower(deviceOverrides.Device) {
//found matching device
if len(deviceOverrides.Commands.MetricsInfoArgs) > 0 {
return deviceOverrides.Commands.MetricsInfoArgs
} else {
return c.GetString("commands.metrics_info_args")
}
}
}
return c.GetString("commands.metrics_info_args")
}
func (c *configuration) GetCommandMetricsSmartArgs(deviceName string) string {
overrides := c.GetDeviceOverrides()
for _, deviceOverrides := range overrides {
if strings.ToLower(deviceName) == strings.ToLower(deviceOverrides.Device) {
//found matching device
if len(deviceOverrides.Commands.MetricsSmartArgs) > 0 {
return deviceOverrides.Commands.MetricsSmartArgs
} else {
return c.GetString("commands.metrics_smart_args")
}
}
}
return c.GetString("commands.metrics_smart_args")
}