working settings update.
Settings are loaded from the DB and added to the AppConfig during startup. When updating settings, they are stored in AppConfig, and written do the database.
This commit is contained in:
@@ -66,6 +66,10 @@ func (c *configuration) Init() error {
|
|||||||
return c.ValidateConfig()
|
return c.ValidateConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *configuration) SubKeys(key string) []string {
|
||||||
|
return c.Sub(key).AllKeys()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *configuration) ReadConfig(configFilePath string) error {
|
func (c *configuration) ReadConfig(configFilePath string) error {
|
||||||
//make sure that we specify that this is the correct config path (for eventual WriteConfig() calls)
|
//make sure that we specify that this is the correct config path (for eventual WriteConfig() calls)
|
||||||
c.SetConfigFile(configFilePath)
|
c.SetConfigFile(configFilePath)
|
||||||
|
|||||||
@@ -12,12 +12,16 @@ type Interface interface {
|
|||||||
WriteConfig() error
|
WriteConfig() error
|
||||||
Set(key string, value interface{})
|
Set(key string, value interface{})
|
||||||
SetDefault(key string, value interface{})
|
SetDefault(key string, value interface{})
|
||||||
|
MergeConfigMap(cfg map[string]interface{}) error
|
||||||
|
|
||||||
AllSettings() map[string]interface{}
|
AllSettings() map[string]interface{}
|
||||||
|
AllKeys() []string
|
||||||
|
SubKeys(key string) []string
|
||||||
IsSet(key string) bool
|
IsSet(key string) bool
|
||||||
Get(key string) interface{}
|
Get(key string) interface{}
|
||||||
GetBool(key string) bool
|
GetBool(key string) bool
|
||||||
GetInt(key string) int
|
GetInt(key string) int
|
||||||
|
GetInt64(key string) int64
|
||||||
GetString(key string) string
|
GetString(key string) string
|
||||||
GetStringSlice(key string) []string
|
GetStringSlice(key string) []string
|
||||||
UnmarshalKey(key string, rawVal interface{}, decoderOpts ...viper.DecoderConfigOption) error
|
UnmarshalKey(key string, rawVal interface{}, decoderOpts ...viper.DecoderConfigOption) error
|
||||||
|
|||||||
@@ -67,14 +67,5 @@ const (
|
|||||||
// Deprecated
|
// Deprecated
|
||||||
const NotifyFilterAttributesAll = "all"
|
const NotifyFilterAttributesAll = "all"
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
const NotifyFilterAttributesCritical = "critical"
|
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
const NotifyLevelFail = "fail"
|
const NotifyLevelFail = "fail"
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
const NotifyLevelFailScrutiny = "fail_scrutiny"
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
const NotifyLevelFailSmart = "fail_smart"
|
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import (
|
|||||||
type DeviceRepo interface {
|
type DeviceRepo interface {
|
||||||
Close() error
|
Close() error
|
||||||
|
|
||||||
//GetSettings()
|
|
||||||
//SaveSetting()
|
|
||||||
|
|
||||||
RegisterDevice(ctx context.Context, dev models.Device) error
|
RegisterDevice(ctx context.Context, dev models.Device) error
|
||||||
GetDevices(ctx context.Context) ([]models.Device, error)
|
GetDevices(ctx context.Context) ([]models.Device, error)
|
||||||
UpdateDevice(ctx context.Context, wwn string, collectorSmartData collector.SmartInfo) (models.Device, error)
|
UpdateDevice(ctx context.Context, wwn string, collectorSmartData collector.SmartInfo) (models.Device, error)
|
||||||
@@ -29,6 +26,6 @@ type DeviceRepo interface {
|
|||||||
GetSummary(ctx context.Context) (map[string]*models.DeviceSummary, error)
|
GetSummary(ctx context.Context) (map[string]*models.DeviceSummary, error)
|
||||||
GetSmartTemperatureHistory(ctx context.Context, durationKey string) (map[string][]measurements.SmartTemperature, error)
|
GetSmartTemperatureHistory(ctx context.Context, durationKey string) (map[string][]measurements.SmartTemperature, error)
|
||||||
|
|
||||||
GetSettings(ctx context.Context) (*models.Settings, error)
|
LoadSettings(ctx context.Context) (*models.Settings, error)
|
||||||
SaveSettings(ctx context.Context, settings models.Settings) error
|
SaveSettings(ctx context.Context, settings models.Settings) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error {
|
|||||||
{
|
{
|
||||||
SettingKeyName: "metrics.status.threshold",
|
SettingKeyName: "metrics.status.threshold",
|
||||||
SettingKeyDescription: "Determines which threshold should impact device status",
|
SettingKeyDescription: "Determines which threshold should impact device status",
|
||||||
SettingDataType: "string",
|
SettingDataType: "numeric",
|
||||||
SettingValueNumeric: int64(pkg.MetricsStatusThresholdBoth), // options: 'scrutiny', 'smart', 'both'
|
SettingValueNumeric: int64(pkg.MetricsStatusThresholdBoth), // options: 'scrutiny', 'smart', 'both'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,30 +4,79 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (sr *scrutinyRepository) GetSettings(ctx context.Context) (*models.Settings, error) {
|
const DBSETTING_SUBKEY = "dbsetting"
|
||||||
|
|
||||||
|
// LoadSettings will retrieve settings from the database, store them in the AppConfig object, and return a Settings struct
|
||||||
|
func (sr *scrutinyRepository) LoadSettings(ctx context.Context) (*models.Settings, error) {
|
||||||
settingsEntries := []models.SettingEntry{}
|
settingsEntries := []models.SettingEntry{}
|
||||||
if err := sr.gormClient.WithContext(ctx).Find(&settingsEntries).Error; err != nil {
|
if err := sr.gormClient.WithContext(ctx).Find(&settingsEntries).Error; err != nil {
|
||||||
return nil, fmt.Errorf("Could not get settings from DB: %v", err)
|
return nil, fmt.Errorf("Could not get settings from DB: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings := models.Settings{}
|
// store retrieved settings in the AppConfig obj
|
||||||
settings.PopulateFromSettingEntries(settingsEntries)
|
for _, settingsEntry := range settingsEntries {
|
||||||
|
configKey := fmt.Sprintf("%s.%s", DBSETTING_SUBKEY, settingsEntry.SettingKeyName)
|
||||||
|
|
||||||
|
if settingsEntry.SettingDataType == "numeric" {
|
||||||
|
sr.appConfig.SetDefault(configKey, settingsEntry.SettingValueNumeric)
|
||||||
|
} else if settingsEntry.SettingDataType == "string" {
|
||||||
|
sr.appConfig.SetDefault(configKey, settingsEntry.SettingValueString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal the dbsetting object data to a settings object.
|
||||||
|
var settings models.Settings
|
||||||
|
err := sr.appConfig.UnmarshalKey(DBSETTING_SUBKEY, &settings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &settings, nil
|
return &settings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testing
|
||||||
|
// curl -d '{"metrics": { "notify": { "level": 5 }, "status": { "filter_attributes": 5, "threshold": 5 } }}' -H "Content-Type: application/json" -X POST http://localhost:9090/api/settings
|
||||||
|
// SaveSettings will update settings in AppConfig object, then save the settings to the database.
|
||||||
func (sr *scrutinyRepository) SaveSettings(ctx context.Context, settings models.Settings) error {
|
func (sr *scrutinyRepository) SaveSettings(ctx context.Context, settings models.Settings) error {
|
||||||
|
|
||||||
//get current settings
|
//save the entries to the appconfig
|
||||||
|
settingsMap := &map[string]interface{}{}
|
||||||
|
err := mapstructure.Decode(settings, &settingsMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settingsWrapperMap := map[string]interface{}{}
|
||||||
|
settingsWrapperMap[DBSETTING_SUBKEY] = *settingsMap
|
||||||
|
err = sr.appConfig.MergeConfigMap(settingsWrapperMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//retrieve current settings from the database
|
||||||
settingsEntries := []models.SettingEntry{}
|
settingsEntries := []models.SettingEntry{}
|
||||||
if err := sr.gormClient.WithContext(ctx).Find(&settingsEntries).Error; err != nil {
|
if err := sr.gormClient.WithContext(ctx).Find(&settingsEntries).Error; err != nil {
|
||||||
return fmt.Errorf("Could not get settings from DB: %v", err)
|
return fmt.Errorf("Could not get settings from DB: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// override with values from settings object
|
//update settingsEntries
|
||||||
settingsEntries = settings.UpdateSettingEntries(settingsEntries)
|
for ndx, settingsEntry := range settingsEntries {
|
||||||
|
configKey := fmt.Sprintf("%s.%s", DBSETTING_SUBKEY, settingsEntry.SettingKeyName)
|
||||||
|
|
||||||
// store in database.
|
if settingsEntry.SettingDataType == "numeric" {
|
||||||
return sr.gormClient.Updates(&settingsEntries).Error
|
settingsEntries[ndx].SettingValueNumeric = sr.appConfig.GetInt(configKey)
|
||||||
|
} else if settingsEntry.SettingDataType == "string" {
|
||||||
|
settingsEntries[ndx].SettingValueString = sr.appConfig.GetString(configKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// store in database.
|
||||||
|
//TODO: this should be `sr.gormClient.Updates(&settingsEntries).Error`
|
||||||
|
err := sr.gormClient.Model(&models.SettingEntry{}).Where([]uint{settingsEntry.ID}).Select("setting_value_numeric", "setting_value_string").Updates(settingsEntries[ndx]).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type SettingEntry struct {
|
|||||||
SettingKeyDescription string `json:"setting_key_description"`
|
SettingKeyDescription string `json:"setting_key_description"`
|
||||||
SettingDataType string `json:"setting_data_type"`
|
SettingDataType string `json:"setting_data_type"`
|
||||||
|
|
||||||
SettingValueNumeric int64 `json:"setting_value_numeric"`
|
SettingValueNumeric int `json:"setting_value_numeric"`
|
||||||
SettingValueString string `json:"setting_value_string"`
|
SettingValueString string `json:"setting_value_string"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,20 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/analogj/scrutiny/webapp/backend/pkg"
|
|
||||||
|
|
||||||
// Settings is made up of parsed SettingEntry objects retrieved from the database
|
// Settings is made up of parsed SettingEntry objects retrieved from the database
|
||||||
|
//type Settings struct {
|
||||||
|
// MetricsNotifyLevel pkg.MetricsNotifyLevel `json:"metrics.notify.level" mapstructure:"metrics.notify.level"`
|
||||||
|
// MetricsStatusFilterAttributes pkg.MetricsStatusFilterAttributes `json:"metrics.status.filter_attributes" mapstructure:"metrics.status.filter_attributes"`
|
||||||
|
// MetricsStatusThreshold pkg.MetricsStatusThreshold `json:"metrics.status.threshold" mapstructure:"metrics.status.threshold"`
|
||||||
|
//}
|
||||||
|
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
MetricsNotifyLevel pkg.MetricsNotifyLevel `json:"metrics_notify_level"`
|
Metrics struct {
|
||||||
MetricsStatusFilterAttributes pkg.MetricsStatusFilterAttributes `json:"metrics_status_filter_attributes"`
|
Notify struct {
|
||||||
MetricsStatusThreshold pkg.MetricsStatusThreshold `json:"metrics_status_threshold"`
|
Level int `json:"level" mapstructure:"level"`
|
||||||
}
|
} `json:"notify" mapstructure:"notify"`
|
||||||
|
Status struct {
|
||||||
func (s *Settings) PopulateFromSettingEntries(entries []SettingEntry) {
|
FilterAttributes int `json:"filter_attributes" mapstructure:"filter_attributes"`
|
||||||
for _, entry := range entries {
|
Threshold int `json:"threshold" mapstructure:"threshold"`
|
||||||
if entry.SettingKeyName == "metrics.notify.level" {
|
} `json:"status" mapstructure:"status"`
|
||||||
s.MetricsNotifyLevel = pkg.MetricsNotifyLevel(entry.SettingValueNumeric)
|
} `json:"metrics" mapstructure:"metrics"`
|
||||||
} else if entry.SettingKeyName == "metrics.status.filter_attributes" {
|
|
||||||
s.MetricsStatusFilterAttributes = pkg.MetricsStatusFilterAttributes(entry.SettingValueNumeric)
|
|
||||||
} else if entry.SettingKeyName == "metrics.status.threshold" {
|
|
||||||
s.MetricsStatusThreshold = pkg.MetricsStatusThreshold(entry.SettingValueNumeric)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Settings) UpdateSettingEntries(entries []SettingEntry) []SettingEntry {
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.SettingKeyName == "metrics.notify.level" {
|
|
||||||
entry.SettingValueNumeric = int64(s.MetricsNotifyLevel)
|
|
||||||
} else if entry.SettingKeyName == "metrics.status.filter_attributes" {
|
|
||||||
entry.SettingValueNumeric = int64(s.MetricsStatusFilterAttributes)
|
|
||||||
} else if entry.SettingKeyName == "metrics.status.threshold" {
|
|
||||||
entry.SettingValueNumeric = int64(s.MetricsStatusThreshold)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entries
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,20 +29,22 @@ const NotifyFailureTypeSmartFailure = "SmartFailure"
|
|||||||
const NotifyFailureTypeScrutinyFailure = "ScrutinyFailure"
|
const NotifyFailureTypeScrutinyFailure = "ScrutinyFailure"
|
||||||
|
|
||||||
// ShouldNotify check if the error Message should be filtered (level mismatch or filtered_attributes)
|
// ShouldNotify check if the error Message should be filtered (level mismatch or filtered_attributes)
|
||||||
func ShouldNotify(device models.Device, smartAttrs measurements.Smart, notifyLevel string, notifyFilterAttributes string) bool {
|
func ShouldNotify(device models.Device, smartAttrs measurements.Smart, statusThreshold pkg.MetricsStatusThreshold, statusFilterAttributes pkg.MetricsStatusFilterAttributes) bool {
|
||||||
// 1. check if the device is healthy
|
// 1. check if the device is healthy
|
||||||
if device.DeviceStatus == pkg.DeviceStatusPassed {
|
if device.DeviceStatus == pkg.DeviceStatusPassed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: cannot check for warning notifyLevel yet.
|
||||||
|
|
||||||
// setup constants for comparison
|
// setup constants for comparison
|
||||||
var requiredDeviceStatus pkg.DeviceStatus
|
var requiredDeviceStatus pkg.DeviceStatus
|
||||||
var requiredAttrStatus pkg.AttributeStatus
|
var requiredAttrStatus pkg.AttributeStatus
|
||||||
if notifyLevel == pkg.NotifyLevelFail {
|
if statusThreshold == pkg.MetricsStatusThresholdBoth {
|
||||||
// either scrutiny or smart failures should trigger an email
|
// either scrutiny or smart failures should trigger an email
|
||||||
requiredDeviceStatus = pkg.DeviceStatusSet(pkg.DeviceStatusFailedSmart, pkg.DeviceStatusFailedScrutiny)
|
requiredDeviceStatus = pkg.DeviceStatusSet(pkg.DeviceStatusFailedSmart, pkg.DeviceStatusFailedScrutiny)
|
||||||
requiredAttrStatus = pkg.AttributeStatusSet(pkg.AttributeStatusFailedSmart, pkg.AttributeStatusFailedScrutiny)
|
requiredAttrStatus = pkg.AttributeStatusSet(pkg.AttributeStatusFailedSmart, pkg.AttributeStatusFailedScrutiny)
|
||||||
} else if notifyLevel == pkg.NotifyLevelFailSmart {
|
} else if statusThreshold == pkg.MetricsStatusThresholdSmart {
|
||||||
//only smart failures
|
//only smart failures
|
||||||
requiredDeviceStatus = pkg.DeviceStatusFailedSmart
|
requiredDeviceStatus = pkg.DeviceStatusFailedSmart
|
||||||
requiredAttrStatus = pkg.AttributeStatusFailedSmart
|
requiredAttrStatus = pkg.AttributeStatusFailedSmart
|
||||||
@@ -53,9 +55,9 @@ func ShouldNotify(device models.Device, smartAttrs measurements.Smart, notifyLev
|
|||||||
|
|
||||||
// 2. check if the attributes that are failing should be filtered (non-critical)
|
// 2. check if the attributes that are failing should be filtered (non-critical)
|
||||||
// 3. for any unfiltered attribute, store the failure reason (Smart or Scrutiny)
|
// 3. for any unfiltered attribute, store the failure reason (Smart or Scrutiny)
|
||||||
if notifyFilterAttributes == pkg.NotifyFilterAttributesCritical {
|
if statusFilterAttributes == pkg.MetricsStatusFilterAttributesCritical {
|
||||||
hasFailingCriticalAttr := false
|
hasFailingCriticalAttr := false
|
||||||
var statusFailingCrtiticalAttr pkg.AttributeStatus
|
var statusFailingCriticalAttr pkg.AttributeStatus
|
||||||
|
|
||||||
for attrId, attrData := range smartAttrs.Attributes {
|
for attrId, attrData := range smartAttrs.Attributes {
|
||||||
//find failing attribute
|
//find failing attribute
|
||||||
@@ -64,7 +66,7 @@ func ShouldNotify(device models.Device, smartAttrs measurements.Smart, notifyLev
|
|||||||
}
|
}
|
||||||
|
|
||||||
// merge the status's of all critical attributes
|
// merge the status's of all critical attributes
|
||||||
statusFailingCrtiticalAttr = pkg.AttributeStatusSet(statusFailingCrtiticalAttr, attrData.GetStatus())
|
statusFailingCriticalAttr = pkg.AttributeStatusSet(statusFailingCriticalAttr, attrData.GetStatus())
|
||||||
|
|
||||||
//found a failing attribute, see if its critical
|
//found a failing attribute, see if its critical
|
||||||
if device.IsScsi() && thresholds.ScsiMetadata[attrId].Critical {
|
if device.IsScsi() && thresholds.ScsiMetadata[attrId].Critical {
|
||||||
@@ -89,7 +91,7 @@ func ShouldNotify(device models.Device, smartAttrs measurements.Smart, notifyLev
|
|||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
// check if any of the critical attributes have a status that we're looking for
|
// check if any of the critical attributes have a status that we're looking for
|
||||||
return pkg.AttributeStatusHas(statusFailingCrtiticalAttr, requiredAttrStatus)
|
return pkg.AttributeStatusHas(statusFailingCriticalAttr, requiredAttrStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,56 +15,56 @@ func TestShouldNotify_MustSkipPassingDevices(t *testing.T) {
|
|||||||
DeviceStatus: pkg.DeviceStatusPassed,
|
DeviceStatus: pkg.DeviceStatusPassed,
|
||||||
}
|
}
|
||||||
smartAttrs := measurements.Smart{}
|
smartAttrs := measurements.Smart{}
|
||||||
notifyLevel := pkg.NotifyLevelFail
|
statusThreshold := pkg.MetricsStatusThresholdBoth
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesAll
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.False(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyLevelFail_FailingSmartDevice(t *testing.T) {
|
func TestShouldNotify_MetricsStatusThresholdBoth_FailingSmartDevice(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
DeviceStatus: pkg.DeviceStatusFailedSmart,
|
DeviceStatus: pkg.DeviceStatusFailedSmart,
|
||||||
}
|
}
|
||||||
smartAttrs := measurements.Smart{}
|
smartAttrs := measurements.Smart{}
|
||||||
notifyLevel := pkg.NotifyLevelFail
|
statusThreshold := pkg.MetricsStatusThresholdBoth
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesAll
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.True(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyLevelFailSmart_FailingSmartDevice(t *testing.T) {
|
func TestShouldNotify_MetricsStatusThresholdSmart_FailingSmartDevice(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
DeviceStatus: pkg.DeviceStatusFailedSmart,
|
DeviceStatus: pkg.DeviceStatusFailedSmart,
|
||||||
}
|
}
|
||||||
smartAttrs := measurements.Smart{}
|
smartAttrs := measurements.Smart{}
|
||||||
notifyLevel := pkg.NotifyLevelFailSmart
|
statusThreshold := pkg.MetricsStatusThresholdSmart
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesAll
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.True(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyLevelFailScrutiny_FailingSmartDevice(t *testing.T) {
|
func TestShouldNotify_MetricsStatusThresholdScrutiny_FailingSmartDevice(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
DeviceStatus: pkg.DeviceStatusFailedSmart,
|
DeviceStatus: pkg.DeviceStatusFailedSmart,
|
||||||
}
|
}
|
||||||
smartAttrs := measurements.Smart{}
|
smartAttrs := measurements.Smart{}
|
||||||
notifyLevel := pkg.NotifyLevelFailScrutiny
|
statusThreshold := pkg.MetricsStatusThresholdScrutiny
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesAll
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.False(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyFilterAttributesCritical_WithCriticalAttrs(t *testing.T) {
|
func TestShouldNotify_MetricsStatusFilterAttributesCritical_WithCriticalAttrs(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
@@ -75,14 +75,14 @@ func TestShouldNotify_NotifyFilterAttributesCritical_WithCriticalAttrs(t *testin
|
|||||||
Status: pkg.AttributeStatusFailedSmart,
|
Status: pkg.AttributeStatusFailedSmart,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
notifyLevel := pkg.NotifyLevelFail
|
statusThreshold := pkg.MetricsStatusThresholdBoth
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesCritical
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.True(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyFilterAttributesCritical_WithMultipleCriticalAttrs(t *testing.T) {
|
func TestShouldNotify_MetricsStatusFilterAttributesCritical_WithMultipleCriticalAttrs(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
@@ -96,14 +96,14 @@ func TestShouldNotify_NotifyFilterAttributesCritical_WithMultipleCriticalAttrs(t
|
|||||||
Status: pkg.AttributeStatusFailedScrutiny,
|
Status: pkg.AttributeStatusFailedScrutiny,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
notifyLevel := pkg.NotifyLevelFail
|
statusThreshold := pkg.MetricsStatusThresholdBoth
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesCritical
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.True(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyFilterAttributesCritical_WithNoCriticalAttrs(t *testing.T) {
|
func TestShouldNotify_MetricsStatusFilterAttributesCritical_WithNoCriticalAttrs(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
@@ -114,14 +114,14 @@ func TestShouldNotify_NotifyFilterAttributesCritical_WithNoCriticalAttrs(t *test
|
|||||||
Status: pkg.AttributeStatusFailedSmart,
|
Status: pkg.AttributeStatusFailedSmart,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
notifyLevel := pkg.NotifyLevelFail
|
statusThreshold := pkg.MetricsStatusThresholdBoth
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesCritical
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.False(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyFilterAttributesCritical_WithNoFailingCriticalAttrs(t *testing.T) {
|
func TestShouldNotify_MetricsStatusFilterAttributesCritical_WithNoFailingCriticalAttrs(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
@@ -132,14 +132,14 @@ func TestShouldNotify_NotifyFilterAttributesCritical_WithNoFailingCriticalAttrs(
|
|||||||
Status: pkg.AttributeStatusPassed,
|
Status: pkg.AttributeStatusPassed,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
notifyLevel := pkg.NotifyLevelFail
|
statusThreshold := pkg.MetricsStatusThresholdBoth
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesCritical
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.False(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldNotify_NotifyFilterAttributesCritical_NotifyLevelFailSmart_WithCriticalAttrsFailingScrutiny(t *testing.T) {
|
func TestShouldNotify_MetricsStatusFilterAttributesCritical_MetricsStatusThresholdSmart_WithCriticalAttrsFailingScrutiny(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//setup
|
//setup
|
||||||
device := models.Device{
|
device := models.Device{
|
||||||
@@ -153,9 +153,9 @@ func TestShouldNotify_NotifyFilterAttributesCritical_NotifyLevelFailSmart_WithCr
|
|||||||
Status: pkg.AttributeStatusFailedScrutiny,
|
Status: pkg.AttributeStatusFailedScrutiny,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
notifyLevel := pkg.NotifyLevelFailSmart
|
statusThreshold := pkg.MetricsStatusThresholdSmart
|
||||||
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
|
notifyFilterAttributes := pkg.MetricsStatusFilterAttributesCritical
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
|
require.False(t, ShouldNotify(device, smartAttrs, statusThreshold, notifyFilterAttributes))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func GetSettings(c *gin.Context) {
|
|||||||
logger := c.MustGet("LOGGER").(logrus.FieldLogger)
|
logger := c.MustGet("LOGGER").(logrus.FieldLogger)
|
||||||
deviceRepo := c.MustGet("DEVICE_REPOSITORY").(database.DeviceRepo)
|
deviceRepo := c.MustGet("DEVICE_REPOSITORY").(database.DeviceRepo)
|
||||||
|
|
||||||
settings, err := deviceRepo.GetSettings(c)
|
settings, err := deviceRepo.LoadSettings(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorln("An error occurred while retrieving settings", err)
|
logger.Errorln("An error occurred while retrieving settings", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
|||||||
@@ -67,7 +67,12 @@ func UploadDeviceMetrics(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check for error
|
//check for error
|
||||||
if notify.ShouldNotify(updatedDevice, smartData, appConfig.GetString("notify.level"), appConfig.GetString("notify.filter_attributes")) {
|
if notify.ShouldNotify(
|
||||||
|
updatedDevice,
|
||||||
|
smartData,
|
||||||
|
pkg.MetricsStatusThreshold(appConfig.GetInt("dbsetting.metrics.status.threshold")),
|
||||||
|
pkg.MetricsStatusFilterAttributes(appConfig.GetInt("dbsetting.metrics.status.filter_attributes")),
|
||||||
|
) {
|
||||||
//send notifications
|
//send notifications
|
||||||
|
|
||||||
liveNotify := notify.New(
|
liveNotify := notify.New(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func LoggerMiddleware(logger logrus.FieldLogger) gin.HandlerFunc {
|
|||||||
|
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hostname = "unknow"
|
hostname = "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/database"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -14,6 +15,14 @@ func RepositoryMiddleware(appConfig config.Interface, globalLogger logrus.FieldL
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure the settings have been loaded into the app config during startup.
|
||||||
|
_, err = deviceRepo.LoadSettings(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//settings.UpdateSettingEntries()
|
||||||
|
|
||||||
//TODO: determine where we can call defer deviceRepo.Close()
|
//TODO: determine where we can call defer deviceRepo.Close()
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
c.Set("DEVICE_REPOSITORY", deviceRepo)
|
c.Set("DEVICE_REPOSITORY", deviceRepo)
|
||||||
|
|||||||
Reference in New Issue
Block a user