|
|
|
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_smartctl_bin", "smartctl")
|
|
|
|
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")
|
|
|
|
}
|