Compare commits

...

25 Commits

Author SHA1 Message Date
packagrio-bot fc09df19f5 (v0.4.15) Automated packaging of release by Packagr 2022-07-07 05:23:20 +00:00
Jason Kulatunga 9ae9c387cc Merge pull request #315 from AnalogJ/beta 2022-07-06 22:20:07 -07:00
Jason Kulatunga 772b4f6528 fix influxdb install. 2022-07-06 21:39:33 -07:00
Jason Kulatunga 4a16ca0d5a wip, migrate all scripts to new build pattern (Makefile + multiple GH agents). 2022-07-06 21:39:33 -07:00
Jason Kulatunga 316ce856f7 cleanup, remove -race flag when testing (requires CGO) 2022-07-06 21:39:33 -07:00
Jason Kulatunga 6e0321f488 add go.sum 2022-07-06 21:39:33 -07:00
Jason Kulatunga 338d2ae04e remove invalid freebsd arch.
remove invalid freebsd arch.
2022-07-06 21:39:28 -07:00
Jason Kulatunga 4419f7f429 remove zig. remove cgo dependency for sqlite (using pkg.go.dev/modernc.org/sqlite) 2022-07-06 21:39:28 -07:00
Jason Kulatunga 797a6b0429 make sure we dont depend on tests for building binaries.
empty commit.

fix checkout.

fix checkout.

fix zig.

fix zig.

fix zig.

fix zig.

fix zig.

fix zig.

fix zig.

fix zig.

fix zig.

fix zig.
2022-07-06 21:39:22 -07:00
Jason Kulatunga d0b545dfb7 fixing make frontend in docker builds. 2022-06-26 15:34:53 -07:00
Jason Kulatunga b0bff53bbd start refactoring the Makefile to build artifacts in parallel (eventually using Zig for cross compilation). 2022-06-26 15:26:20 -07:00
Jason Kulatunga b4adf3d88d cleanup before go generate (and multi-arch builds using zig). 2022-06-25 19:15:36 -07:00
packagrio-bot eefdc548b2 (v0.4.14) Automated packaging of release by Packagr 2022-06-25 22:13:15 +00:00
Jason Kulatunga fb918e2d6e Merge pull request #308 from AnalogJ/beta
pre v0.4.14 release
2022-06-25 15:03:35 -07:00
Jason Kulatunga 3d9001a5e4 when deviceType not specified in collector config, scrutiny will ignore the device. We need to make sure we correctly override the device.
fixes #255
2022-06-25 11:19:44 -07:00
Jason Kulatunga fbe7d63a24 trying to fix tests. 2022-06-20 18:01:43 -07:00
Jason Kulatunga d718b0898b trying to fix tests. 2022-06-20 17:21:27 -07:00
Jason Kulatunga 44c7211b5f temp artifacts for #304 2022-06-20 13:32:53 -07:00
Jason Kulatunga 157c93b967 provide a mechanism to specify the absolute path to the smartctl binary used by metrics collector.
- fixes #304
2022-06-20 12:09:56 -07:00
Jason Kulatunga 7babc280a0 ensure that users can filter their notifications by:
- failing attribute type (Critical vs All)
 - failure reason (Smart, Scrutiny, Both)

 fixes #300
