Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pre v0.5.0 #352

Merged
merged 35 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6ca4bd4
fix the WORKDIR for collector image.
AnalogJ Jul 14, 2022
e9c1de9
update support table in README.
AnalogJ Jul 16, 2022
5b2746f
initial settings table.
AnalogJ Jun 26, 2022
dd0c3e6
rename the migration model package name.
AnalogJ Jul 17, 2022
99af2b8
WIP settings system.
AnalogJ Jul 17, 2022
29bc799
working settings update.
AnalogJ Jul 20, 2022
c0f1dfd
fixing config mock.
AnalogJ Jul 21, 2022
54e2cac
move frontend settings into the DB (for consistent settings handling).
AnalogJ Jul 23, 2022
7e672e8
adding tests for config.MergeConfigMap functionality. (Set vs SetDef…
AnalogJ Jul 23, 2022
94594db
on settings save, return the new settings.
AnalogJ Jul 23, 2022
7a68a68
frontend, determine the device status by checking against the configu…
AnalogJ Jul 23, 2022
e41ee47
filter attributes after notify
AnalogJ Jul 23, 2022
e8755ff
Merge pull request #338 from AnalogJ/app_db_settings
AnalogJ Jul 23, 2022
2e768fb
adding tests. Make sure that device status depends on the configured …
AnalogJ Jul 25, 2022
5cd441d
Add udev troubleshooting doc
MattKobayashi Jul 28, 2022
b11b873
Merge pull request #342 from MattKobayashi/docs_udev
AnalogJ Jul 29, 2022
ce2f990
consolidate device status to string logic in DeviceStatusPipe.
AnalogJ Jul 29, 2022
b238579
Merge pull request #343 from AnalogJ/app_db_settings
AnalogJ Jul 30, 2022
3f272b3
adding setting to allow users to customize between binary vs SI/Metri…
AnalogJ Jul 30, 2022
3205e3d
Update INSTALL_SYNOLOGY_COLLECTOR.md
KF5JWC Jul 31, 2022
83186ba
Merge pull request #345 from KF5JWC/patch-2
AnalogJ Jul 31, 2022
41c9daa
Make `run_collect.sh` executable
KF5JWC Aug 1, 2022
fa8f86a
Add missing setup command
MattKobayashi Aug 3, 2022
9dafde8
Merge pull request #350 from MattKobayashi/docs_udev
AnalogJ Aug 3, 2022
f0275d2
Merge pull request #346 from KF5JWC/patch-3
AnalogJ Aug 3, 2022
a1b0108
Added PRAGMA settings support when connecting to SQLITE db.
AnalogJ Aug 3, 2022
d201f79
Merge pull request #351 from AnalogJ/app_db_settings
AnalogJ Aug 3, 2022
2d6f60a
attrHistory needs to be reversed, so the newest data is on the right
AnalogJ Aug 4, 2022
9a4a8de
make sure the settings dialog width is 600px for readability.
AnalogJ Aug 4, 2022
d41d535
make sure that the device host id is provided in notifications (if av…
AnalogJ Aug 4, 2022
f823127
simplify logger creation (move logic into a function in `main` packages)
AnalogJ Aug 4, 2022
51f59e4
docs, added an explanation for why influxdb is required.
AnalogJ Aug 4, 2022
59e2e92
remove the notify.level and notify.filter_attributes values from the …
AnalogJ Aug 4, 2022
15d3206
remove settings dialog from Details page.
AnalogJ Aug 4, 2022
26b2215
fix tests.
AnalogJ Aug 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,9 @@ scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
| linux-arm-6 | :white_check_mark: | |
| linux-arm-7 | :white_check_mark: | web/collector only. see [#236](https://github.com/AnalogJ/scrutiny/issues/236) |
| linux-arm64 | :white_check_mark: | :white_check_mark: |
| freebsd-amd64 | collector only. see [#238](https://github.com/AnalogJ/scrutiny/issues/238) | |
| macos-amd64 | | :white_check_mark: |
| macos-arm64 | | :white_check_mark: |
| freebsd-amd64 | :white_check_mark: | |
| macos-amd64 | :white_check_mark: | :white_check_mark: |
| macos-arm64 | :white_check_mark: | :white_check_mark: |
| windows-amd64 | :white_check_mark: | WIP, see [#15](https://github.com/AnalogJ/scrutiny/issues/15) |
| windows-arm64 | :white_check_mark: | |

Expand Down
48 changes: 31 additions & 17 deletions collector/cmd/collector-metrics/collector-metrics.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/json"
"fmt"
"github.com/analogj/scrutiny/collector/pkg/collector"
"github.com/analogj/scrutiny/collector/pkg/config"
Expand Down Expand Up @@ -120,26 +121,16 @@ OPTIONS:
config.Set("api.endpoint", apiEndpoint)
}

collectorLogger := logrus.WithFields(logrus.Fields{
"type": "metrics",
})

if level, err := logrus.ParseLevel(config.GetString("log.level")); err == nil {
logrus.SetLevel(level)
} else {
logrus.SetLevel(logrus.InfoLevel)
}

if config.IsSet("log.file") && len(config.GetString("log.file")) > 0 {
logFile, err := os.OpenFile(config.GetString("log.file"), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
logrus.Errorf("Failed to open log file %s for output: %s", config.GetString("log.file"), err)
return err
}
collectorLogger, logFile, err := CreateLogger(config)
if logFile != nil {
defer logFile.Close()
logrus.SetOutput(io.MultiWriter(os.Stderr, logFile))
}
if err != nil {
return err
}

settingsData, err := json.MarshalIndent(config.AllSettings(), "", "\t")
collectorLogger.Debug(string(settingsData), err)
metricCollector, err := collector.CreateMetricsCollector(
config,
collectorLogger,
Expand Down Expand Up @@ -192,5 +183,28 @@ OPTIONS:
if err != nil {
log.Fatal(color.HiRedString("ERROR: %v", err))
}
}

func CreateLogger(appConfig config.Interface) (*logrus.Entry, *os.File, error) {
logger := logrus.WithFields(logrus.Fields{
"type": "metrics",
})

if level, err := logrus.ParseLevel(appConfig.GetString("log.level")); err == nil {
logger.Logger.SetLevel(level)
} else {
logger.Logger.SetLevel(logrus.InfoLevel)
}

var logFile *os.File
var err error
if appConfig.IsSet("log.file") && len(appConfig.GetString("log.file")) > 0 {
logFile, err = os.OpenFile(appConfig.GetString("log.file"), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
logger.Logger.Errorf("Failed to open log file %s for output: %s", appConfig.GetString("log.file"), err)
return nil, logFile, err
}
logger.Logger.SetOutput(io.MultiWriter(os.Stderr, logFile))
}
return logger, logFile, nil
}
2 changes: 1 addition & 1 deletion docker/Dockerfile.collector
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ RUN make binary-clean binary-collector

########
FROM debian:bullseye-slim as runtime
WORKDIR /scrutiny
WORKDIR /opt/scrutiny
ENV PATH="/opt/scrutiny/bin:${PATH}"

RUN apt-get update && apt-get install -y cron smartmontools ca-certificates tzdata && update-ca-certificates
Expand Down
File renamed without changes.
8 changes: 6 additions & 2 deletions docs/INSTALL_SYNOLOGY_COLLECTOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,13 @@ wget https://raw.githubusercontent.com/smartmontools/smartmontools/master/smartm
```
#!/bin/bash

/volume1/\@Entware/scrutiny/bin/scrutiny-collector-metrics-linux-arm64 run --config /volume1/\@Entware/scrutiny/config/collector.yaml
/volume1/\@Entware/scrutiny/bin/scrutiny-collector-metrics-linux-arm64 run --config /volume1/\@Entware/scrutiny/conf/collector.yaml
```

**Make `run_collect.sh` executable**

`chmod +x /volume1/\@Entware/scrutiny/bin/run_collect.sh`

## Set up Synology to run a scheduled task.

Log in to DSM and do the following:
Expand Down Expand Up @@ -131,4 +135,4 @@ Frequency: <Your desired frequency>

## Troubleshooting

If you have any issues with your devices being detected, or incorrect data, please take a look at [TROUBLESHOOTING_DEVICE_COLLECTOR.md](./TROUBLESHOOTING_DEVICE_COLLECTOR.md)
If you have any issues with your devices being detected, or incorrect data, please take a look at [TROUBLESHOOTING_DEVICE_COLLECTOR.md](./TROUBLESHOOTING_DEVICE_COLLECTOR.md)
16 changes: 14 additions & 2 deletions docs/TROUBLESHOOTING_INFLUXDB.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
# InfluxDB Troubleshooting

## Installation
InfluxDB is a required dependency for Scrutiny v0.4.0+.
## Why??

Scrutiny has many features, but the relevant one to this conversation is the "S.M.A.R.T metric tracking for historical
trends". Basically Scrutiny not only shows you the current SMART values, but how they've changed over weeks, months (or
even years).

To efficiently handle that data at scale (and to make my life easier as a developer) I decided to add InfluxDB as a
dependency. It's a dedicated timeseries database, as opposed to the general purpose sqlite DB I used before. I also did
a bunch of testing and analysis before I made the change. With InfluxDB the memory footprint for Scrutiny (at idle) is ~
100mb, which is still fairly reasonable.

## Installation

InfluxDB is a required dependency for Scrutiny v0.4.0+.

https://docs.influxdata.com/influxdb/v2.2/install/

Expand Down
1 change: 1 addition & 0 deletions docs/TROUBLESHOOTING_NOTIFICATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ SCRUTINY_DEVICE_NAME - eg. /dev/sda
SCRUTINY_DEVICE_TYPE - ATA/SCSI/NVMe
SCRUTINY_DEVICE_SERIAL - eg. WDDJ324KSO
SCRUTINY_MESSAGE - eg. "Scrutiny SMART error notification for device: %s\nFailure Type: %s\nDevice Name: %s\nDevice Serial: %s\nDevice Type: %s\nDate: %s"
SCRUTINY_HOST_ID - (optional) eg. "my-custom-host-id"
```

18 changes: 18 additions & 0 deletions docs/TROUBLESHOOTING_UDEV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Operating systems without udev

Some operating systems do not come with `udev` out of the box, for example Alpine Linux. In these instances you will not be able to bind `/run/udev` to the container for sharing device metadata. Some operating systems offer `udev` as a package that can be installed separately, or an alternative (such as `eudev` in the case of Alpine Linux) that provides the same functionality.

To install `eudev` in Alpine Linux (run as root):

```
apk add eudev
setup-udev
```

Once your `udev` implementation is installed, create `/run/udev` with the following command:

```
udevadm trigger
```

On Alpine Linux, this also has the benefit of creating symlinks to device serial numbers in `/dev/disk/by-id`.
122 changes: 74 additions & 48 deletions docs/dbdiagram.io.txt
Original file line number Diff line number Diff line change
@@ -1,62 +1,88 @@

// 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
Table device_temperature {
//timestamp
created_at timestamp

//tags (indexed & queryable)
device_wwn varchar [pk]

//fields
temp bigint
}

SettingKeyName string
SettingKeyDescription string
SettingDataType string

Table smart_ata_results {
//timestamp
created_at timestamp

//tags (indexed & queryable)
device_wwn varchar [pk]
smart_status varchar
scrutiny_status varchar
SettingValueNumeric int64
SettingValueString string
}


// 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
2 changes: 0 additions & 2 deletions example.scrutiny.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ log:
# - "join://shoutrrr:api-key@join/?devices=device1[,device2, ...][&icon=icon][&title=title]"
# - "script:///file/path/on/disk"
# - "https://www.example.com/path"
# filter_attributes: 'all' # options: 'all' or 'critical'
# level: 'fail' # options: 'fail', 'fail_scrutiny', 'fail_smart'

########################################################################################################################
# FEATURES COMING SOON
Expand Down
42 changes: 40 additions & 2 deletions webapp/backend/cmd/scrutiny/scrutiny.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package main

import (
"encoding/json"
"fmt"
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
"github.com/analogj/scrutiny/webapp/backend/pkg/errors"
"github.com/analogj/scrutiny/webapp/backend/pkg/version"
"github.com/analogj/scrutiny/webapp/backend/pkg/web"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
"io"
"log"
"os"
"time"

Expand Down Expand Up @@ -107,7 +110,18 @@ OPTIONS:
config.Set("log.file", c.String("log-file"))
}

webServer := web.AppEngine{Config: config}
webLogger, logFile, err := CreateLogger(config)
if logFile != nil {
defer logFile.Close()
}
if err != nil {
return err
}

settingsData, err := json.Marshal(config.AllSettings())
webLogger.Debug(string(settingsData), err)

webServer := web.AppEngine{Config: config, Logger: webLogger}

return webServer.Start()
},
Expand Down Expand Up @@ -140,3 +154,27 @@ OPTIONS:
}

}

func CreateLogger(appConfig config.Interface) (*logrus.Entry, *os.File, error) {
logger := logrus.WithFields(logrus.Fields{
"type": "web",
})
//set default log level
if level, err := logrus.ParseLevel(appConfig.GetString("log.level")); err == nil {
logger.Logger.SetLevel(level)
} else {
logger.Logger.SetLevel(logrus.InfoLevel)
}

var logFile *os.File
var err error
if appConfig.IsSet("log.file") && len(appConfig.GetString("log.file")) > 0 {
logFile, err = os.OpenFile(appConfig.GetString("log.file"), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
logger.Logger.Errorf("Failed to open log file %s for output: %s", appConfig.GetString("log.file"), err)
return nil, logFile, err
}
logger.Logger.SetOutput(io.MultiWriter(os.Stderr, logFile))
}
return logger, logFile, nil
}
Loading