- updated dbdiagrams schema - [BREAKING] force failure if `notify.filter_attributes` or `notify.level` is set - added Settings table (and default values during migration) - Added Save Settings and Get Settings functions. - Added web API endpoints for getting and saving settings. - Deprecated old Notify* constants. Created new MetricsStatus* and MetricsNotifyLevel constants.pull/338/head
parent
dd0c3e6fba
commit
99af2b8b16
@ -1,62 +1,88 @@
|
|||||||
|
|
||||||
// SQLite Table(s)
|
// SQLite Table(s)
|
||||||
Table device {
|
|
||||||
created_at timestamp
|
|
||||||
|
|
||||||
wwn varchar [pk]
|
|
||||||
|
|
||||||
//user provided
|
|
||||||
label varchar
|
|
||||||
host_id varchar
|
|
||||||
|
|
||||||
// smartctl provided
|
|
||||||
device_name varchar
|
|
||||||
manufacturer varchar
|
|
||||||
model_name varchar
|
|
||||||
interface_type varchar
|
|
||||||
interface_speed varchar
|
|
||||||
serial_number varchar
|
|
||||||
firmware varchar
|
|
||||||
rotational_speed varchar
|
|
||||||
capacity varchar
|
|
||||||
form_factor varchar
|
|
||||||
smart_support varchar
|
|
||||||
device_protocol varchar
|
|
||||||
device_type varchar
|
|
||||||
|
|
||||||
|
Table Device {
|
||||||
|
//GORM attributes, see: http://gorm.io/docs/conventions.html
|
||||||
|
CreatedAt time
|
||||||
|
UpdatedAt time
|
||||||
|
DeletedAt time
|
||||||
|
|
||||||
|
WWN string
|
||||||
|
|
||||||
|
DeviceName string
|
||||||
|
DeviceUUID string
|
||||||
|
DeviceSerialID string
|
||||||
|
DeviceLabel string
|
||||||
|
|
||||||
|
Manufacturer string
|
||||||
|
ModelName string
|
||||||
|
InterfaceType string
|
||||||
|
InterfaceSpeed string
|
||||||
|
SerialNumber string
|
||||||
|
Firmware string
|
||||||
|
RotationSpeed int
|
||||||
|
Capacity int64
|
||||||
|
FormFactor string
|
||||||
|
SmartSupport bool
|
||||||
|
DeviceProtocol string//protocol determines which smart attribute types are available (ATA, NVMe, SCSI)
|
||||||
|
DeviceType string//device type is used for querying with -d/t flag, should only be used by collector.
|
||||||
|
|
||||||
|
// User provided metadata
|
||||||
|
Label string
|
||||||
|
HostId string
|
||||||
|
|
||||||
|
// Data set by Scrutiny
|
||||||
|
DeviceStatus enum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Table Setting {
|
||||||
|
//GORM attributes, see: http://gorm.io/docs/conventions.html
|
||||||
|
|
||||||
// InfluxDB Tables
|
SettingKeyName string
|
||||||
Table device_temperature {
|
SettingKeyDescription string
|
||||||
//timestamp
|
SettingDataType string
|
||||||
created_at timestamp
|
|
||||||
|
|
||||||
//tags (indexed & queryable)
|
|
||||||
device_wwn varchar [pk]
|
|
||||||
|
|
||||||
//fields
|
|
||||||
temp bigint
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Table smart_ata_results {
|
SettingValueNumeric int64
|
||||||
//timestamp
|
SettingValueString string
|
||||||
created_at timestamp
|
}
|
||||||
|
|
||||||
//tags (indexed & queryable)
|
|
||||||
device_wwn varchar [pk]
|
|
||||||
smart_status varchar
|
|
||||||
scrutiny_status varchar
|
|
||||||
|
|
||||||
|
|
||||||
|
// InfluxDB Tables
|
||||||
|
Table SmartTemperature {
|
||||||
|
Date time
|
||||||
|
DeviceWWN string //(tag)
|
||||||
|
Temp int64
|
||||||
|
}
|
||||||
|
|
||||||
//fields
|
|
||||||
temp bigint
|
|
||||||
power_on_hours bigint
|
|
||||||
power_cycle_count bigint
|
|
||||||
|
|
||||||
|
Table Smart {
|
||||||
|
Date time
|
||||||
|
DeviceWWN string //(tag)
|
||||||
|
DeviceProtocol string
|
||||||
|
|
||||||
|
//Metrics (fields)
|
||||||
|
Temp int64
|
||||||
|
PowerOnHours int64
|
||||||
|
PowerCycleCount int64
|
||||||
|
|
||||||
|
//Smart Status
|
||||||
|
Status enum
|
||||||
|
|
||||||
|
//SMART Attributes (fields)
|
||||||
|
Attr_ID_AttributeId int
|
||||||
|
Attr_ID_Value int64
|
||||||
|
Attr_ID_Threshold int64
|
||||||
|
Attr_ID_Worst int64
|
||||||
|
Attr_ID_RawValue int64
|
||||||
|
Attr_ID_RawString string
|
||||||
|
Attr_ID_WhenFailed string
|
||||||
|
//Generated data
|
||||||
|
Attr_ID_TransformedValue int64
|
||||||
|
Attr_ID_Status enum
|
||||||
|
Attr_ID_StatusReason string
|
||||||
|
Attr_ID_FailureRate float64
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref: device.wwn < smart_ata_results.device_wwn
|
Ref: Device.WWN < Smart.DeviceWWN
|
||||||
|
Ref: Device.WWN < SmartTemperature.DeviceWWN
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sr *scrutinyRepository) GetSettings(ctx context.Context) (*models.Settings, error) {
|
||||||
|
settingsEntries := []models.SettingEntry{}
|
||||||
|
if err := sr.gormClient.WithContext(ctx).Find(&settingsEntries).Error; err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not get settings from DB: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings := models.Settings{}
|
||||||
|
settings.PopulateFromSettingEntries(settingsEntries)
|
||||||
|
|
||||||
|
return &settings, nil
|
||||||
|
}
|
||||||
|
func (sr *scrutinyRepository) SaveSettings(ctx context.Context, settings models.Settings) error {
|
||||||
|
|
||||||
|
//get current settings
|
||||||
|
settingsEntries := []models.SettingEntry{}
|
||||||
|
if err := sr.gormClient.WithContext(ctx).Find(&settingsEntries).Error; err != nil {
|
||||||
|
return fmt.Errorf("Could not get settings from DB: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// override with values from settings object
|
||||||
|
settingsEntries = settings.UpdateSettingEntries(settingsEntries)
|
||||||
|
|
||||||
|
// store in database.
|
||||||
|
return sr.gormClient.Updates(&settingsEntries).Error
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Setting struct {
|
|
||||||
//GORM attributes, see: http://gorm.io/docs/conventions.html
|
|
||||||
gorm.Model
|
|
||||||
|
|
||||||
SettingKeyName string `json:"setting_key_name"`
|
|
||||||
SettingDataType string `json:"setting_data_type"`
|
|
||||||
|
|
||||||
SettingValueNumeric int64 `json:"setting_value_numeric"`
|
|
||||||
SettingValueString string `json:"setting_value_string"`
|
|
||||||
}
|
|
@ -0,0 +1,22 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SettingEntry matches a setting row in the database
|
||||||
|
type SettingEntry struct {
|
||||||
|
//GORM attributes, see: http://gorm.io/docs/conventions.html
|
||||||
|
gorm.Model
|
||||||
|
|
||||||
|
SettingKeyName string `json:"setting_key_name" gorm:"unique;not null"`
|
||||||
|
SettingKeyDescription string `json:"setting_key_description"`
|
||||||
|
SettingDataType string `json:"setting_data_type"`
|
||||||
|
|
||||||
|
SettingValueNumeric int64 `json:"setting_value_numeric"`
|
||||||
|
SettingValueString string `json:"setting_value_string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SettingEntry) TableName() string {
|
||||||
|
return "settings"
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/analogj/scrutiny/webapp/backend/pkg"
|
||||||
|
|
||||||
|
// Settings is made up of parsed SettingEntry objects retrieved from the database
|
||||||
|
type Settings struct {
|
||||||
|
MetricsNotifyLevel pkg.MetricsNotifyLevel `json:"metrics_notify_level"`
|
||||||
|
MetricsStatusFilterAttributes pkg.MetricsStatusFilterAttributes `json:"metrics_status_filter_attributes"`
|
||||||
|
MetricsStatusThreshold pkg.MetricsStatusThreshold `json:"metrics_status_threshold"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Settings) PopulateFromSettingEntries(entries []SettingEntry) {
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.SettingKeyName == "metrics.notify.level" {
|
||||||
|
s.MetricsNotifyLevel = pkg.MetricsNotifyLevel(entry.SettingValueNumeric)
|
||||||
|
} 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
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/analogj/scrutiny/webapp/backend/pkg/database"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSettings(c *gin.Context) {
|
||||||
|
logger := c.MustGet("LOGGER").(logrus.FieldLogger)
|
||||||
|
deviceRepo := c.MustGet("DEVICE_REPOSITORY").(database.DeviceRepo)
|
||||||
|
|
||||||
|
settings, err := deviceRepo.GetSettings(c)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("An error occurred while retrieving settings", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"settings": settings,
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/analogj/scrutiny/webapp/backend/pkg/database"
|
||||||
|
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SaveSettings(c *gin.Context) {
|
||||||
|
logger := c.MustGet("LOGGER").(logrus.FieldLogger)
|
||||||
|
deviceRepo := c.MustGet("DEVICE_REPOSITORY").(database.DeviceRepo)
|
||||||
|
|
||||||
|
var settings models.Settings
|
||||||
|
err := c.BindJSON(&settings)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("Cannot parse updated settings", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = deviceRepo.SaveSettings(c, settings)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("An error occurred while saving settings", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in new issue