2022-06-20 08:15:06 -07:00
Jason Kulatunga e364e480e8 update Synology Guide. 2022-06-15 07:10:36 -07:00
Jason Kulatunga bfefe7e98a Merge pull request #303 from SiM22/patch-1 2022-06-15 07:05:29 -07:00
Simon Garcia 831cca7853 Create INSTALL_COLLECTOR_SYNOLOGY_AARCH64.md
A little tutorial to get the collector running on Synology
2022-06-15 09:56:32 +01:00
Jason Kulatunga 46f3b1c02c fix using linter. 2022-06-14 22:21:00 -07:00
Jason Kulatunga 8a1ae2ffa0 Update TROUBLESHOOTING_DEVICE_COLLECTOR.md 2022-06-14 21:41:22 -07:00
63 changed files with 5127 additions and 4583 deletions
-85
View File
@@ -1,85 +0,0 @@
name: CI
# This workflow is triggered on pushes & pull requests
on: [pull_request]
jobs:
build:
name: Build
runs-on: ubuntu-latest
container: techknowlogick/xgo:go-1.17.x
# Service containers to run with `build` (Required for end-to-end testing)
services:
influxdb:
image: influxdb:2.2
env:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: admin
DOCKER_INFLUXDB_INIT_PASSWORD: password12345
DOCKER_INFLUXDB_INIT_ORG: scrutiny
DOCKER_INFLUXDB_INIT_BUCKET: metrics
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: my-super-secret-auth-token
ports:
- 8086:8086
env:
PROJECT_PATH: /go/src/github.com/analogj/scrutiny
CGO_ENABLED: 1
steps:
- name: Git
run: |
apt-get update && apt-get install -y software-properties-common
add-apt-repository ppa:git-core/ppa && apt-get update && apt-get install -y git
git --version
- name: Checkout
uses: actions/checkout@v2
- name: Test
run: |
mkdir -p $(dirname "$PROJECT_PATH")
cp -a $GITHUB_WORKSPACE $PROJECT_PATH
cd $PROJECT_PATH
go mod vendor
go test -race -coverprofile=coverage.txt -covermode=atomic -v -tags "static" $(go list ./... | grep -v /vendor/)
- name: Generate coverage report
uses: codecov/codecov-action@v2
with:
files: ${{ env.PROJECT_PATH }}/coverage.txt
flags: unittests
fail_ci_if_error: true
verbose: true
- name: Build Binaries
run: |
cd $PROJECT_PATH
make all
- name: Archive
uses: actions/upload-artifact@v2
with:
name: binaries.zip
path: |
/build/scrutiny-web-linux-amd64
/build/scrutiny-collector-metrics-linux-amd64
/build/scrutiny-web-linux-arm64
/build/scrutiny-collector-metrics-linux-arm64
/build/scrutiny-web-linux-arm-5
/build/scrutiny-collector-metrics-linux-arm-5
/build/scrutiny-web-linux-arm-6
/build/scrutiny-collector-metrics-linux-arm-6
/build/scrutiny-web-linux-arm-7
/build/scrutiny-collector-metrics-linux-arm-7
/build/scrutiny-web-windows-4.0-amd64.exe
/build/scrutiny-collector-metrics-windows-4.0-amd64.exe
# /build/scrutiny-web-darwin-arm64
# /build/scrutiny-collector-metrics-darwin-arm64
# /build/scrutiny-web-darwin-amd64
# /build/scrutiny-collector-metrics-darwin-amd64
# /build/scrutiny-web-freebsd-amd64
# /build/scrutiny-collector-metrics-freebsd-amd64
- uses: codecov/codecov-action@v2
with:
file: ${{ env.PROJECT_PATH }}/coverage.txt
flags: unittests
fail_ci_if_error: false
+78
View File
@@ -0,0 +1,78 @@
name: CI
# This workflow is triggered on pushes & pull requests
on: [pull_request]
jobs:
test:
name: Test
runs-on: ubuntu-latest
container: ghcr.io/packagrio/packagr:latest-golang
# Service containers to run with `build` (Required for end-to-end testing)
services:
influxdb:
image: influxdb:2.2
env:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: admin
DOCKER_INFLUXDB_INIT_PASSWORD: password12345
DOCKER_INFLUXDB_INIT_ORG: scrutiny
DOCKER_INFLUXDB_INIT_BUCKET: metrics
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: my-super-secret-auth-token
ports:
- 8086:8086
env:
PROJECT_PATH: /go/src/github.com/analogj/scrutiny
STATIC: true
steps:
- name: Git
run: |
apt-get update && apt-get install -y software-properties-common
add-apt-repository ppa:git-core/ppa && apt-get update && apt-get install -y git
git --version
- name: Checkout
uses: actions/checkout@v2
- name: Test
run: |
make binary-clean binary-test-coverage
- name: Generate coverage report
uses: codecov/codecov-action@v2
with:
files: ${{ github.workspace }}/coverage.txt
flags: unittests
fail_ci_if_error: true
verbose: true
build:
name: Build ${{ matrix.cfg.goos }}/${{ matrix.cfg.goarch }}
runs-on: ${{ matrix.cfg.on }}
env:
GOOS: ${{ matrix.cfg.goos }}
GOARCH: ${{ matrix.cfg.goarch }}
GOARM: ${{ matrix.cfg.goarm }}
STATIC: true
strategy:
matrix:
cfg:
- { on: ubuntu-latest, goos: linux, goarch: amd64 }
- { on: ubuntu-latest, goos: linux, goarch: arm, goarm: 5 }
- { on: ubuntu-latest, goos: linux, goarch: arm, goarm: 6 }
- { on: ubuntu-latest, goos: linux, goarch: arm, goarm: 7 }
- { on: ubuntu-latest, goos: linux, goarch: arm64 }
- { on: macos-latest, goos: darwin, goarch: amd64 }
- { on: macos-latest, goos: darwin, goarch: arm64 }
- { on: macos-latest, goos: freebsd, goarch: amd64 }
- { on: windows-latest, goos: windows, goarch: amd64 }
- { on: windows-latest, goos: windows, goarch: arm64 }
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build Binaries
run: |
make binary-clean binary-all
- name: Archive
uses: actions/upload-artifact@v2
with:
name: binaries.zip
path: |
scrutiny-web-*
scrutiny-collector-metrics-*
+2 -2
View File
@@ -81,7 +81,7 @@ jobs:
options: -v ${{ github.workspace }}:/work
run: |
cd /work
make frontend && echo "print contents of /work/dist" && ls -alt /work/dist
make binary-frontend && echo "print contents of /work/dist" && ls -alt /work/dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
@@ -141,7 +141,7 @@ jobs:
options: -v ${{ github.workspace }}:/work
run: |
cd /work
make frontend && echo "print contents of /work/dist" && ls -alt /work/dist
make binary-frontend && echo "print contents of /work/dist" && ls -alt /work/dist
- name: Set up QEMU
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
options: -v ${{ github.workspace }}:/work
run: |
cd /work
make frontend && echo "print contents of /work/dist" && ls -alt /work/dist
make binary-frontend && echo "print contents of /work/dist" && ls -alt /work/dist
- name: Set up QEMU
-83
View File
@@ -1,83 +0,0 @@
# compiles FreeBSD artifacts and attaches them to build
name: Release FreeBSD
on:
release:
# Only use the types keyword to narrow down the activity types that will trigger your workflow.
types: [published]
workflow_dispatch:
inputs:
tag_name:
description: 'tag to build artifacts for'
required: true
default: 'v0.0.0'
jobs:
release-freebsd:
name: Release FreeBSD
runs-on: macos-10.15
env:
PROJECT_PATH: /go/src/github.com/analogj/scrutiny
GOPATH: /go
GOOS: freebsd
GOARCH: amd64
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{github.event.release.tag_name || github.event.inputs.tag_name }}
- name: Build Binaries
uses: vmactions/freebsd-vm@v0.1.5
with:
envs: 'PROJECT_PATH GOPATH GOOS GOARCH'
usesh: true
#TODO: lock go version using https://www.jeremymorgan.com/tutorials/golang/how-to-install-go-freebsd/
prepare: pkg install -y curl go gmake
run: |
pwd
ls -lah
whoami
freebsd-version
mkdir -p $(dirname "$PROJECT_PATH")
cp -R $GITHUB_WORKSPACE $PROJECT_PATH
cd $PROJECT_PATH
mkdir -p $GITHUB_WORKSPACE/dist
echo "building web binary (OS = ${GOOS}, ARCH = ${GOARCH})"
go build -ldflags "-extldflags=-static -X main.goos=${GOOS} -X main.goarch=${GOARCH}" -o $GITHUB_WORKSPACE/dist/scrutiny-web-${GOOS}-${GOARCH} -tags "static netgo sqlite_omit_load_extension" webapp/backend/cmd/scrutiny/scrutiny.go
chmod +x "$GITHUB_WORKSPACE/dist/scrutiny-web-${GOOS}-${GOARCH}"
file "$GITHUB_WORKSPACE/dist/scrutiny-web-${GOOS}-${GOARCH}" || true
ldd "$GITHUB_WORKSPACE/dist/scrutiny-web-${GOOS}-${GOARCH}" || true
echo "building collector binary (OS = ${GOOS}, ARCH = ${GOARCH})"
go build -ldflags "-extldflags=-static -X main.goos=${GOOS} -X main.goarch=${GOARCH}" -o $GITHUB_WORKSPACE/dist/scrutiny-collector-metrics-${GOOS}-${GOARCH} -tags "static netgo" collector/cmd/collector-metrics/collector-metrics.go
chmod +x "$GITHUB_WORKSPACE/dist/scrutiny-collector-metrics-${GOOS}-${GOARCH}"
file "$GITHUB_WORKSPACE/dist/scrutiny-collector-metrics-${GOOS}-${GOARCH}" || true
ldd "$GITHUB_WORKSPACE/dist/scrutiny-collector-metrics-${GOOS}-${GOARCH}" || true
- name: Release Asset - Collector - freebsd-amd64
id: upload-release-asset2
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.SCRUTINY_GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: './dist/scrutiny-collector-metrics-freebsd-amd64'
asset_name: scrutiny-collector-metrics-freebsd-amd64
asset_content_type: application/octet-stream
- name: Release Asset - Web - freebsd-amd64
id: upload-release-asset1
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.SCRUTINY_GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: './dist/scrutiny-web-freebsd-amd64'
asset_name: scrutiny-web-freebsd-amd64
asset_content_type: application/octet-stream
+2 -6
View File
@@ -19,11 +19,7 @@ jobs:
run: "cd webapp/frontend && ./git.version.sh"
- name: Build Frontend
run: |
cd webapp/frontend
npm install -g @angular/cli@9.1.4
npm install
mkdir -p dist
npm run build:prod -- --output-path=dist
make binary-frontend
tar -czf scrutiny-web-frontend.tar.gz dist
- name: Upload Frontend Asset
id: upload-release-asset3
@@ -32,6 +28,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.SCRUTINY_GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: './webapp/frontend/scrutiny-web-frontend.tar.gz'
asset_path: './scrutiny-web-frontend.tar.gz'
asset_name: scrutiny-web-frontend.tar.gz
asset_content_type: application/gzip
+88 -35
View File
@@ -13,10 +13,10 @@ on:
default: 'webapp/backend/pkg/version/version.go'
jobs:
build:
name: Build
release:
name: Create Release Commit
runs-on: ubuntu-latest
container: techknowlogick/xgo:go-1.17.x
container: ghcr.io/packagrio/packagr:latest-golang
# Service containers to run with `build` (Required for end-to-end testing)
services:
influxdb:
@@ -31,8 +31,7 @@ jobs:
ports:
- 8086:8086
env:
PROJECT_PATH: /go/src/github.com/analogj/scrutiny
CGO_ENABLED: 1
STATIC: true
steps:
- name: Git
run: |
@@ -53,34 +52,80 @@ jobs:
GITHUB_TOKEN: ${{ secrets.SCRUTINY_GITHUB_TOKEN }} # Leave this line unchanged
- name: Test
run: |
mkdir -p $(dirname "$PROJECT_PATH")
cp -a $GITHUB_WORKSPACE $PROJECT_PATH
cd $PROJECT_PATH
go mod vendor
go test -v -tags "static" $(go list ./... | grep -v /vendor/)
- name: Build Binaries
run: |
cd $PROJECT_PATH
make all
# restore modified dir to GH workspace.
cp -arf $PROJECT_PATH/. $GITHUB_WORKSPACE/
# copy all the build artifacts to the GH workspace
cp -arf /build/. $GITHUB_WORKSPACE/
- name: Commit Changes
make binary-clean binary-test-coverage
- name: Commit Changes Locally
id: commit
uses: packagrio/action-releasr-go@master
env:
# This is necessary in order to push a commit to the repo
GITHUB_TOKEN: ${{ secrets.SCRUTINY_GITHUB_TOKEN }} # Leave this line unchanged
with:
version_metadata_path: ${{ github.event.inputs.version_metadata_path }}
- name: Publish Release
- name: Upload workspace
uses: actions/upload-artifact@v3
with:
name: workspace
path: ${{ github.workspace }}/**/*
retention-days: 1
build:
name: Build ${{ matrix.cfg.goos }}/${{ matrix.cfg.goarch }}${{ matrix.cfg.goarm }}
needs: release
runs-on: ${{ matrix.cfg.on }}
env:
GOOS: ${{ matrix.cfg.goos }}
GOARCH: ${{ matrix.cfg.goarch }}
GOARM: ${{ matrix.cfg.goarm }}
STATIC: true
strategy:
matrix:
cfg:
- { on: ubuntu-latest, goos: linux, goarch: amd64 }
- { on: ubuntu-latest, goos: linux, goarch: arm, goarm: 5 }
- { on: ubuntu-latest, goos: linux, goarch: arm, goarm: 6 }
- { on: ubuntu-latest, goos: linux, goarch: arm, goarm: 7 }
- { on: ubuntu-latest, goos: linux, goarch: arm64 }
- { on: macos-latest, goos: darwin, goarch: amd64 }
- { on: macos-latest, goos: darwin, goarch: arm64 }
- { on: macos-latest, goos: freebsd, goarch: amd64 }
- { on: windows-latest, goos: windows, goarch: amd64 }
- { on: windows-latest, goos: windows, goarch: arm64 }
steps:
- name: Download workspace
uses: actions/download-artifact@v3
with:
name: workspace
- uses: actions/setup-go@v3
with:
go-version: '1.18.3' # The Go version to download (if necessary) and use.
- name: Build Binaries
run: |
make binary-clean binary-all
- name: Archive
uses: actions/upload-artifact@v2
with:
name: binaries.zip
path: |
scrutiny-web-*
scrutiny-collector-metrics-*
release-publish:
name: Publish Release
needs: build
runs-on: ubuntu-latest
steps:
- name: Download workspace
uses: actions/download-artifact@v3
with:
name: workspace
- name: Download binaries
uses: actions/download-artifact@v3
with:
name: binaries.zip
- name: List
shell: bash
run: |
ls -alt
- name: Publish Release & Assets
id: publish
uses: packagrio/action-publishr-go@master
env:
@@ -89,15 +134,23 @@ jobs:
with:
version_metadata_path: ${{ github.event.inputs.version_metadata_path }}
upload_assets:
scrutiny-web-linux-amd64
scrutiny-collector-metrics-darwin-amd64
scrutiny-collector-metrics-darwin-arm64
scrutiny-collector-metrics-freebsd-amd64
scrutiny-collector-metrics-linux-amd64
scrutiny-web-linux-arm64
scrutiny-collector-metrics-linux-arm64
scrutiny-web-linux-arm-5
scrutiny-collector-metrics-linux-arm-5
scrutiny-web-linux-arm-6
scrutiny-collector-metrics-linux-arm-6
scrutiny-web-linux-arm-7
scrutiny-collector-metrics-linux-arm-7
scrutiny-web-windows-4.0-amd64.exe
scrutiny-collector-metrics-windows-4.0-amd64.exe
scrutiny-collector-metrics-linux-arm64
scrutiny-collector-metrics-windows-amd64.exe
scrutiny-collector-metrics-windows-arm64.exe
scrutiny-web-darwin-amd64
scrutiny-web-darwin-arm64
scrutiny-web-freebsd-amd64
scrutiny-web-linux-amd64
scrutiny-web-linux-arm-5
scrutiny-web-linux-arm-6
scrutiny-web-linux-arm-7
scrutiny-web-linux-arm64
scrutiny-web-windows-amd64.exe
scrutiny-web-windows-arm64.exe
@@ -1,19 +0,0 @@
name: Cleanup Artifacts
on:
schedule:
# Every day at 1am
- cron: '0 1 * * *'
jobs:
remove-old-artifacts:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Remove old artifacts
uses: c-hive/gha-remove-artifacts@v1
with:
age: '1 day'
skip-tags: true
skip-recent: 5
+103 -47
View File
@@ -1,66 +1,122 @@
export CGO_ENABLED = 1
.ONESHELL: # Applies to every targets in the file! .ONESHELL instructs make to invoke a single instance of the shell and provide it with the entire recipe, regardless of how many lines it contains.
########################################################################################################################
# Global Env Settings
########################################################################################################################
GO_WORKSPACE ?= /go/src/github.com/analogj/scrutiny
BINARY=\
linux/amd64 \
linux/arm-5 \
linux/arm-6 \
linux/arm-7 \
linux/arm64 \
COLLECTOR_BINARY_NAME = scrutiny-collector-metrics
WEB_BINARY_NAME = scrutiny-web
LD_FLAGS =
.ONESHELL: # Applies to every targets in the file! .ONESHELL instructs make to invoke a single instance of the shell and provide it with the entire recipe, regardless of how many lines it contains.
.PHONY: all $(BINARY)
all: $(BINARY) windows/amd64
STATIC_TAGS =
# enable multiarch docker image builds
DOCKER_TARGETARCH_BUILD_ARG =
ifdef TARGETARCH
DOCKER_TARGETARCH_BUILD_ARG := $(DOCKER_TARGETARCH_BUILD_ARG) --build-arg TARGETARCH=$(TARGETARCH)
endif
$(BINARY): OS = $(word 1,$(subst /, ,$*))
$(BINARY): ARCH = $(word 2,$(subst /, ,$*))
$(BINARY): build/scrutiny-web-%:
@echo "building web binary (OS = $(OS), ARCH = $(ARCH))"
xgo -v --targets="$(OS)/$(ARCH)" -ldflags "-extldflags=-static -X main.goos=$(OS) -X main.goarch=$(ARCH)" -out scrutiny-web -tags "static netgo sqlite_omit_load_extension" ${GO_WORKSPACE}/webapp/backend/cmd/scrutiny/
# enable to build static binaries.
ifdef STATIC
export CGO_ENABLED = 0
LD_FLAGS := $(LD_FLAGS) -extldflags=-static
STATIC_TAGS := $(STATIC_TAGS) -tags "static netgo"
endif
ifdef GOOS
COLLECTOR_BINARY_NAME := $(COLLECTOR_BINARY_NAME)-$(GOOS)
WEB_BINARY_NAME := $(WEB_BINARY_NAME)-$(GOOS)
LD_FLAGS := $(LD_FLAGS) -X main.goos=$(GOOS)
endif
ifdef GOARCH
COLLECTOR_BINARY_NAME := $(COLLECTOR_BINARY_NAME)-$(GOARCH)
WEB_BINARY_NAME := $(WEB_BINARY_NAME)-$(GOARCH)
LD_FLAGS := $(LD_FLAGS) -X main.goarch=$(GOARCH)
endif
ifdef GOARM
COLLECTOR_BINARY_NAME := $(COLLECTOR_BINARY_NAME)-$(GOARM)
WEB_BINARY_NAME := $(WEB_BINARY_NAME)-$(GOARM)
endif
ifeq ($(OS),Windows_NT)
COLLECTOR_BINARY_NAME := $(COLLECTOR_BINARY_NAME).exe
WEB_BINARY_NAME := $(WEB_BINARY_NAME).exe
endif
chmod +x "/build/scrutiny-web-$(OS)-$(ARCH)"
file "/build/scrutiny-web-$(OS)-$(ARCH)" || true
ldd "/build/scrutiny-web-$(OS)-$(ARCH)" || true
########################################################################################################################
# Binary
########################################################################################################################
.PHONY: all
all: binary-all
@echo "building collector binary (OS = $(OS), ARCH = $(ARCH))"
xgo -v --targets="$(OS)/$(ARCH)" -ldflags "-extldflags=-static -X main.goos=$(OS) -X main.goarch=$(ARCH)" -out scrutiny-collector-metrics -tags "static netgo" ${GO_WORKSPACE}/collector/cmd/collector-metrics/
chmod +x "/build/scrutiny-collector-metrics-$(OS)-$(ARCH)"
file "/build/scrutiny-collector-metrics-$(OS)-$(ARCH)" || true
ldd "/build/scrutiny-collector-metrics-$(OS)-$(ARCH)" || true
windows/amd64: export OS = windows
windows/amd64: export ARCH = amd64
windows/amd64:
@echo "building web binary (OS = $(OS), ARCH = $(ARCH))"
xgo -v --targets="$(OS)/$(ARCH)" -ldflags "-extldflags=-static -X main.goos=$(OS) -X main.goarch=$(ARCH)" -out scrutiny-web -tags "static netgo sqlite_omit_load_extension" ${GO_WORKSPACE}/webapp/backend/cmd/scrutiny/
@echo "building collector binary (OS = $(OS), ARCH = $(ARCH))"
xgo -v --targets="$(OS)/$(ARCH)" -ldflags "-extldflags=-static -X main.goos=$(OS) -X main.goarch=$(ARCH)" -out scrutiny-collector-metrics -tags "static netgo" ${GO_WORKSPACE}/collector/cmd/collector-metrics/
.PHONY: binary-all
binary-all: binary-collector binary-web
@echo "built binary-collector and binary-web targets"
docker-collector:
@echo "building collector docker image"
docker build --build-arg TARGETARCH=amd64 -f docker/Dockerfile.collector -t analogj/scrutiny-dev:collector .
.PHONY: binary-clean
binary-clean:
go clean
docker-web:
@echo "building web docker image"
docker build --build-arg TARGETARCH=amd64 -f docker/Dockerfile.web -t analogj/scrutiny-dev:web .
.PHONY: binary-dep
binary-dep:
go mod vendor
docker-omnibus:
@echo "building omnibus docker image"
docker build --build-arg TARGETARCH=amd64 -f docker/Dockerfile -t analogj/scrutiny-dev:omnibus .
.PHONY: binary-test
binary-test: binary-dep
go test -v $(STATIC_TAGS) ./...
.PHONY: binary-test-coverage
binary-test-coverage: binary-dep
go test -coverprofile=coverage.txt -covermode=atomic -v $(STATIC_TAGS) ./...
.PHONY: binary-collector
binary-collector: binary-dep
go build -ldflags "$(LD_FLAGS)" -o $(COLLECTOR_BINARY_NAME) $(STATIC_TAGS) ./collector/cmd/collector-metrics/
ifneq ($(OS),Windows_NT)
chmod +x $(COLLECTOR_BINARY_NAME)
file $(COLLECTOR_BINARY_NAME) || true
ldd $(COLLECTOR_BINARY_NAME) || true
./$(COLLECTOR_BINARY_NAME) || true
endif
.PHONY: binary-web
binary-web: binary-dep
go build -ldflags "$(LD_FLAGS)" -o $(WEB_BINARY_NAME) $(STATIC_TAGS) ./webapp/backend/cmd/scrutiny/
ifneq ($(OS),Windows_NT)
chmod +x $(WEB_BINARY_NAME)
file $(WEB_BINARY_NAME) || true
ldd $(WEB_BINARY_NAME) || true
./$(WEB_BINARY_NAME) || true
endif
.PHONY: binary-frontend
# reduce logging, disable angular-cli analytics for ci environment
frontend: export NPM_CONFIG_LOGLEVEL = warn
frontend: export NG_CLI_ANALYTICS = false
frontend:
binary-frontend: export NPM_CONFIG_LOGLEVEL = warn
binary-frontend: export NG_CLI_ANALYTICS = false
binary-frontend:
cd webapp/frontend
npm install -g @angular/cli@9.1.4
mkdir -p $(CURDIR)/dist
npm ci
npm run build:prod -- --output-path=$(CURDIR)/dist
# clean:
# rm scrutiny-collector-metrics-* scrutiny-web-*
########################################################################################################################
# Docker
# NOTE: these docker make targets are only used for local development (not used by Github Actions/CI)
# NOTE: docker-web and docker-omnibus require `make binary-frontend` or frontend.tar.gz content in /dist before executing.
########################################################################################################################
.PHONY: docker-collector
docker-collector:
@echo "building collector docker image"
docker build $(DOCKER_TARGETARCH_BUILD_ARG) -f docker/Dockerfile.collector -t analogj/scrutiny-dev:collector .
.PHONY: docker-web
docker-web:
@echo "building web docker image"
docker build $(DOCKER_TARGETARCH_BUILD_ARG) -f docker/Dockerfile.web -t analogj/scrutiny-dev:web .
.PHONY: docker-omnibus
docker-omnibus:
@echo "building omnibus docker image"
docker build $(DOCKER_TARGETARCH_BUILD_ARG) -f docker/Dockerfile -t analogj/scrutiny-dev:omnibus .
+8 -8
View File
@@ -232,18 +232,18 @@ scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
# Supported Architectures
| Architecture Name | Binaries | Docker |
| --- | --- | --- |
| amd64 | :white_check_mark: | :white_check_mark: |
| arm-5 | :white_check_mark: | |
| arm-6 | :white_check_mark: | |
| arm-7 | :white_check_mark: | web/collector only. see [#236](https://github.com/AnalogJ/scrutiny/issues/236) |
| arm64 | :white_check_mark: | :white_check_mark: |
| freebsd | collector only. see [#238](https://github.com/AnalogJ/scrutiny/issues/238) | |
| linux-amd64 | :white_check_mark: | :white_check_mark: |
| linux-arm-5 | :white_check_mark: | |
| 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: |
| windows-amd64 | :white_check_mark: | |
| windows-amd64 | :white_check_mark: | WIP, see [#15](https://github.com/AnalogJ/scrutiny/issues/15) |
| windows-arm64 | :white_check_mark: | |
# Contributing
+3 -3
View File
@@ -98,10 +98,10 @@ func (mc *MetricsCollector) Run() error {
func (mc *MetricsCollector) Validate() error {
mc.logger.Infoln("Verifying required tools")
_, lookErr := exec.LookPath("smartctl")
_, lookErr := exec.LookPath(mc.config.GetString("commands.metrics_smartctl_bin"))
if lookErr != nil {
return errors.DependencyMissingError("smartctl is missing")
return errors.DependencyMissingError(fmt.Sprintf("%s binary is missing", mc.config.GetString("commands.metrics_smartctl_bin")))
}
return nil
@@ -124,7 +124,7 @@ func (mc *MetricsCollector) Collect(deviceWWN string, deviceName string, deviceT
}
args = append(args, fullDeviceName)
result, err := mc.shell.Command(mc.logger, "smartctl", args, "", os.Environ())
result, err := mc.shell.Command(mc.logger, mc.config.GetString("commands.metrics_smartctl_bin"), args, "", os.Environ())
resultBytes := []byte(result)
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
+1
View File
@@ -43,6 +43,7 @@ func (c *configuration) Init() error {
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")
+29 -4
View File
@@ -29,7 +29,7 @@ type Detect struct {
func (d *Detect) SmartctlScan() ([]models.Device, error) {
//we use smartctl to detect all the drives available.
args := strings.Split(d.Config.GetString("commands.metrics_scan_args"), " ")
detectedDeviceConnJson, err := d.Shell.Command(d.Logger, "smartctl", args, "", os.Environ())
detectedDeviceConnJson, err := d.Shell.Command(d.Logger, d.Config.GetString("commands.metrics_smartctl_bin"), args, "", os.Environ())
if err != nil {
d.Logger.Errorf("Error scanning for devices: %v", err)
return nil, err
@@ -60,7 +60,7 @@ func (d *Detect) SmartCtlInfo(device *models.Device) error {
}
args = append(args, fullDeviceName)
availableDeviceInfoJson, err := d.Shell.Command(d.Logger, "smartctl", args, "", os.Environ())
availableDeviceInfoJson, err := d.Shell.Command(d.Logger, d.Config.GetString("commands.metrics_smartctl_bin"), args, "", os.Environ())
if err != nil {
d.Logger.Errorf("Could not retrieve device information for %s: %v", device.DeviceName, err)
return err
@@ -149,10 +149,35 @@ func (d *Detect) TransformDetectedDevices(detectedDeviceConns models.Scan) []mod
//create a new device group, and replace the one generated by smartctl --scan
overrideDeviceGroup := []models.Device{}
for _, overrideDeviceType := range overrideDevice.DeviceType {
if overrideDevice.DeviceType != nil {
for _, overrideDeviceType := range overrideDevice.DeviceType {
overrideDeviceGroup = append(overrideDeviceGroup, models.Device{
HostId: d.Config.GetString("host.id"),
DeviceType: overrideDeviceType,
DeviceName: strings.TrimPrefix(overrideDeviceFile, DevicePrefix()),
})
}
} else {
//user may have specified device in config file without device type (default to scanned device type)
//check if the device file was detected by the scanner
var deviceType string
if scannedDevice, foundScannedDevice := groupedDevices[overrideDeviceFile]; foundScannedDevice {
if len(scannedDevice) > 0 {
//take the device type from the first grouped device
deviceType = scannedDevice[0].DeviceType
} else {
deviceType = "ata"
}
} else {
//fallback to ata if no scanned device detected
deviceType = "ata"
}
overrideDeviceGroup = append(overrideDeviceGroup, models.Device{
HostId: d.Config.GetString("host.id"),
DeviceType: overrideDeviceType,
DeviceType: deviceType,
DeviceName: strings.TrimPrefix(overrideDeviceFile, DevicePrefix()),
})
}
+63
View File
@@ -19,6 +19,7 @@ func TestDetect_SmartctlScan(t *testing.T) {
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{})
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeShell := mock_shell.NewMockInterface(mockCtrl)
@@ -47,6 +48,7 @@ func TestDetect_SmartctlScan_Megaraid(t *testing.T) {
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{})
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeShell := mock_shell.NewMockInterface(mockCtrl)
@@ -78,6 +80,7 @@ func TestDetect_SmartctlScan_Nvme(t *testing.T) {
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{})
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeShell := mock_shell.NewMockInterface(mockCtrl)
@@ -108,6 +111,7 @@ func TestDetect_TransformDetectedDevices_Empty(t *testing.T) {
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{})
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
detectedDevices := models.Scan{
@@ -140,6 +144,7 @@ func TestDetect_TransformDetectedDevices_Ignore(t *testing.T) {
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{{Device: "/dev/sda", DeviceType: nil, Ignore: true}})
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
detectedDevices := models.Scan{
@@ -170,6 +175,7 @@ func TestDetect_TransformDetectedDevices_Raid(t *testing.T) {
defer mockCtrl.Finish()
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{
{
@@ -210,6 +216,7 @@ func TestDetect_TransformDetectedDevices_Simple(t *testing.T) {
defer mockCtrl.Finish()
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{{Device: "/dev/sda", DeviceType: []string{"sat+megaraid"}}})
detectedDevices := models.Scan{
@@ -234,3 +241,59 @@ func TestDetect_TransformDetectedDevices_Simple(t *testing.T) {
require.Equal(t, 1, len(transformedDevices))
require.Equal(t, "sat+megaraid", transformedDevices[0].DeviceType)
}
// test https://github.com/AnalogJ/scrutiny/issues/255#issuecomment-1164024126
func TestDetect_TransformDetectedDevices_WithoutDeviceTypeOverride(t *testing.T) {
//setup
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{{Device: "/dev/sda"}})
detectedDevices := models.Scan{
Devices: []models.ScanDevice{
{
Name: "/dev/sda",
InfoName: "/dev/sda",
Protocol: "ata",
Type: "scsi",
},
},
}
d := detect.Detect{
Config: fakeConfig,
}
//test
transformedDevices := d.TransformDetectedDevices(detectedDevices)
//assert
require.Equal(t, 1, len(transformedDevices))
require.Equal(t, "scsi", transformedDevices[0].DeviceType)
}
func TestDetect_TransformDetectedDevices_WhenDeviceNotDetected(t *testing.T) {
//setup
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
fakeConfig := mock_config.NewMockInterface(mockCtrl)
fakeConfig.EXPECT().GetString("host.id").AnyTimes().Return("")
fakeConfig.EXPECT().GetString("commands.metrics_smartctl_bin").AnyTimes().Return("smartctl")
fakeConfig.EXPECT().GetString("commands.metrics_scan_args").AnyTimes().Return("--scan --json")
fakeConfig.EXPECT().GetDeviceOverrides().AnyTimes().Return([]models.ScanOverride{{Device: "/dev/sda"}})
detectedDevices := models.Scan{}
d := detect.Detect{
Config: fakeConfig,
}
//test
transformedDevices := d.TransformDetectedDevices(detectedDevices)
//assert
require.Equal(t, 1, len(transformedDevices))
require.Equal(t, "ata", transformedDevices[0].DeviceType)
}
+7 -7
View File
@@ -1,14 +1,16 @@
########################################################################################################################
# Omnibus Image
# NOTE: this image requires the `make binary-frontend` target to have been run before `docker build` The `dist` directory must exist.
########################################################################################################################
########
FROM golang:1.17-bullseye as backendbuild
WORKDIR /go/src/github.com/analogj/scrutiny
COPY . /go/src/github.com/analogj/scrutiny
RUN make binary-clean binary-all WEB_BINARY_NAME=scrutiny
RUN go mod vendor && \
go build -o scrutiny webapp/backend/cmd/scrutiny/scrutiny.go && \
go build -o scrutiny-collector-selftest collector/cmd/collector-selftest/collector-selftest.go && \
go build -o scrutiny-collector-metrics collector/cmd/collector-metrics/collector-metrics.go
########
FROM debian:bullseye-slim as runtime
@@ -34,11 +36,9 @@ COPY /rootfs /
COPY /rootfs/etc/cron.d/scrutiny /etc/cron.d/scrutiny
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny /opt/scrutiny/bin/
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny-collector-selftest /opt/scrutiny/bin/
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny-collector-metrics /opt/scrutiny/bin/
COPY dist /opt/scrutiny/web
RUN chmod +x /opt/scrutiny/bin/scrutiny && \
chmod +x /opt/scrutiny/bin/scrutiny-collector-selftest && \
chmod +x /opt/scrutiny/bin/scrutiny-collector-metrics && \
chmod 0644 /etc/cron.d/scrutiny && \
rm -f /etc/cron.daily/* && \
+7 -6
View File
@@ -1,3 +1,8 @@
########################################################################################################################
# Collector Image
########################################################################################################################
########
FROM golang:1.17-bullseye as backendbuild
@@ -5,9 +10,7 @@ WORKDIR /go/src/github.com/analogj/scrutiny
COPY . /go/src/github.com/analogj/scrutiny
RUN go mod vendor && \
go build -ldflags '-w -extldflags "-static"' -o scrutiny-collector-selftest collector/cmd/collector-selftest/collector-selftest.go && \
go build -ldflags '-w -extldflags "-static"' -o scrutiny-collector-metrics collector/cmd/collector-metrics/collector-metrics.go
RUN make binary-clean binary-collector
########
FROM debian:bullseye-slim as runtime
@@ -18,10 +21,8 @@ RUN apt-get update && apt-get install -y cron smartmontools ca-certificates tzda
COPY /docker/entrypoint-collector.sh /entrypoint-collector.sh
COPY /rootfs/etc/cron.d/scrutiny /etc/cron.d/scrutiny
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny-collector-selftest /opt/scrutiny/bin/
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny-collector-metrics /opt/scrutiny/bin/
RUN chmod +x /opt/scrutiny/bin/scrutiny-collector-selftest && \
chmod +x /opt/scrutiny/bin/scrutiny-collector-metrics && \
RUN chmod +x /opt/scrutiny/bin/scrutiny-collector-metrics && \
chmod +x /entrypoint-collector.sh && \
chmod 0644 /etc/cron.d/scrutiny && \
rm -f /etc/cron.daily/apt /etc/cron.daily/dpkg /etc/cron.daily/passwd
+8 -2
View File
@@ -1,3 +1,9 @@
########################################################################################################################
# Web Image
# NOTE: this image requires the `make binary-frontend` target to have been run before `docker build` The `dist` directory must exist.
########################################################################################################################
########
FROM golang:1.17-bullseye as backendbuild
@@ -5,8 +11,8 @@ WORKDIR /go/src/github.com/analogj/scrutiny
COPY . /go/src/github.com/analogj/scrutiny
RUN go mod vendor && \
go build -o scrutiny webapp/backend/cmd/scrutiny/scrutiny.go
RUN make binary-clean binary-all WEB_BINARY_NAME=scrutiny
########
FROM debian:bullseye-slim as runtime
-7
View File
@@ -1,7 +0,0 @@
FROM techknowlogick/xgo:go-1.17.x
WORKDIR /go/src/github.com/analogj/scrutiny
COPY . /go/src/github.com/analogj/scrutiny
RUN make all
-18
View File
@@ -1,18 +0,0 @@
# This vagrant file is only used for local development & testing.
Vagrant.configure("2") do |config|
config.vm.guest = :freebsd
config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true
config.vm.box = "freebsd/FreeBSD-11.0-CURRENT"
config.ssh.shell = "sh"
config.vm.base_mac = "080027D14C66"
config.vm.provider :virtualbox do |vb|
vb.customize ["modifyvm", :id, "--memory", "1024"]
vb.customize ["modifyvm", :id, "--cpus", "1"]
vb.customize ["modifyvm", :id, "--hwvirtex", "on"]
vb.customize ["modifyvm", :id, "--audio", "none"]
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
end
end
+134
View File
@@ -0,0 +1,134 @@
# Install collector on Synology
## Install Entware
This will allow you to install a newer version of smartmontools on your Synology. Follow the instructions here (This is tested on DSM7) - https://github.com/Entware/Entware/wiki/Install-on-Synology-NAS
**PLEASE NOTE THAT IF YOU UPDATE DSM FIRMWARE YOU MAY BORK THE EXISTING ENTWARE INSTALLATION, FOR ANYTHING THAT MAY RELATE TO ENTWARE PLEASE VISIT THEIR REPO**
## Collector Setup
**1. Run an update**
`sudo opkg update`
**2. Run an upgrade**
`sudo opkg upgrade`
**3. Install smartmontools**
`sudo opkg install smartmontools`
*It should install v7.2-2*
`Installing smartmontools (7.2-2) to root...`
**4. We will now create the directories.**
```
mkdir -p /volume1/\@Entware/scrutiny/bin
mkdir -p /volume1/\@Entware/scrutiny/conf
```
**5. change into the bin directory**
`cd /volume1/\@Entware/scrutiny/bin`
**6. Download the collector binary for your architecture and make it executable**
`wget https://github.com/AnalogJ/scrutiny/releases/download/v0.4.12/scrutiny-collector-metrics-linux-arm64`
`chmod +x /volume1/\@Entware/scrutiny/bin/scrutiny-collector-metrics-linux-arm64`
**7. Create a config file for the collector**
```
cd /volume1/\@Entware/scrutiny/conf
wget https://raw.githubusercontent.com/AnalogJ/scrutiny/master/example.collector.yaml
mv example.collector.yaml collector.yaml
```
**8. Lets make some changes in the [collector config file](../example.collector.yaml), these are what i uncommented/added, please tweak the device paths to your needs**
```
host:
id: 'Server_Name'
devices:
# # example for forcing device type detection for a single disk
- device: /dev/sda
type: 'sat'
- device: /dev/sdb
type: 'sat'
- device: /dev/sdc
type: 'sat'
- device: /dev/sdd
type: 'sat'
api:
endpoint: 'http://<url>:8080'
```
**9. Let's update the smartd db**
```
cd /volume1/\@Entware/scrutiny/bin/
wget https://raw.githubusercontent.com/smartmontools/smartmontools/master/smartmontools/drivedb.h
```
**10. I ran it like this but you can tweak to your liking, the most important part is the --drivedb, as this loads it into the aplication for future use**
`smartctl -d sat --all /dev/sda --drivedb=/volume1/\@Entware/scrutiny/bin/drivedb.h`
**11. Now lets create a small bash script, this will be used for the scheduled task inside Synology**
`vim /volume1/\@Entware/scrutiny/bin/run_collect.sh`
**The contents are below, copy and paste them in**
```
#!/bin/bash
/volume1/\@Entware/scrutiny/bin/scrutiny-collector-metrics-linux-arm64 run --config /volume1/\@Entware/scrutiny/config/collector.yaml
```
## Set up Synology to run a scheduled task.
Log in to DSM and do the following:
Goto: DSM > Control Panel > Task Scheduler
Create > Scheduled Task > User Defined Script
###### General
```
Task: Scrutiny_Collector
User: root
Enabled: yes
```
###### Schedule
```
Run on the following days: Daily
```
###### Time:
```
Frequency: <Your desired frequency>
```
###### Task Settings
**Run Command**
```
. /opt/etc/profile; /volume1/\@Entware/scrutiny/bin/run_collect.sh
```
## 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)
+2 -1
View File
@@ -7,10 +7,11 @@ Once a guide is created (in `docs/guides/`) it will be linked here.
- [x] [unraid](./INSTALL_UNRAID.md)
- [ ] ESXI
- [ ] Proxmox
- [ ] Synology
- [x] Synology(./INSTALL_SYNOLOGY_COLLECTOR.md)
- [ ] OMV
- [ ] Amahi
- [ ] Running in a LXC container
- [x] [PFSense](./INSTALL_UNRAID.md)
- [ ] QNAP
- [ ] RockStor
+20
View File
@@ -0,0 +1,20 @@
# Testers
Scrutiny supports many operating systems, CPU architectures and runtime environments. Unfortunately that makes it incredibly
difficult to test.
Thankfully the following users have been gracious enough to test/validate Scrutiny works on their system.
> NOTE: If you're interested in volunteering to test Scrutiny beta builds on your system, please [open an issue](https://github.com/AnalogJ/scrutiny/issues).
| Architecture Name | Binaries | Docker |
| --- | --- | --- |
| linux-amd64 | -- | @feroxy @rshxyz |
| linux-arm-5 | -- | |
| linux-arm-6 | -- | |
| linux-arm-7 | @Zorlin | @martini1992 |
| linux-arm64 | @SiM22 @Zorlin | @ViRb3 @agneevX @benamajin |
| freebsd-amd64 | @BadCo-NZ @varunsridharan @martadinata666 @KenwoodFox @FingerlessGlov3s | |
| macos-amd64 | -- | -- |
| macos-arm64 | -- | -- |
| windows-amd64 | @gabrielv33 | -- |
| windows-arm64 | -- | -- |
+2 -2
View File
@@ -205,7 +205,7 @@ apt update && apt install -y sqlite3
sqlite3 /opt/scrutiny/config/scrutiny.db
# reset/update the devices table, unset the failure status.
UPDATE devices SET device_status = null
UPDATE devices SET device_status = null;
# exit sqlite CLI
.exit
@@ -267,4 +267,4 @@ Or if you're not using docker, you can pass CLI arguments to the collector durin
```bash
scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
```
```
+1 -3
View File
@@ -73,6 +73,7 @@ devices:
# example to show how to override the smartctl command args globally
#commands:
# metrics_smartctl_bin: 'smartctl' # change to provide custom `smartctl` binary path, eg. `/usr/sbin/smartctl`
# metrics_scan_args: '--scan --json' # used to detect devices
# metrics_info_args: '--info --json' # used to determine device unique ID & register device with Scrutiny
# metrics_smart_args: '--xall --json' # used to retrieve smart data for each device.
@@ -86,9 +87,6 @@ devices:
########################################################################################################################
#collect:
# metric:
# enable: true
# command: '-a -o on -S on'
# long:
# enable: false
# command: ''
+2
View File
@@ -73,6 +73,8 @@ 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
+19 -14
View File
@@ -7,19 +7,18 @@ require (
github.com/containrrr/shoutrrr v0.4.4
github.com/fatih/color v1.10.0
github.com/gin-gonic/gin v1.6.3
github.com/glebarez/sqlite v1.4.5
github.com/go-gormigrate/gormigrate/v2 v2.0.0
github.com/golang/mock v1.4.3
github.com/influxdata/influxdb-client-go/v2 v2.9.0
github.com/jaypipes/ghw v0.6.1
github.com/jinzhu/gorm v1.9.16
github.com/mitchellh/mapstructure v1.2.2
github.com/sirupsen/logrus v1.4.2
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.5.1
github.com/stretchr/testify v1.7.1
github.com/urfave/cli/v2 v2.2.0
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
gorm.io/driver/sqlite v1.1.3
gorm.io/gorm v1.20.2
gorm.io/gorm v1.23.5
)
require (
@@ -31,34 +30,35 @@ require (
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.17.2 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/golang/protobuf v1.4.2 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/jaypipes/pcidb v0.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.1 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/klauspost/compress v1.12.1 // indirect
github.com/klauspost/compress v1.11.7 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 // indirect
github.com/kvz/logstreamer v0.0.0-20201023134116-02d20f4338f5 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-sqlite3 v1.14.4 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.1 // indirect
github.com/nxadm/tail v1.4.6 // indirect
github.com/onsi/ginkgo v1.14.2 // indirect
github.com/pelletier/go-toml v1.7.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spf13/afero v1.2.2 // indirect
@@ -69,7 +69,7 @@ require (
github.com/ugorji/go/codec v1.1.7 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.5 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
@@ -77,7 +77,12 @@ require (
gopkg.in/ini.v1 v1.55.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gosrc.io/xmpp v0.5.1 // indirect
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
nhooyr.io/websocket v1.8.7 // indirect
modernc.org/libc v1.16.8 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.1.1 // indirect
modernc.org/sqlite v1.17.2 // indirect
nhooyr.io/websocket v1.8.6 // indirect
)
+62 -37
View File
@@ -63,13 +63,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
@@ -84,6 +83,10 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/glebarez/go-sqlite v1.17.2 h1:gyTyFr2RFFQd2gp6fOOdfnTvUn99zwvVOrQFHA4S+DY=
github.com/glebarez/go-sqlite v1.17.2/go.mod h1:lakPjzvnJ6uSIARV+5dPALDuSLL3879PlzHFMEpbceM=
github.com/glebarez/sqlite v1.4.5 h1:oaJupO4X9iTn4sXRvP5Vs15BNvKh9dx5AQfciKlDvV4=
github.com/glebarez/sqlite v1.4.5/go.mod h1:6D+bB+DdXlEC4mO+pUFJWixVcnrHTIAJ9U6Ynnn4Lxk=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gormigrate/gormigrate/v2 v2.0.0 h1:e2A3Uznk4viUC4UuemuVgsNnvYZyOA8B3awlYk3UioU=
@@ -108,7 +111,6 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
@@ -139,15 +141,15 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -156,8 +158,8 @@ github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OI
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
@@ -254,13 +256,11 @@ github.com/jaypipes/ghw v0.6.1/go.mod h1:QOXppNRCLGYR1H+hu09FxZPqjNt09bqUZUnOL3R
github.com/jaypipes/pcidb v0.5.0 h1:4W5gZ+G7QxydevI8/MmmKdnIPJpURqJ2JNXTzfLxF5c=
github.com/jaypipes/pcidb v0.5.0/go.mod h1:L2RGk04sfRhp5wvHO0gfRAMoLY/F3PKv/nwJeVoho0o=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -271,12 +271,12 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.12.1 h1:/+xsCsk06wE38cyiqOR/o7U2fSftcH72xD+BQXmja/g=
github.com/klauspost/compress v1.12.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
@@ -288,15 +288,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 h1:3tLzEnUizyN9YLWFTT9loC30lSBvh2y70LTDcZOTs1s=
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0/go.mod h1:8/LTPeDLaklcUjgSQBHbhBF1ibKAFxzS5o+H7USfMSA=
github.com/kvz/logstreamer v0.0.0-20201023134116-02d20f4338f5 h1:dkCjlgGN81ahDFtM9R1x16gFGTa7ZvgZfdtAfM9lWOs=
github.com/kvz/logstreamer v0.0.0-20201023134116-02d20f4338f5/go.mod h1:8/LTPeDLaklcUjgSQBHbhBF1ibKAFxzS5o+H7USfMSA=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -320,12 +319,12 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.4 h1:4rQjbDxdu9fSgI/r3KN72G3c2goxknAqHHgPWWs8UlI=
github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -347,16 +346,14 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.6 h1:11TGpSHY7Esh/i/qnq02Jo5oVrI1Gue8Slbq0ujPZFQ=
github.com/nxadm/tail v1.4.6/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.1 h1:foqVmeWDD6yYpK+Yz3fHyNIxFYNxswxqNFjSKe+vI54=
github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
@@ -381,6 +378,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
@@ -432,8 +431,9 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -475,7 +475,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -572,11 +571,12 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 h1:D1v9ucDTYBtbz5vNuBbAhIMAGhQhJ6Ym5ah3maMVNX4=
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -616,7 +616,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -675,20 +675,20 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.0.1 h1:omJoilUzyrAp0xNoio88lGJCroGdIOen9hq2A/+3ifw=
gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw=
gorm.io/driver/postgres v1.0.0 h1:Yh4jyFQ0a7F+JPU0Gtiam/eKmpT/XFc1FKxotGqc6FM=
gorm.io/driver/postgres v1.0.0/go.mod h1:wtMFcOzmuA5QigNsgEIb7O5lhvH1tHAF1RbWmLWV4to=
gorm.io/driver/sqlite v1.1.1 h1:qtWqNAEUyi7gYSUAJXeiAMz0lUOdakZF5ia9Fqnp5G4=
gorm.io/driver/sqlite v1.1.1/go.mod h1:hm2olEcl8Tmsc6eZyxYSeznnsDaMqamBvEXLNtBg4cI=
gorm.io/driver/sqlite v1.1.3 h1:BYfdVuZB5He/u9dt4qDpZqiqDJ6KhPqs5QUqsr/Eeuc=
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
gorm.io/driver/sqlserver v1.0.2 h1:FzxAlw0/7hntMzSiNfotpYCo9Lz8dqWQGdmCGqIiFGo=
gorm.io/driver/sqlserver v1.0.2/go.mod h1:gb0Y9QePGgqjzrVyTQUZeh9zkd5v0iz71cM1B4ZycEY=
gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.0/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.2 h1:bZzSEnq7NDGsrd+n3evOOedDrY5oLM5QPlCjZJUK2ro=
gorm.io/gorm v1.20.2/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM=
gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gosrc.io/xmpp v0.5.1 h1:Rgrm5s2rt+npGggJH3HakQxQXR8ZZz3+QRzakRQqaq4=
gosrc.io/xmpp v0.5.1/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
@@ -699,11 +699,36 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.17.2 h1:TjmF36Wi5QcPYqRoAacV1cAyJ7xB/CD0ExpVUEMebnw=
modernc.org/sqlite v1.17.2/go.mod h1:GOQmuiXd6pTTes1Fi2s9apiCcD/wbKQtBZ0Nw6/etjM=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+3
View File
@@ -2,6 +2,7 @@ package config
import (
"github.com/analogj/go-util/utils"
"github.com/analogj/scrutiny/webapp/backend/pkg"
"github.com/analogj/scrutiny/webapp/backend/pkg/errors"
"github.com/spf13/viper"
"log"
@@ -38,6 +39,8 @@ func (c *configuration) Init() error {
c.SetDefault("log.file", "")
c.SetDefault("notify.urls", []string{})
c.SetDefault("notify.filter_attributes", pkg.NotifyFilterAttributesAll)
c.SetDefault("notify.level", pkg.NotifyLevelFail)
c.SetDefault("web.influxdb.scheme", "http")
c.SetDefault("web.influxdb.host", "localhost")
+9 -2
View File
@@ -4,8 +4,15 @@ const DeviceProtocolAta = "ATA"
const DeviceProtocolScsi = "SCSI"
const DeviceProtocolNvme = "NVMe"
type AttributeStatus uint8
const NotifyFilterAttributesAll = "all"
const NotifyFilterAttributesCritical = "critical"
const NotifyLevelFail = "fail"
const NotifyLevelFailScrutiny = "fail_scrutiny"
const NotifyLevelFailSmart = "fail_smart"
//go:generate stringer -type=AttributeStatus
type AttributeStatus uint8
const (
// AttributeStatusPassed binary, 1,2,4,8,16,32,etc
AttributeStatusPassed AttributeStatus = 0
@@ -22,8 +29,8 @@ func AttributeStatusClear(b, flag AttributeStatus) AttributeStatus { return b &
func AttributeStatusToggle(b, flag AttributeStatus) AttributeStatus { return b ^ flag }
func AttributeStatusHas(b, flag AttributeStatus) bool { return b&flag != 0 }
//go:generate stringer -type=DeviceStatus
type DeviceStatus uint8
const (
// DeviceStatusPassed binary, 1,2,4,8,16,32,etc
DeviceStatusPassed DeviceStatus = 0
@@ -6,11 +6,11 @@ import (
"fmt"
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
"github.com/glebarez/sqlite"
influxdb2 "github.com/influxdata/influxdb-client-go/v2"
"github.com/influxdata/influxdb-client-go/v2/api"
"github.com/influxdata/influxdb-client-go/v2/domain"
"github.com/sirupsen/logrus"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"io/ioutil"
"net/http"
@@ -10,9 +10,9 @@ import (
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/collector"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements"
_ "github.com/glebarez/sqlite"
"github.com/go-gormigrate/gormigrate/v2"
"github.com/influxdata/influxdb-client-go/v2/api/http"
_ "github.com/jinzhu/gorm/dialects/sqlite"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
"strconv"
+122 -13
View File
@@ -6,7 +6,11 @@ import (
"errors"
"fmt"
"github.com/analogj/go-util/utils"
"github.com/analogj/scrutiny/webapp/backend/pkg"
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements"
"github.com/analogj/scrutiny/webapp/backend/pkg/thresholds"
"github.com/containrrr/shoutrrr"
shoutrrrTypes "github.com/containrrr/shoutrrr/pkg/types"
"github.com/sirupsen/logrus"
@@ -14,28 +18,130 @@ import (
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
const NotifyFailureTypeEmailTest = "EmailTest"
const NotifyFailureTypeSmartPrefail = "SmartPreFailure"
const NotifyFailureTypeBothFailure = "SmartFailure" //SmartFailure always takes precedence when Scrutiny & Smart failed.
const NotifyFailureTypeSmartFailure = "SmartFailure"
const NotifyFailureTypeSmartErrorLog = "SmartErrorLog"
const NotifyFailureTypeSmartSelfTest = "SmartSelfTestLog"
const NotifyFailureTypeScrutinyFailure = "ScrutinyFailure"
// 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 {
// 1. check if the device is healthy
if device.DeviceStatus == pkg.DeviceStatusPassed {
return false
}
// setup constants for comparison
var requiredDeviceStatus pkg.DeviceStatus
var requiredAttrStatus pkg.AttributeStatus
if notifyLevel == pkg.NotifyLevelFail {
// either scrutiny or smart failures should trigger an email
requiredDeviceStatus = pkg.DeviceStatusSet(pkg.DeviceStatusFailedSmart, pkg.DeviceStatusFailedScrutiny)
requiredAttrStatus = pkg.AttributeStatusSet(pkg.AttributeStatusFailedSmart, pkg.AttributeStatusFailedScrutiny)
} else if notifyLevel == pkg.NotifyLevelFailSmart {
//only smart failures
requiredDeviceStatus = pkg.DeviceStatusFailedSmart
requiredAttrStatus = pkg.AttributeStatusFailedSmart
} else {
requiredDeviceStatus = pkg.DeviceStatusFailedScrutiny
requiredAttrStatus = pkg.AttributeStatusFailedScrutiny
}
// 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)
if notifyFilterAttributes == pkg.NotifyFilterAttributesCritical {
hasFailingCriticalAttr := false
var statusFailingCrtiticalAttr pkg.AttributeStatus
for attrId, attrData := range smartAttrs.Attributes {
//find failing attribute
if attrData.GetStatus() == pkg.AttributeStatusPassed {
continue //skip all passing attributes
}
// merge the status's of all critical attributes
statusFailingCrtiticalAttr = pkg.AttributeStatusSet(statusFailingCrtiticalAttr, attrData.GetStatus())
//found a failing attribute, see if its critical
if device.IsScsi() && thresholds.ScsiMetadata[attrId].Critical {
hasFailingCriticalAttr = true
} else if device.IsNvme() && thresholds.NmveMetadata[attrId].Critical {
hasFailingCriticalAttr = true
} else {
//this is ATA
attrIdInt, err := strconv.Atoi(attrId)
if err != nil {
continue
}
if thresholds.AtaMetadata[attrIdInt].Critical {
hasFailingCriticalAttr = true
}
}
}
if !hasFailingCriticalAttr {
//no critical attributes are failing, and notifyFilterAttributes == "critical"
return false
} else {
// check if any of the critical attributes have a status that we're looking for
return pkg.AttributeStatusHas(statusFailingCrtiticalAttr, requiredAttrStatus)
}
} else {
// 2. SKIP - we are processing every attribute.
// 3. check if the device failure level matches the wanted failure level.
return pkg.DeviceStatusHas(device.DeviceStatus, requiredDeviceStatus)
}
}
// TODO: include host and/or user label for device.
type Payload struct {
Date string `json:"date"` //populated by Send function.
FailureType string `json:"failure_type"` //EmailTest, SmartFail, ScrutinyFail
DeviceType string `json:"device_type"` //ATA/SCSI/NVMe
DeviceName string `json:"device_name"` //dev/sda
DeviceSerial string `json:"device_serial"` //WDDJ324KSO
Test bool `json:"test"` // false
//should not be populated
Subject string `json:"subject"`
Message string `json:"message"`
//private, populated during init (marked as Public for JSON serialization)
Date string `json:"date"` //populated by Send function.
FailureType string `json:"failure_type"` //EmailTest, BothFail, SmartFail, ScrutinyFail
Subject string `json:"subject"`
Message string `json:"message"`
}
func NewPayload(device models.Device, test bool) Payload {
payload := Payload{
DeviceType: device.DeviceType,
DeviceName: device.DeviceName,
DeviceSerial: device.SerialNumber,
Test: test,
}
//validate that the Payload is populated
sendDate := time.Now()
payload.Date = sendDate.Format(time.RFC3339)
payload.FailureType = payload.GenerateFailureType(device.DeviceStatus)
payload.Subject = payload.GenerateSubject()
payload.Message = payload.GenerateMessage()
return payload
}
func (p *Payload) GenerateFailureType(deviceStatus pkg.DeviceStatus) string {
//generate a failure type, given Test and DeviceStatus
if p.Test {
return NotifyFailureTypeEmailTest // must be an email test if "Test" is true
}
if pkg.DeviceStatusHas(deviceStatus, pkg.DeviceStatusFailedSmart) && pkg.DeviceStatusHas(deviceStatus, pkg.DeviceStatusFailedScrutiny) {
return NotifyFailureTypeBothFailure //both failed
} else if pkg.DeviceStatusHas(deviceStatus, pkg.DeviceStatusFailedSmart) {
return NotifyFailureTypeSmartFailure //only SMART failed
} else {
return NotifyFailureTypeScrutinyFailure //only Scrutiny failed
}
}
func (p *Payload) GenerateSubject() string {
@@ -61,6 +167,14 @@ Date: %s`, p.DeviceName, p.FailureType, p.DeviceName, p.DeviceSerial, p.DeviceTy
return message
}
func New(logger logrus.FieldLogger, appconfig config.Interface, device models.Device, test bool) Notify {
return Notify{
Logger: logger,
Config: appconfig,
Payload: NewPayload(device, test),
}
}
type Notify struct {
Logger logrus.FieldLogger
Config config.Interface
@@ -68,11 +182,6 @@ type Notify struct {
}
func (n *Notify) Send() error {
//validate that the Payload is populated
sendDate := time.Now()
n.Payload.Date = sendDate.Format(time.RFC3339)
n.Payload.Subject = n.Payload.GenerateSubject()
n.Payload.Message = n.Payload.GenerateMessage()
//retrieve list of notification endpoints from config file
configUrls := n.Config.GetStringSlice("notify.urls")
+161
View File
@@ -0,0 +1,161 @@
package notify
import (
"github.com/analogj/scrutiny/webapp/backend/pkg"
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements"
"github.com/stretchr/testify/require"
"testing"
)
func TestShouldNotify_MustSkipPassingDevices(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusPassed,
}
smartAttrs := measurements.Smart{}
notifyLevel := pkg.NotifyLevelFail
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
//assert
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyLevelFail_FailingSmartDevice(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{}
notifyLevel := pkg.NotifyLevelFail
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
//assert
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyLevelFailSmart_FailingSmartDevice(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{}
notifyLevel := pkg.NotifyLevelFailSmart
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
//assert
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyLevelFailScrutiny_FailingSmartDevice(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{}
notifyLevel := pkg.NotifyLevelFailScrutiny
notifyFilterAttributes := pkg.NotifyFilterAttributesAll
//assert
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyFilterAttributesCritical_WithCriticalAttrs(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{Attributes: map[string]measurements.SmartAttribute{
"5": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusFailedSmart,
},
}}
notifyLevel := pkg.NotifyLevelFail
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
//assert
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyFilterAttributesCritical_WithMultipleCriticalAttrs(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{Attributes: map[string]measurements.SmartAttribute{
"5": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusPassed,
},
"10": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusFailedScrutiny,
},
}}
notifyLevel := pkg.NotifyLevelFail
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
//assert
require.True(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyFilterAttributesCritical_WithNoCriticalAttrs(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{Attributes: map[string]measurements.SmartAttribute{
"1": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusFailedSmart,
},
}}
notifyLevel := pkg.NotifyLevelFail
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
//assert
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyFilterAttributesCritical_WithNoFailingCriticalAttrs(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{Attributes: map[string]measurements.SmartAttribute{
"5": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusPassed,
},
}}
notifyLevel := pkg.NotifyLevelFail
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
//assert
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
func TestShouldNotify_NotifyFilterAttributesCritical_NotifyLevelFailSmart_WithCriticalAttrsFailingScrutiny(t *testing.T) {
t.Parallel()
//setup
device := models.Device{
DeviceStatus: pkg.DeviceStatusFailedSmart,
}
smartAttrs := measurements.Smart{Attributes: map[string]measurements.SmartAttribute{
"5": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusPassed,
},
"10": &measurements.SmartAtaAttribute{
Status: pkg.AttributeStatusFailedScrutiny,
},
}}
notifyLevel := pkg.NotifyLevelFailSmart
notifyFilterAttributes := pkg.NotifyFilterAttributesCritical
//assert
require.False(t, ShouldNotify(device, smartAttrs, notifyLevel, notifyFilterAttributes))
}
+1 -1
View File
@@ -2,4 +2,4 @@ package version
// VERSION is the app-global version string, which will be replaced with a
// new value during packaging
const VERSION = "0.4.13"
const VERSION = "0.4.15"
@@ -15,17 +15,16 @@ func SendTestNotification(c *gin.Context) {
appConfig := c.MustGet("CONFIG").(config.Interface)
logger := c.MustGet("LOGGER").(logrus.FieldLogger)
testNotify := notify.Notify{
Logger: logger,
Config: appConfig,
Payload: notify.Payload{
FailureType: "EmailTest",
DeviceSerial: "FAKEWDDJ324KSO",
testNotify := notify.New(
logger,
appConfig,
models.Device{
SerialNumber: "FAKEWDDJ324KSO",
DeviceType: pkg.DeviceProtocolAta,
DeviceName: "/dev/sda",
Test: true,
},
}
true,
)
err := testNotify.Send()
if err != nil {
logger.Errorln("An error occurred while sending test notification", err)
@@ -63,20 +63,16 @@ func UploadDeviceMetrics(c *gin.Context) {
}
//check for error
if updatedDevice.DeviceStatus != pkg.DeviceStatusPassed {
if notify.ShouldNotify(updatedDevice, smartData, appConfig.GetString("notify.level"), appConfig.GetString("notify.filter_attributes")) {
//send notifications
testNotify := notify.Notify{
Config: appConfig,
Payload: notify.Payload{
FailureType: notify.NotifyFailureTypeSmartFailure,
DeviceName: updatedDevice.DeviceName,
DeviceType: updatedDevice.DeviceProtocol,
DeviceSerial: updatedDevice.SerialNumber,
Test: false,
},
Logger: logger,
}
_ = testNotify.Send() //we ignore error message when sending notifications.
liveNotify := notify.New(
logger,
appConfig,
updatedDevice,
false,
)
_ = liveNotify.Send() //we ignore error message when sending notifications.
}
c.JSON(http.StatusOK, gin.H{"success": true})
+18
View File
@@ -186,6 +186,8 @@ func (suite *ServerTestSuite) TestUploadDeviceMetricsRoute() {
} else {
fakeConfig.EXPECT().GetString("web.influxdb.host").Return("localhost").AnyTimes()
}
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
ae := web.AppEngine{
Config: fakeConfig,
@@ -219,6 +221,8 @@ func (suite *ServerTestSuite) TestPopulateMultiple() {
fakeConfig := mock_config.NewMockInterface(mockCtrl)
//fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return("testdata/scrutiny_test.db")
fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes()
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
@@ -326,6 +330,9 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_WebhookFailure() {
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"https://unroutable.domain.example.asdfghj"})
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
fakeConfig.EXPECT().GetString("web.influxdb.host").Return("influxdb").AnyTimes()
@@ -365,6 +372,9 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptFailure() {
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///missing/path/on/disk"})
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
fakeConfig.EXPECT().GetString("web.influxdb.host").Return("influxdb").AnyTimes()
@@ -404,6 +414,9 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptSuccess() {
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///usr/bin/env"})
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
fakeConfig.EXPECT().GetString("web.influxdb.host").Return("influxdb").AnyTimes()
@@ -443,6 +456,9 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_ShoutrrrFailure() {
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"discord://invalidtoken@channel"})
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
fakeConfig.EXPECT().GetString("web.influxdb.host").Return("influxdb").AnyTimes()
@@ -481,6 +497,8 @@ func (suite *ServerTestSuite) TestGetDevicesSummaryRoute_Nvme() {
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{})
fakeConfig.EXPECT().GetString("notify.level").AnyTimes().Return(pkg.NotifyLevelFail)
fakeConfig.EXPECT().GetString("notify.filter_attributes").AnyTimes().Return(pkg.NotifyFilterAttributesAll)
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
fakeConfig.EXPECT().GetString("web.influxdb.host").Return("influxdb").AnyTimes()
@@ -21,10 +21,10 @@ export class TreoConfigService
{
let currentScrutinyConfig = defaultConfig
let localConfigStr = localStorage.getItem(SCRUTINY_CONFIG_LOCAL_STORAGE_KEY)
const localConfigStr = localStorage.getItem(SCRUTINY_CONFIG_LOCAL_STORAGE_KEY)
if (localConfigStr){
//check localstorage for a value
let localConfig = JSON.parse(localConfigStr)
// check localstorage for a value
const localConfig = JSON.parse(localConfigStr)
currentScrutinyConfig = Object.assign({}, currentScrutinyConfig, localConfig) // make sure defaults are available if missing from localStorage.
}
// Set the private defaults
@@ -38,20 +38,20 @@ export class TreoConfigService
/**
* Setter and getter for config
*/
//Setter
// Setter
set config(value: any)
{
// Merge the new config over to the current config
let config = _.merge({}, this._config.getValue(), value);
const config = _.merge({}, this._config.getValue(), value);
//Store the config in localstorage
// Store the config in localstorage
localStorage.setItem(SCRUTINY_CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config));
// Execute the observable
this._config.next(config);
}
//Getter
// Getter
get config$(): Observable<any>
{
return this._config.asObservable();
@@ -1,7 +1,7 @@
import { Layout } from "app/layout/layout.types";
import { Layout } from 'app/layout/layout.types';
// Theme type
export type Theme = "light" | "dark" | "system";
export type Theme = 'light' | 'dark' | 'system';
/**
* AppConfig interface. Update this interface to strictly type your config
@@ -28,12 +28,12 @@ export interface AppConfig
* "ConfigService".
*/
export const appConfig: AppConfig = {
theme : "light",
layout: "material",
theme : 'light',
layout: 'material',
dashboardDisplay: "name",
dashboardSort: "status",
dashboardDisplay: 'name',
dashboardSort: 'status',
temperatureUnit: "celsius",
temperatureUnit: 'celsius',
};
@@ -1,264 +1,264 @@
export const sda = {
"data": {
"device": {
"CreatedAt": "2021-06-24T21:17:31.301226-07:00",
"UpdatedAt": "2021-10-24T16:37:56.981833-07:00",
"DeletedAt": null,
"wwn": "0x5002538e40a22954",
"device_name": "sda",
"manufacturer": "ATA",
"model_name": "Samsung_SSD_860_EVO_500GB",
"interface_type": "SCSI",
"interface_speed": "",
"serial_number": "S3YZNB0KBXXXXXX",
"firmware": "002C",
"rotational_speed": 0,
"capacity": 500107862016,
"form_factor": "",
"smart_support": false,
"device_protocol": "NVMe",
"device_type": "",
"label": "",
"host_id": "",
"device_status": 0
'data': {
'device': {
'CreatedAt': '2021-06-24T21:17:31.301226-07:00',
'UpdatedAt': '2021-10-24T16:37:56.981833-07:00',
'DeletedAt': null,
'wwn': '0x5002538e40a22954',
'device_name': 'sda',
'manufacturer': 'ATA',
'model_name': 'Samsung_SSD_860_EVO_500GB',
'interface_type': 'SCSI',
'interface_speed': '',
'serial_number': 'S3YZNB0KBXXXXXX',
'firmware': '002C',
'rotational_speed': 0,
'capacity': 500107862016,
'form_factor': '',
'smart_support': false,
'device_protocol': 'NVMe',
'device_type': '',
'label': '',
'host_id': '',
'device_status': 0
},
"smart_results": [{
"date": "2021-10-24T23:20:44Z",
"device_wwn": "0x5002538e40a22954",
"device_protocol": "NVMe",
"temp": 36,
"power_on_hours": 2401,
"power_cycle_count": 266,
"attrs": {
"available_spare": {
"attribute_id": "available_spare",
"value": 100,
"thresh": 10,
"transformed_value": 0,
"status": 0
'smart_results': [{
'date': '2021-10-24T23:20:44Z',
'device_wwn': '0x5002538e40a22954',
'device_protocol': 'NVMe',
'temp': 36,
'power_on_hours': 2401,
'power_cycle_count': 266,
'attrs': {
'available_spare': {
'attribute_id': 'available_spare',
'value': 100,
'thresh': 10,
'transformed_value': 0,
'status': 0
},
"controller_busy_time": {
"attribute_id": "controller_busy_time",
"value": 3060,
"thresh": -1,
"transformed_value": 0,
"status": 0
'controller_busy_time': {
'attribute_id': 'controller_busy_time',
'value': 3060,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"critical_comp_time": {
"attribute_id": "critical_comp_time",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'critical_comp_time': {
'attribute_id': 'critical_comp_time',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"critical_warning": {
"attribute_id": "critical_warning",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'critical_warning': {
'attribute_id': 'critical_warning',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"data_units_read": {
"attribute_id": "data_units_read",
"value": 9511859,
"thresh": -1,
"transformed_value": 0,
"status": 0
'data_units_read': {
'attribute_id': 'data_units_read',
'value': 9511859,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"data_units_written": {
"attribute_id": "data_units_written",
"value": 7773431,
"thresh": -1,
"transformed_value": 0,
"status": 0
'data_units_written': {
'attribute_id': 'data_units_written',
'value': 7773431,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"host_reads": {
"attribute_id": "host_reads",
"value": 111303174,
"thresh": -1,
"transformed_value": 0,
"status": 0
'host_reads': {
'attribute_id': 'host_reads',
'value': 111303174,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"host_writes": {
"attribute_id": "host_writes",
"value": 83170961,
"thresh": -1,
"transformed_value": 0,
"status": 0
'host_writes': {
'attribute_id': 'host_writes',
'value': 83170961,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"media_errors": {
"attribute_id": "media_errors",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'media_errors': {
'attribute_id': 'media_errors',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"num_err_log_entries": {
"attribute_id": "num_err_log_entries",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'num_err_log_entries': {
'attribute_id': 'num_err_log_entries',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"percentage_used": {
"attribute_id": "percentage_used",
"value": 0,
"thresh": 100,
"transformed_value": 0,
"status": 0
'percentage_used': {
'attribute_id': 'percentage_used',
'value': 0,
'thresh': 100,
'transformed_value': 0,
'status': 0
},
"power_cycles": {
"attribute_id": "power_cycles",
"value": 266,
"thresh": -1,
"transformed_value": 0,
"status": 0
'power_cycles': {
'attribute_id': 'power_cycles',
'value': 266,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"power_on_hours": {
"attribute_id": "power_on_hours",
"value": 2401,
"thresh": -1,
"transformed_value": 0,
"status": 0
'power_on_hours': {
'attribute_id': 'power_on_hours',
'value': 2401,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"temperature": {
"attribute_id": "temperature",
"value": 36,
"thresh": -1,
"transformed_value": 0,
"status": 0
'temperature': {
'attribute_id': 'temperature',
'value': 36,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"unsafe_shutdowns": {
"attribute_id": "unsafe_shutdowns",
"value": 43,
"thresh": -1,
"transformed_value": 0,
"status": 0
'unsafe_shutdowns': {
'attribute_id': 'unsafe_shutdowns',
'value': 43,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"warning_temp_time": {
"attribute_id": "warning_temp_time",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'warning_temp_time': {
'attribute_id': 'warning_temp_time',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
}
},
"Status": 0
'Status': 0
}]
},
"metadata": {
"available_spare": {
"display_name": "Available Spare",
"ideal": "high",
"critical": true,
"description": "Contains a normalized percentage (0 to 100%) of the remaining spare capacity available.",
"display_type": ""
'metadata': {
'available_spare': {
'display_name': 'Available Spare',
'ideal': 'high',
'critical': true,
'description': 'Contains a normalized percentage (0 to 100%) of the remaining spare capacity available.',
'display_type': ''
},
"controller_busy_time": {
"display_name": "Controller Busy Time",
"ideal": "",
"critical": false,
"description": "Contains the amount of time the controller is busy with I/O commands. The controller is busy when there is a command outstanding to an I/O Queue (specifically, a command was issued via an I/O Submission Queue Tail doorbell write and the corresponding completion queue entry has not been posted yet to the associated I/O Completion Queue). This value is reported in minutes.",
"display_type": ""
'controller_busy_time': {
'display_name': 'Controller Busy Time',
'ideal': '',
'critical': false,
'description': 'Contains the amount of time the controller is busy with I/O commands. The controller is busy when there is a command outstanding to an I/O Queue (specifically, a command was issued via an I/O Submission Queue Tail doorbell write and the corresponding completion queue entry has not been posted yet to the associated I/O Completion Queue). This value is reported in minutes.',
'display_type': ''
},
"critical_comp_time": {
"display_name": "Critical CompTime",
"ideal": "",
"critical": false,
"description": "Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater the Critical Composite Temperature Threshold (CCTEMP) field in the Identify Controller data structure.",
"display_type": ""
'critical_comp_time': {
'display_name': 'Critical CompTime',
'ideal': '',
'critical': false,
'description': 'Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater the Critical Composite Temperature Threshold (CCTEMP) field in the Identify Controller data structure.',
'display_type': ''
},
"critical_warning": {
"display_name": "Critical Warning",
"ideal": "low",
"critical": true,
"description": "This field indicates critical warnings for the state of the controller. Each bit corresponds to a critical warning type; multiple bits may be set. If a bit is cleared to 0, then that critical warning does not apply. Critical warnings may result in an asynchronous event notification to the host. Bits in this field represent the current associated state and are not persistent.",
"display_type": ""
'critical_warning': {
'display_name': 'Critical Warning',
'ideal': 'low',
'critical': true,
'description': 'This field indicates critical warnings for the state of the controller. Each bit corresponds to a critical warning type; multiple bits may be set. If a bit is cleared to 0, then that critical warning does not apply. Critical warnings may result in an asynchronous event notification to the host. Bits in this field represent the current associated state and are not persistent.',
'display_type': ''
},
"data_units_read": {
"display_name": "Data Units Read",
"ideal": "",
"critical": false,
"description": "Contains the number of 512 byte data units the host has read from the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes read) and is rounded up. When the LBA size is a value other than 512 bytes, the controller shall convert the amount of data read to 512 byte units.",
"display_type": ""
'data_units_read': {
'display_name': 'Data Units Read',
'ideal': '',
'critical': false,
'description': 'Contains the number of 512 byte data units the host has read from the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes read) and is rounded up. When the LBA size is a value other than 512 bytes, the controller shall convert the amount of data read to 512 byte units.',
'display_type': ''
},
"data_units_written": {
"display_name": "Data Units Written",
"ideal": "",
"critical": false,
"description": "Contains the number of 512 byte data units the host has written to the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes written) and is rounded up. When the LBA size is a value other than 512 bytes, the controller shall convert the amount of data written to 512 byte units.",
"display_type": ""
'data_units_written': {
'display_name': 'Data Units Written',
'ideal': '',
'critical': false,
'description': 'Contains the number of 512 byte data units the host has written to the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes written) and is rounded up. When the LBA size is a value other than 512 bytes, the controller shall convert the amount of data written to 512 byte units.',
'display_type': ''
},
"host_reads": {
"display_name": "Host Reads",
"ideal": "",
"critical": false,
"description": "Contains the number of read commands completed by the controller",
"display_type": ""
'host_reads': {
'display_name': 'Host Reads',
'ideal': '',
'critical': false,
'description': 'Contains the number of read commands completed by the controller',
'display_type': ''
},
"host_writes": {
"display_name": "Host Writes",
"ideal": "",
"critical": false,
"description": "Contains the number of write commands completed by the controller",
"display_type": ""
'host_writes': {
'display_name': 'Host Writes',
'ideal': '',
'critical': false,
'description': 'Contains the number of write commands completed by the controller',
'display_type': ''
},
"media_errors": {
"display_name": "Media Errors",
"ideal": "low",
"critical": true,
"description": "Contains the number of occurrences where the controller detected an unrecovered data integrity error. Errors such as uncorrectable ECC, CRC checksum failure, or LBA tag mismatch are included in this field.",
"display_type": ""
'media_errors': {
'display_name': 'Media Errors',
'ideal': 'low',
'critical': true,
'description': 'Contains the number of occurrences where the controller detected an unrecovered data integrity error. Errors such as uncorrectable ECC, CRC checksum failure, or LBA tag mismatch are included in this field.',
'display_type': ''
},
"num_err_log_entries": {
"display_name": "Numb Err Log Entries",
"ideal": "low",
"critical": true,
"description": "Contains the number of Error Information log entries over the life of the controller.",
"display_type": ""
'num_err_log_entries': {
'display_name': 'Numb Err Log Entries',
'ideal': 'low',
'critical': true,
'description': 'Contains the number of Error Information log entries over the life of the controller.',
'display_type': ''
},
"percentage_used": {
"display_name": "Percentage Used",
"ideal": "low",
"critical": true,
"description": "Contains a vendor specific estimate of the percentage of NVM subsystem life used based on the actual usage and the manufacturers prediction of NVM life. A value of 100 indicates that the estimated endurance of the NVM in the NVM subsystem has been consumed, but may not indicate an NVM subsystem failure. The value is allowed to exceed 100. Percentages greater than 254 shall be represented as 255. This value shall be updated once per power-on hour (when the controller is not in a sleep state).",
"display_type": ""
'percentage_used': {
'display_name': 'Percentage Used',
'ideal': 'low',
'critical': true,
'description': 'Contains a vendor specific estimate of the percentage of NVM subsystem life used based on the actual usage and the manufacturers prediction of NVM life. A value of 100 indicates that the estimated endurance of the NVM in the NVM subsystem has been consumed, but may not indicate an NVM subsystem failure. The value is allowed to exceed 100. Percentages greater than 254 shall be represented as 255. This value shall be updated once per power-on hour (when the controller is not in a sleep state).',
'display_type': ''
},
"power_cycles": {
"display_name": "Power Cycles",
"ideal": "",
"critical": false,
"description": "Contains the number of power cycles.",
"display_type": ""
'power_cycles': {
'display_name': 'Power Cycles',
'ideal': '',
'critical': false,
'description': 'Contains the number of power cycles.',
'display_type': ''
},
"power_on_hours": {
"display_name": "Power on Hours",
"ideal": "",
"critical": false,
"description": "Contains the number of power-on hours. Power on hours is always logging, even when in low power mode.",
"display_type": ""
'power_on_hours': {
'display_name': 'Power on Hours',
'ideal': '',
'critical': false,
'description': 'Contains the number of power-on hours. Power on hours is always logging, even when in low power mode.',
'display_type': ''
},
"temperature": {
"display_name": "Temperature",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'temperature': {
'display_name': 'Temperature',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"unsafe_shutdowns": {
"display_name": "Unsafe Shutdowns",
"ideal": "",
"critical": false,
"description": "Contains the number of unsafe shutdowns. This count is incremented when a shutdown notification (CC.SHN) is not received prior to loss of power.",
"display_type": ""
'unsafe_shutdowns': {
'display_name': 'Unsafe Shutdowns',
'ideal': '',
'critical': false,
'description': 'Contains the number of unsafe shutdowns. This count is incremented when a shutdown notification (CC.SHN) is not received prior to loss of power.',
'display_type': ''
},
"warning_temp_time": {
"display_name": "Warning Temp Time",
"ideal": "",
"critical": false,
"description": "Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater than or equal to the Warning Composite Temperature Threshold (WCTEMP) field and less than the Critical Composite Temperature Threshold (CCTEMP) field in the Identify Controller data structure.",
"display_type": ""
'warning_temp_time': {
'display_name': 'Warning Temp Time',
'ideal': '',
'critical': false,
'description': 'Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater than or equal to the Warning Composite Temperature Threshold (WCTEMP) field and less than the Critical Composite Temperature Threshold (CCTEMP) field in the Identify Controller data structure.',
'display_type': ''
}
},
"success": true
'success': true
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,222 +1,222 @@
export const sdd = {
"data": {
"device": {
"CreatedAt": "2021-06-24T21:17:31.30374-07:00",
"UpdatedAt": "2021-10-24T16:37:57.013758-07:00",
"DeletedAt": null,
"wwn": "0x5000cca252c859cc",
"device_name": "sdd",
"manufacturer": "ATA",
"model_name": "WDC_WD80EFAX-68LHPN0",
"interface_type": "SCSI",
"interface_speed": "",
"serial_number": "7SGLXXXXX",
"firmware": "",
"rotational_speed": 0,
"capacity": 8001563222016,
"form_factor": "",
"smart_support": false,
"device_protocol": "SCSI",
"device_type": "",
"label": "",
"host_id": "",
"device_status": 0
'data': {
'device': {
'CreatedAt': '2021-06-24T21:17:31.30374-07:00',
'UpdatedAt': '2021-10-24T16:37:57.013758-07:00',
'DeletedAt': null,
'wwn': '0x5000cca252c859cc',
'device_name': 'sdd',
'manufacturer': 'ATA',
'model_name': 'WDC_WD80EFAX-68LHPN0',
'interface_type': 'SCSI',
'interface_speed': '',
'serial_number': '7SGLXXXXX',
'firmware': '',
'rotational_speed': 0,
'capacity': 8001563222016,
'form_factor': '',
'smart_support': false,
'device_protocol': 'SCSI',
'device_type': '',
'label': '',
'host_id': '',
'device_status': 0
},
"smart_results": [{
"date": "2021-10-24T23:20:44Z",
"device_wwn": "0x5000cca252c859cc",
"device_protocol": "SCSI",
"temp": 34,
"power_on_hours": 43549,
"power_cycle_count": 0,
"attrs": {
"read_correction_algorithm_invocations": {
"attribute_id": "read_correction_algorithm_invocations",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'smart_results': [{
'date': '2021-10-24T23:20:44Z',
'device_wwn': '0x5000cca252c859cc',
'device_protocol': 'SCSI',
'temp': 34,
'power_on_hours': 43549,
'power_cycle_count': 0,
'attrs': {
'read_correction_algorithm_invocations': {
'attribute_id': 'read_correction_algorithm_invocations',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_errors_corrected_by_eccdelayed": {
"attribute_id": "read_errors_corrected_by_eccdelayed",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'read_errors_corrected_by_eccdelayed': {
'attribute_id': 'read_errors_corrected_by_eccdelayed',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_errors_corrected_by_eccfast": {
"attribute_id": "read_errors_corrected_by_eccfast",
"value": 300357663,
"thresh": -1,
"transformed_value": 0,
"status": 0
'read_errors_corrected_by_eccfast': {
'attribute_id': 'read_errors_corrected_by_eccfast',
'value': 300357663,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_errors_corrected_by_rereads_rewrites": {
"attribute_id": "read_errors_corrected_by_rereads_rewrites",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'read_errors_corrected_by_rereads_rewrites': {
'attribute_id': 'read_errors_corrected_by_rereads_rewrites',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"read_total_errors_corrected": {
"attribute_id": "read_total_errors_corrected",
"value": 300357663,
"thresh": -1,
"transformed_value": 0,
"status": 0
'read_total_errors_corrected': {
'attribute_id': 'read_total_errors_corrected',
'value': 300357663,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_total_uncorrected_errors": {
"attribute_id": "read_total_uncorrected_errors",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'read_total_uncorrected_errors': {
'attribute_id': 'read_total_uncorrected_errors',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"scsi_grown_defect_list": {
"attribute_id": "scsi_grown_defect_list",
"value": 56,
"thresh": 0,
"transformed_value": 0,
"status": 0
'scsi_grown_defect_list': {
'attribute_id': 'scsi_grown_defect_list',
'value': 56,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"write_correction_algorithm_invocations": {
"attribute_id": "write_correction_algorithm_invocations",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_correction_algorithm_invocations': {
'attribute_id': 'write_correction_algorithm_invocations',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_errors_corrected_by_eccdelayed": {
"attribute_id": "write_errors_corrected_by_eccdelayed",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_errors_corrected_by_eccdelayed': {
'attribute_id': 'write_errors_corrected_by_eccdelayed',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_errors_corrected_by_eccfast": {
"attribute_id": "write_errors_corrected_by_eccfast",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_errors_corrected_by_eccfast': {
'attribute_id': 'write_errors_corrected_by_eccfast',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_errors_corrected_by_rereads_rewrites": {
"attribute_id": "write_errors_corrected_by_rereads_rewrites",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'write_errors_corrected_by_rereads_rewrites': {
'attribute_id': 'write_errors_corrected_by_rereads_rewrites',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"write_total_errors_corrected": {
"attribute_id": "write_total_errors_corrected",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_total_errors_corrected': {
'attribute_id': 'write_total_errors_corrected',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_total_uncorrected_errors": {
"attribute_id": "write_total_uncorrected_errors",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'write_total_uncorrected_errors': {
'attribute_id': 'write_total_uncorrected_errors',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
}
},
"Status": 0
'Status': 0
}]
},
"metadata": {
"read_correction_algorithm_invocations": {
"display_name": "Read Correction Algorithm Invocations",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'metadata': {
'read_correction_algorithm_invocations': {
'display_name': 'Read Correction Algorithm Invocations',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_errors_corrected_by_eccdelayed": {
"display_name": "Read Errors Corrected by ECC Delayed",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'read_errors_corrected_by_eccdelayed': {
'display_name': 'Read Errors Corrected by ECC Delayed',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_errors_corrected_by_eccfast": {
"display_name": "Read Errors Corrected by ECC Fast",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'read_errors_corrected_by_eccfast': {
'display_name': 'Read Errors Corrected by ECC Fast',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_errors_corrected_by_rereads_rewrites": {
"display_name": "Read Errors Corrected by ReReads/ReWrites",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'read_errors_corrected_by_rereads_rewrites': {
'display_name': 'Read Errors Corrected by ReReads/ReWrites',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"read_total_errors_corrected": {
"display_name": "Read Total Errors Corrected",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'read_total_errors_corrected': {
'display_name': 'Read Total Errors Corrected',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_total_uncorrected_errors": {
"display_name": "Read Total Uncorrected Errors",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'read_total_uncorrected_errors': {
'display_name': 'Read Total Uncorrected Errors',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"scsi_grown_defect_list": {
"display_name": "Grown Defect List",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'scsi_grown_defect_list': {
'display_name': 'Grown Defect List',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"write_correction_algorithm_invocations": {
"display_name": "Write Correction Algorithm Invocations",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_correction_algorithm_invocations': {
'display_name': 'Write Correction Algorithm Invocations',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_errors_corrected_by_eccdelayed": {
"display_name": "Write Errors Corrected by ECC Delayed",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_errors_corrected_by_eccdelayed': {
'display_name': 'Write Errors Corrected by ECC Delayed',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_errors_corrected_by_eccfast": {
"display_name": "Write Errors Corrected by ECC Fast",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_errors_corrected_by_eccfast': {
'display_name': 'Write Errors Corrected by ECC Fast',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_errors_corrected_by_rereads_rewrites": {
"display_name": "Write Errors Corrected by ReReads/ReWrites",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'write_errors_corrected_by_rereads_rewrites': {
'display_name': 'Write Errors Corrected by ReReads/ReWrites',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"write_total_errors_corrected": {
"display_name": "Write Total Errors Corrected",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_total_errors_corrected': {
'display_name': 'Write Total Errors Corrected',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_total_uncorrected_errors": {
"display_name": "Write Total Uncorrected Errors",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'write_total_uncorrected_errors': {
'display_name': 'Write Total Uncorrected Errors',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
}
},
"success": true
'success': true
}
@@ -1,222 +1,222 @@
export const sde = {
"data": {
"device": {
"CreatedAt": "2021-06-24T21:17:31.304461-07:00",
"UpdatedAt": "2021-10-24T16:40:16.495248-07:00",
"DeletedAt": null,
"wwn": "0x5000cca264ebc248",
"device_name": "sde",
"manufacturer": "ATA",
"model_name": "WDC_WD140EDFZ-11A0VA0",
"interface_type": "SCSI",
"interface_speed": "",
"serial_number": "9RK3XXXXX",
"firmware": "",
"rotational_speed": 0,
"capacity": 14000519643136,
"form_factor": "",
"smart_support": false,
"device_protocol": "SCSI",
"device_type": "",
"label": "",
"host_id": "",
"device_status": 0
'data': {
'device': {
'CreatedAt': '2021-06-24T21:17:31.304461-07:00',
'UpdatedAt': '2021-10-24T16:40:16.495248-07:00',
'DeletedAt': null,
'wwn': '0x5000cca264ebc248',
'device_name': 'sde',
'manufacturer': 'ATA',
'model_name': 'WDC_WD140EDFZ-11A0VA0',
'interface_type': 'SCSI',
'interface_speed': '',
'serial_number': '9RK3XXXXX',
'firmware': '',
'rotational_speed': 0,
'capacity': 14000519643136,
'form_factor': '',
'smart_support': false,
'device_protocol': 'SCSI',
'device_type': '',
'label': '',
'host_id': '',
'device_status': 0
},
"smart_results": [{
"date": "2021-10-24T23:20:44Z",
"device_wwn": "0x5000cca264ebc248",
"device_protocol": "SCSI",
"temp": 31,
"power_on_hours": 5675,
"power_cycle_count": 0,
"attrs": {
"read_correction_algorithm_invocations": {
"attribute_id": "read_correction_algorithm_invocations",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'smart_results': [{
'date': '2021-10-24T23:20:44Z',
'device_wwn': '0x5000cca264ebc248',
'device_protocol': 'SCSI',
'temp': 31,
'power_on_hours': 5675,
'power_cycle_count': 0,
'attrs': {
'read_correction_algorithm_invocations': {
'attribute_id': 'read_correction_algorithm_invocations',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_errors_corrected_by_eccdelayed": {
"attribute_id": "read_errors_corrected_by_eccdelayed",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'read_errors_corrected_by_eccdelayed': {
'attribute_id': 'read_errors_corrected_by_eccdelayed',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_errors_corrected_by_eccfast": {
"attribute_id": "read_errors_corrected_by_eccfast",
"value": 1410362924,
"thresh": -1,
"transformed_value": 0,
"status": 0
'read_errors_corrected_by_eccfast': {
'attribute_id': 'read_errors_corrected_by_eccfast',
'value': 1410362924,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_errors_corrected_by_rereads_rewrites": {
"attribute_id": "read_errors_corrected_by_rereads_rewrites",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'read_errors_corrected_by_rereads_rewrites': {
'attribute_id': 'read_errors_corrected_by_rereads_rewrites',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"read_total_errors_corrected": {
"attribute_id": "read_total_errors_corrected",
"value": 1410362924,
"thresh": -1,
"transformed_value": 0,
"status": 0
'read_total_errors_corrected': {
'attribute_id': 'read_total_errors_corrected',
'value': 1410362924,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"read_total_uncorrected_errors": {
"attribute_id": "read_total_uncorrected_errors",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'read_total_uncorrected_errors': {
'attribute_id': 'read_total_uncorrected_errors',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"scsi_grown_defect_list": {
"attribute_id": "scsi_grown_defect_list",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'scsi_grown_defect_list': {
'attribute_id': 'scsi_grown_defect_list',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"write_correction_algorithm_invocations": {
"attribute_id": "write_correction_algorithm_invocations",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_correction_algorithm_invocations': {
'attribute_id': 'write_correction_algorithm_invocations',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_errors_corrected_by_eccdelayed": {
"attribute_id": "write_errors_corrected_by_eccdelayed",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_errors_corrected_by_eccdelayed': {
'attribute_id': 'write_errors_corrected_by_eccdelayed',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_errors_corrected_by_eccfast": {
"attribute_id": "write_errors_corrected_by_eccfast",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_errors_corrected_by_eccfast': {
'attribute_id': 'write_errors_corrected_by_eccfast',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_errors_corrected_by_rereads_rewrites": {
"attribute_id": "write_errors_corrected_by_rereads_rewrites",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'write_errors_corrected_by_rereads_rewrites': {
'attribute_id': 'write_errors_corrected_by_rereads_rewrites',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
},
"write_total_errors_corrected": {
"attribute_id": "write_total_errors_corrected",
"value": 0,
"thresh": -1,
"transformed_value": 0,
"status": 0
'write_total_errors_corrected': {
'attribute_id': 'write_total_errors_corrected',
'value': 0,
'thresh': -1,
'transformed_value': 0,
'status': 0
},
"write_total_uncorrected_errors": {
"attribute_id": "write_total_uncorrected_errors",
"value": 0,
"thresh": 0,
"transformed_value": 0,
"status": 0
'write_total_uncorrected_errors': {
'attribute_id': 'write_total_uncorrected_errors',
'value': 0,
'thresh': 0,
'transformed_value': 0,
'status': 0
}
},
"Status": 0
'Status': 0
}]
},
"metadata": {
"read_correction_algorithm_invocations": {
"display_name": "Read Correction Algorithm Invocations",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'metadata': {
'read_correction_algorithm_invocations': {
'display_name': 'Read Correction Algorithm Invocations',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_errors_corrected_by_eccdelayed": {
"display_name": "Read Errors Corrected by ECC Delayed",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'read_errors_corrected_by_eccdelayed': {
'display_name': 'Read Errors Corrected by ECC Delayed',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_errors_corrected_by_eccfast": {
"display_name": "Read Errors Corrected by ECC Fast",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'read_errors_corrected_by_eccfast': {
'display_name': 'Read Errors Corrected by ECC Fast',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_errors_corrected_by_rereads_rewrites": {
"display_name": "Read Errors Corrected by ReReads/ReWrites",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'read_errors_corrected_by_rereads_rewrites': {
'display_name': 'Read Errors Corrected by ReReads/ReWrites',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"read_total_errors_corrected": {
"display_name": "Read Total Errors Corrected",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'read_total_errors_corrected': {
'display_name': 'Read Total Errors Corrected',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"read_total_uncorrected_errors": {
"display_name": "Read Total Uncorrected Errors",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'read_total_uncorrected_errors': {
'display_name': 'Read Total Uncorrected Errors',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"scsi_grown_defect_list": {
"display_name": "Grown Defect List",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'scsi_grown_defect_list': {
'display_name': 'Grown Defect List',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"write_correction_algorithm_invocations": {
"display_name": "Write Correction Algorithm Invocations",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_correction_algorithm_invocations': {
'display_name': 'Write Correction Algorithm Invocations',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_errors_corrected_by_eccdelayed": {
"display_name": "Write Errors Corrected by ECC Delayed",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_errors_corrected_by_eccdelayed': {
'display_name': 'Write Errors Corrected by ECC Delayed',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_errors_corrected_by_eccfast": {
"display_name": "Write Errors Corrected by ECC Fast",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_errors_corrected_by_eccfast': {
'display_name': 'Write Errors Corrected by ECC Fast',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_errors_corrected_by_rereads_rewrites": {
"display_name": "Write Errors Corrected by ReReads/ReWrites",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'write_errors_corrected_by_rereads_rewrites': {
'display_name': 'Write Errors Corrected by ReReads/ReWrites',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
},
"write_total_errors_corrected": {
"display_name": "Write Total Errors Corrected",
"ideal": "",
"critical": false,
"description": "",
"display_type": ""
'write_total_errors_corrected': {
'display_name': 'Write Total Errors Corrected',
'ideal': '',
'critical': false,
'description': '',
'display_type': ''
},
"write_total_uncorrected_errors": {
"display_name": "Write Total Uncorrected Errors",
"ideal": "low",
"critical": true,
"description": "",
"display_type": ""
'write_total_uncorrected_errors': {
'display_name': 'Write Total Uncorrected Errors',
'ideal': 'low',
'critical': true,
'description': '',
'display_type': ''
}
},
"success": true
'success': true
}
@@ -1,29 +1,29 @@
export const sdf = {
"data": {
"device": {
"CreatedAt": "2021-06-24T21:17:31.305246-07:00",
"UpdatedAt": "2021-06-24T21:17:31.305246-07:00",
"DeletedAt": null,
"wwn": "0x50014ee20b2a72a9",
"device_name": "sdf",
"manufacturer": "ATA",
"model_name": "WDC_WD60EFRX-68MYMN1",
"interface_type": "SCSI",
"interface_speed": "",
"serial_number": "WD-WXL1HXXXXX",
"firmware": "",
"rotational_speed": 0,
"capacity": 6001175126016,
"form_factor": "",
"smart_support": false,
"device_protocol": "",
"device_type": "",
"label": "",
"host_id": "",
"device_status": 0
'data': {
'device': {
'CreatedAt': '2021-06-24T21:17:31.305246-07:00',
'UpdatedAt': '2021-06-24T21:17:31.305246-07:00',
'DeletedAt': null,
'wwn': '0x50014ee20b2a72a9',
'device_name': 'sdf',
'manufacturer': 'ATA',
'model_name': 'WDC_WD60EFRX-68MYMN1',
'interface_type': 'SCSI',
'interface_speed': '',
'serial_number': 'WD-WXL1HXXXXX',
'firmware': '',
'rotational_speed': 0,
'capacity': 6001175126016,
'form_factor': '',
'smart_support': false,
'device_protocol': '',
'device_type': '',
'label': '',
'host_id': '',
'device_status': 0
},
"smart_results": []
'smart_results': []
},
"metadata": null,
"success": true
'metadata': null,
'success': true
}
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
import { Component, OnInit, Inject } from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {DashboardDeviceDeleteDialogService} from "./dashboard-device-delete-dialog.service";
import {Subject} from "rxjs";
import {DashboardDeviceDeleteDialogService} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.service';
import {Subject} from 'rxjs';
@Component({
selector: 'app-dashboard-device-delete-dialog',
@@ -9,18 +9,18 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { SharedModule } from 'app/shared/shared.module';
import {DashboardDeviceDeleteDialogComponent} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.component'
import { MatButtonToggleModule} from "@angular/material/button-toggle";
import {MatTabsModule} from "@angular/material/tabs";
import {MatSliderModule} from "@angular/material/slider";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
import {MatTooltipModule} from "@angular/material/tooltip";
import {dashboardRoutes} from "../../../modules/dashboard/dashboard.routing";
import {MatDividerModule} from "@angular/material/divider";
import {MatMenuModule} from "@angular/material/menu";
import {MatProgressBarModule} from "@angular/material/progress-bar";
import {MatSortModule} from "@angular/material/sort";
import {MatTableModule} from "@angular/material/table";
import {NgApexchartsModule} from "ng-apexcharts";
import { MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatTabsModule} from '@angular/material/tabs';
import {MatSliderModule} from '@angular/material/slider';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatTooltipModule} from '@angular/material/tooltip';
import {dashboardRoutes} from 'app/modules/dashboard/dashboard.routing';
import {MatDividerModule} from '@angular/material/divider';
import {MatMenuModule} from '@angular/material/menu';
import {MatProgressBarModule} from '@angular/material/progress-bar';
import {MatSortModule} from '@angular/material/sort';
import {MatTableModule} from '@angular/material/table';
import {NgApexchartsModule} from 'ng-apexcharts';
import { MatDialogModule } from '@angular/material/dialog';
@NgModule({
@@ -1,13 +1,13 @@
import { Component, Input, Output, OnInit, EventEmitter} from '@angular/core';
import * as moment from "moment";
import {takeUntil} from "rxjs/operators";
import {AppConfig} from "app/core/config/app.config";
import {TreoConfigService} from "@treo/services/config";
import {Subject} from "rxjs";
import * as moment from 'moment';
import {takeUntil} from 'rxjs/operators';
import {AppConfig} from 'app/core/config/app.config';
import {TreoConfigService} from '@treo/services/config';
import {Subject} from 'rxjs';
import humanizeDuration from 'humanize-duration'
import {MatDialog} from '@angular/material/dialog';
import {DashboardDeviceDeleteDialogComponent} from "app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.component";
import {DeviceTitlePipe} from "app/shared/device-title.pipe";
import {DashboardDeviceDeleteDialogComponent} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.component';
import {DeviceTitlePipe} from 'app/shared/device-title.pipe';
@Component({
selector: 'app-dashboard-device',
@@ -15,13 +15,6 @@ import {DeviceTitlePipe} from "app/shared/device-title.pipe";
styleUrls: ['./dashboard-device.component.scss']
})
export class DashboardDeviceComponent implements OnInit {
@Input() deviceSummary: any;
@Input() deviceWWN: string;
@Output() deviceDeleted = new EventEmitter<string>();
config: AppConfig;
private _unsubscribeAll: Subject<any>;
constructor(
private _configService: TreoConfigService,
@@ -30,6 +23,15 @@ export class DashboardDeviceComponent implements OnInit {
// Set the private defaults
this._unsubscribeAll = new Subject();
}
@Input() deviceSummary: any;
@Input() deviceWWN: string;
@Output() deviceDeleted = new EventEmitter<string>();
config: AppConfig;
private _unsubscribeAll: Subject<any>;
readonly humanizeDuration = humanizeDuration;
ngOnInit(): void {
// Subscribe to config changes
@@ -45,7 +47,7 @@ export class DashboardDeviceComponent implements OnInit {
// @ Public methods
// -----------------------------------------------------------------------------------------------------
classDeviceLastUpdatedOn(deviceSummary){
classDeviceLastUpdatedOn(deviceSummary): string {
if (deviceSummary.device.device_status !== 0) {
return 'text-red' // if the device has failed, always highlight in red
} else if(deviceSummary.device.device_status === 0 && deviceSummary.smart){
@@ -65,16 +67,14 @@ export class DashboardDeviceComponent implements OnInit {
}
}
deviceStatusString(deviceStatus){
if(deviceStatus == 0){
return "passed"
deviceStatusString(deviceStatus): string {
if(deviceStatus === 0){
return 'passed'
} else {
return "failed"
return 'failed'
}
}
readonly humanizeDuration = humanizeDuration;
openDeleteDialog(): void {
@@ -9,20 +9,20 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { SharedModule } from 'app/shared/shared.module';
import {DashboardDeviceComponent} from 'app/layout/common/dashboard-device/dashboard-device.component'
import { MatDialogModule } from "@angular/material/dialog";
import { MatButtonToggleModule} from "@angular/material/button-toggle";
import {MatTabsModule} from "@angular/material/tabs";
import {MatSliderModule} from "@angular/material/slider";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
import {MatTooltipModule} from "@angular/material/tooltip";
import {dashboardRoutes} from "../../../modules/dashboard/dashboard.routing";
import {MatDividerModule} from "@angular/material/divider";
import {MatMenuModule} from "@angular/material/menu";
import {MatProgressBarModule} from "@angular/material/progress-bar";
import {MatSortModule} from "@angular/material/sort";
import {MatTableModule} from "@angular/material/table";
import {NgApexchartsModule} from "ng-apexcharts";
import {DashboardDeviceDeleteDialogModule} from "../dashboard-device-delete-dialog/dashboard-device-delete-dialog.module";
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatTabsModule} from '@angular/material/tabs';
import {MatSliderModule} from '@angular/material/slider';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatTooltipModule} from '@angular/material/tooltip';
import {dashboardRoutes} from '../../../modules/dashboard/dashboard.routing';
import {MatDividerModule} from '@angular/material/divider';
import {MatMenuModule} from '@angular/material/menu';
import {MatProgressBarModule} from '@angular/material/progress-bar';
import {MatSortModule} from '@angular/material/sort';
import {MatTableModule} from '@angular/material/table';
import {NgApexchartsModule} from 'ng-apexcharts';
import {DashboardDeviceDeleteDialogModule} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.module';
@NgModule({
declarations: [
@@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import {AppConfig} from 'app/core/config/app.config';
import { TreoConfigService } from '@treo/services/config';
import {Subject} from "rxjs";
import {takeUntil} from "rxjs/operators";
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
@Component({
selector: 'app-dashboard-settings',
@@ -9,12 +9,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { SharedModule } from 'app/shared/shared.module';
import {DashboardSettingsComponent} from 'app/layout/common/dashboard-settings/dashboard-settings.component'
import { MatDialogModule } from "@angular/material/dialog";
import { MatButtonToggleModule} from "@angular/material/button-toggle";
import {MatTabsModule} from "@angular/material/tabs";
import {MatSliderModule} from "@angular/material/slider";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
import {MatTooltipModule} from "@angular/material/tooltip";
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatTabsModule} from '@angular/material/tabs';
import {MatSliderModule} from '@angular/material/slider';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatTooltipModule} from '@angular/material/tooltip';
@NgModule({
declarations: [
@@ -9,12 +9,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { SharedModule } from 'app/shared/shared.module';
import {DetailSettingsComponent} from 'app/layout/common/detail-settings/detail-settings.component'
import { MatDialogModule } from "@angular/material/dialog";
import { MatButtonToggleModule} from "@angular/material/button-toggle";
import {MatTabsModule} from "@angular/material/tabs";
import {MatSliderModule} from "@angular/material/slider";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
import {MatTooltipModule} from "@angular/material/tooltip";
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatTabsModule} from '@angular/material/tabs';
import {MatSliderModule} from '@angular/material/slider';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatTooltipModule} from '@angular/material/tooltip';
@NgModule({
declarations: [
@@ -45,7 +45,7 @@ export class LayoutComponent implements OnInit, OnDestroy
// Set the private defaults
this._unsubscribeAll = new Subject();
this.systemPrefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
this.systemPrefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
@@ -7,11 +7,11 @@ import {ApexOptions, ChartComponent} from 'ng-apexcharts';
import { DashboardService } from 'app/modules/dashboard/dashboard.service';
import {MatDialog} from '@angular/material/dialog';
import { DashboardSettingsComponent } from 'app/layout/common/dashboard-settings/dashboard-settings.component';
import {AppConfig} from "app/core/config/app.config";
import {TreoConfigService} from "@treo/services/config";
import {Router} from "@angular/router";
import {TemperaturePipe} from "app/shared/temperature.pipe";
import {DeviceTitlePipe} from "app/shared/device-title.pipe";
import {AppConfig} from 'app/core/config/app.config';
import {TreoConfigService} from '@treo/services/config';
import {Router} from '@angular/router';
import {TemperaturePipe} from 'app/shared/temperature.pipe';
import {DeviceTitlePipe} from 'app/shared/device-title.pipe';
@Component({
selector : 'example',
@@ -25,12 +25,12 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
data: any;
hostGroups: { [hostId: string]: string[] } = {}
temperatureOptions: ApexOptions;
tempDurationKey: string = "forever"
tempDurationKey = 'forever'
config: AppConfig;
// Private
private _unsubscribeAll: Subject<any>;
@ViewChild("tempChart", { static: false }) tempChart: ChartComponent;
@ViewChild('tempChart', { static: false }) tempChart: ChartComponent;
/**
* Constructor
@@ -64,17 +64,17 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config: AppConfig) => {
//check if the old config and the new config do not match.
let oldConfig = JSON.stringify(this.config)
let newConfig = JSON.stringify(config)
// check if the old config and the new config do not match.
const oldConfig = JSON.stringify(this.config)
const newConfig = JSON.stringify(config)
if(oldConfig != newConfig){
if(oldConfig !== newConfig){
console.log(`Configuration updated: ${newConfig} vs ${oldConfig}`)
// Store the config
this.config = config;
if(oldConfig){
console.log("reloading component...")
console.log('reloading component...')
this.refreshComponent()
}
}
@@ -88,10 +88,10 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
// Store the data
this.data = data;
//generate group data.
for(let wwn in this.data.data.summary){
let hostid = this.data.data.summary[wwn].device.host_id
let hostDeviceList = this.hostGroups[hostid] || []
// generate group data.
for(const wwn in this.data.data.summary){
const hostid = this.data.data.summary[wwn].device.host_id
const hostDeviceList = this.hostGroups[hostid] || []
hostDeviceList.push(wwn)
this.hostGroups[hostid] = hostDeviceList
}
@@ -121,34 +121,34 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
private refreshComponent(){
private refreshComponent(): void {
let currentUrl = this.router.url;
const currentUrl = this.router.url;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([currentUrl]);
}
private _deviceDataTemperatureSeries() {
var deviceTemperatureSeries = []
private _deviceDataTemperatureSeries(): any[] {
const deviceTemperatureSeries = []
console.log("DEVICE DATA SUMMARY", this.data)
console.log('DEVICE DATA SUMMARY', this.data)
for(const wwn in this.data.data.summary){
var deviceSummary = this.data.data.summary[wwn]
const deviceSummary = this.data.data.summary[wwn]
if (!deviceSummary.temp_history){
continue
}
let deviceName = DeviceTitlePipe.deviceTitleWithFallback(deviceSummary.device, this.config.dashboardDisplay)
const deviceName = DeviceTitlePipe.deviceTitleWithFallback(deviceSummary.device, this.config.dashboardDisplay)
var deviceSeriesMetadata = {
const deviceSeriesMetadata = {
name: deviceName,
data: []
}
for(let tempHistory of deviceSummary.temp_history){
let newDate = new Date(tempHistory.date);
for(const tempHistory of deviceSummary.temp_history){
const newDate = new Date(tempHistory.date);
deviceSeriesMetadata.data.push({
x: newDate,
y: TemperaturePipe.formatTemperature(tempHistory.temp, this.config.temperatureUnit, false)
@@ -216,9 +216,9 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
// @ Public methods
// -----------------------------------------------------------------------------------------------------
deviceSummariesForHostGroup(hostGroupWWNs: string[]) {
let deviceSummaries = []
for(let wwn of hostGroupWWNs){
deviceSummariesForHostGroup(hostGroupWWNs: string[]): any[] {
const deviceSummaries = []
for(const wwn of hostGroupWWNs){
if(this.data.data.summary[wwn]){
deviceSummaries.push(this.data.data.summary[wwn])
}
@@ -226,7 +226,7 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
return deviceSummaries
}
openDialog() {
openDialog(): void {
const dialogRef = this.dialog.open(DashboardSettingsComponent);
dialogRef.afterClosed().subscribe(result => {
@@ -234,7 +234,7 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
});
}
onDeviceDeleted(wwn: string) {
onDeviceDeleted(wwn: string): void {
delete this.data.data.summary[wwn] // remove the device from the summary list.
}
@@ -12,8 +12,8 @@ import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { NgApexchartsModule } from 'ng-apexcharts';
import { MatTooltipModule } from '@angular/material/tooltip'
import { DashboardSettingsModule } from "app/layout/common/dashboard-settings/dashboard-settings.module";
import { DashboardDeviceModule } from "app/layout/common/dashboard-device/dashboard-device.module";
import { DashboardSettingsModule } from 'app/layout/common/dashboard-settings/dashboard-settings.module';
import { DashboardDeviceModule } from 'app/layout/common/dashboard-device/dashboard-device.module';
@NgModule({
declarations: [
@@ -1,6 +1,6 @@
import { Route } from '@angular/router';
import { DashboardComponent } from 'app/modules/dashboard/dashboard.component';
import {DashboardResolver} from "./dashboard.resolvers";
import {DashboardResolver} from 'app/modules/dashboard/dashboard.resolvers';
export const dashboardRoutes: Route[] = [
{
@@ -55,9 +55,9 @@ export class DashboardService
getSummaryTempData(durationKey: string): Observable<any>
{
let params = {}
const params = {}
if(durationKey){
params["duration_key"] = durationKey
params['duration_key'] = durationKey
}
return this._httpClient.get(getBasePath() + '/api/summary/temp', {params: params});
@@ -1,18 +1,17 @@
import {AfterViewInit, Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ApexOptions} from "ng-apexcharts";
import {MatTableDataSource} from "@angular/material/table";
import {MatSort} from "@angular/material/sort";
import {Subject} from "rxjs";
import {DetailService} from "./detail.service";
import {takeUntil} from "rxjs/operators";
import {fadeOut} from "../../../@treo/animations/fade";
import {DetailSettingsComponent} from "app/layout/common/detail-settings/detail-settings.component";
import {MatDialog} from "@angular/material/dialog";
import {ApexOptions} from 'ng-apexcharts';
import {MatTableDataSource} from '@angular/material/table';
import {MatSort} from '@angular/material/sort';
import {Subject} from 'rxjs';
import {DetailService} from './detail.service';
import {takeUntil} from 'rxjs/operators';
import {DetailSettingsComponent} from 'app/layout/common/detail-settings/detail-settings.component';
import {MatDialog} from '@angular/material/dialog';
import humanizeDuration from 'humanize-duration';
import {TreoConfigService} from "../../../@treo/services/config";
import {AppConfig} from "../../core/config/app.config";
import {TreoConfigService} from '@treo/services/config';
import {AppConfig} from 'app/core/config/app.config';
import {animate, state, style, transition, trigger} from '@angular/animations';
import {formatDate} from "@angular/common";
import {formatDate} from '@angular/common';
import { LOCALE_ID, Inject } from '@angular/core';
// from Constants.go - these must match
@@ -37,27 +36,6 @@ const AttributeStatusFailedScrutiny = 4
export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
config: AppConfig;
onlyCritical: boolean = true;
// data: any;
expandedAttribute: any | null;
metadata: any;
device: any;
smart_results: any[];
commonSparklineOptions: Partial<ApexOptions>;
smartAttributeDataSource: MatTableDataSource<any>;
smartAttributeTableColumns: string[];
@ViewChild('smartAttributeTable', {read: MatSort})
smartAttributeTableMatSort: MatSort;
// Private
private _unsubscribeAll: Subject<any>;
private systemPrefersDark: boolean;
/**
* Constructor
*
@@ -79,10 +57,33 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
// this.recentTransactionsTableColumns = ['status', 'id', 'name', 'value', 'worst', 'thresh'];
this.smartAttributeTableColumns = ['status', 'id', 'name', 'value', 'worst', 'thresh','ideal', 'failure', 'history'];
this.systemPrefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
this.systemPrefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
config: AppConfig;
onlyCritical = true;
// data: any;
expandedAttribute: any | null;
metadata: any;
device: any;
smart_results: any[];
commonSparklineOptions: Partial<ApexOptions>;
smartAttributeDataSource: MatTableDataSource<any>;
smartAttributeTableColumns: string[];
@ViewChild('smartAttributeTable', {read: MatSort})
smartAttributeTableMatSort: MatSort;
// Private
private _unsubscribeAll: Subject<any>;
private systemPrefersDark: boolean;
readonly humanizeDuration = humanizeDuration;
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
@@ -181,7 +182,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
getAttributeName(attribute_data): string {
let attribute_metadata = this.metadata[attribute_data.attribute_id]
const attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata){
return 'Unknown Attribute Name'
} else {
@@ -189,7 +190,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
}
}
getAttributeDescription(attribute_data){
let attribute_metadata = this.metadata[attribute_data.attribute_id]
const attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata){
return 'Unknown'
} else {
@@ -200,12 +201,12 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
getAttributeValue(attribute_data){
if(this.isAta()) {
let attribute_metadata = this.metadata[attribute_data.attribute_id]
const attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata){
return attribute_data.value
} else if (attribute_metadata.display_type == "raw") {
} else if (attribute_metadata.display_type == 'raw') {
return attribute_data.raw_value
} else if (attribute_metadata.display_type == "transformed" && attribute_data.transformed_value) {
} else if (attribute_metadata.display_type == 'transformed' && attribute_data.transformed_value) {
return attribute_data.transformed_value
} else {
return attribute_data.value
@@ -218,7 +219,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
getAttributeValueType(attribute_data){
if(this.isAta()) {
let attribute_metadata = this.metadata[attribute_data.attribute_id]
const attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata){
return ''
} else {
@@ -231,25 +232,25 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
getAttributeIdeal(attribute_data){
if(this.isAta()){
return this.metadata[attribute_data.attribute_id]?.display_type == "raw" ? this.metadata[attribute_data.attribute_id]?.ideal : ''
return this.metadata[attribute_data.attribute_id]?.display_type == 'raw' ? this.metadata[attribute_data.attribute_id]?.ideal : ''
} else {
return this.metadata[attribute_data.attribute_id]?.ideal
}
}
getAttributeWorst(attribute_data){
let attribute_metadata = this.metadata[attribute_data.attribute_id]
const attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata){
return attribute_data.worst
} else {
return attribute_metadata?.display_type == "normalized" ? attribute_data.worst : ''
return attribute_metadata?.display_type == 'normalized' ? attribute_data.worst : ''
}
}
getAttributeThreshold(attribute_data){
if(this.isAta()){
let attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata || attribute_metadata.display_type == "normalized"){
const attribute_metadata = this.metadata[attribute_data.attribute_id]
if(!attribute_metadata || attribute_metadata.display_type == 'normalized'){
return attribute_data.thresh
} else {
// if(this.data.metadata[attribute_data.attribute_id].observed_thresholds){
@@ -273,7 +274,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
}
let attributes_length = 0
let attributes = this.smart_results[0]?.attrs
const attributes = this.smart_results[0]?.attrs
if (attributes) {
attributes_length = Object.keys(attributes).length
}
@@ -292,12 +293,12 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
}
private _generateSmartAttributeTableDataSource(smart_results){
var smartAttributeDataSource = [];
const smartAttributeDataSource = [];
if(smart_results.length == 0){
return smartAttributeDataSource
}
var latest_smart_result = smart_results[0];
const latest_smart_result = smart_results[0];
let attributes = {}
if(this.isScsi()) {
this.smartAttributeTableColumns = ['status', 'name', 'value', 'thresh', 'history'];
@@ -306,20 +307,20 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
this.smartAttributeTableColumns = ['status', 'name', 'value', 'thresh', 'ideal', 'history'];
attributes = latest_smart_result.attrs
} else {
//ATA
// ATA
attributes = latest_smart_result.attrs
this.smartAttributeTableColumns = ['status', 'id', 'name', 'value', 'thresh','ideal', 'failure', 'history'];
}
for(const attrId in attributes){
var attr = attributes[attrId]
const attr = attributes[attrId]
//chart history data
// chart history data
if (!attr.chartData) {
var attrHistory = []
for (let smart_result of smart_results){
const attrHistory = []
for (const smart_result of smart_results){
// attrHistory.push(this.getAttributeValue(smart_result.attrs[attrId]))
const chartDatapoint = {
@@ -342,12 +343,12 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
attributes[attrId].chartData = [
{
name: "chart-line-sparkline",
name: 'chart-line-sparkline',
data: attrHistory
}
]
}
//determine when to include the attributes in table.
// determine when to include the attributes in table.
if(!this.onlyCritical || this.onlyCritical && this.metadata[attr.attribute_id]?.critical || attr.value < attr.thresh){
smartAttributeDataSource.push(attr)
@@ -367,7 +368,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
// Account balance
this.commonSparklineOptions = {
chart: {
type: "bar",
type: 'bar',
width: 100,
height: 25,
sparkline: {
@@ -392,7 +393,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
y: {
title: {
formatter: function(seriesName) {
return "";
return '';
}
}
},
@@ -421,7 +422,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
// -----------------------------------------------------------------------------------------------------
toHex(decimalNumb){
return "0x" + Number(decimalNumb).toString(16).padStart(2, '0').toUpperCase()
return '0x' + Number(decimalNumb).toString(16).padStart(2, '0').toUpperCase()
}
toggleOnlyCritical(){
this.onlyCritical = !this.onlyCritical
@@ -449,6 +450,4 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
// return item.id || index;
}
readonly humanizeDuration = humanizeDuration;
}
@@ -13,7 +13,7 @@ import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'
import { NgApexchartsModule } from 'ng-apexcharts';
import { TreoCardModule } from '@treo/components/card';
import {DetailSettingsModule} from "app/layout/common/detail-settings/detail-settings.module";
import {DetailSettingsModule} from 'app/layout/common/detail-settings/detail-settings.module';
@NgModule({
declarations: [
@@ -1,6 +1,6 @@
import { Route } from '@angular/router';
import { DetailComponent } from 'app/modules/detail/detail.component';
import {DetailResolver} from "./detail.resolvers";
import {DetailResolver} from './detail.resolvers';
export const detailRoutes: Route[] = [
{
@@ -1,33 +1,33 @@
import { Pipe, PipeTransform } from '@angular/core';
import {DeviceTitlePipe} from "./device-title.pipe";
import {DeviceTitlePipe} from 'app/shared/device-title.pipe';
@Pipe({
name: 'deviceSort'
})
export class DeviceSortPipe implements PipeTransform {
statusCompareFn(a: any, b: any) {
statusCompareFn(a: any, b: any): number {
function deviceStatus(deviceSummary): number {
if(!deviceSummary.smart){
return 0
} else if (deviceSummary.device.device_status == 0){
} else if (deviceSummary.device.device_status === 0){
return 1
} else {
return deviceSummary.device.device_status * -1 // will return range from -1, -2, -3
}
}
let left = deviceStatus(a)
let right = deviceStatus(b)
const left = deviceStatus(a)
const right = deviceStatus(b)
return left - right;
}
titleCompareFn(dashboardDisplay: string) {
return function (a: any, b: any){
let _dashboardDisplay = dashboardDisplay
let left = DeviceTitlePipe.deviceTitleForType(a.device, _dashboardDisplay) || DeviceTitlePipe.deviceTitleForType(a.device, 'name')
let right = DeviceTitlePipe.deviceTitleForType(b.device, _dashboardDisplay) || DeviceTitlePipe.deviceTitleForType(b.device, 'name')
const _dashboardDisplay = dashboardDisplay
const left = DeviceTitlePipe.deviceTitleForType(a.device, _dashboardDisplay) || DeviceTitlePipe.deviceTitleForType(a.device, 'name')
const right = DeviceTitlePipe.deviceTitleForType(b.device, _dashboardDisplay) || DeviceTitlePipe.deviceTitleForType(b.device, 'name')
if( left < right )
return -1;
@@ -39,7 +39,7 @@ export class DeviceSortPipe implements PipeTransform {
}
}
ageCompareFn(a: any, b: any) {
ageCompareFn(a: any, b: any): number {
const left = a.smart?.power_on_hours
const right = b.smart?.power_on_hours
@@ -1,5 +1,5 @@
import { Pipe, PipeTransform } from '@angular/core';
import {formatNumber} from "@angular/common";
import {formatNumber} from '@angular/common';
@Pipe({
name: 'temperature'