diff --git a/Dockerfile.collector b/Dockerfile.collector index b3b15d4..e783d02 100644 --- a/Dockerfile.collector +++ b/Dockerfile.collector @@ -28,4 +28,4 @@ COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny-collector- RUN chmod +x /scrutiny/bin/scrutiny-collector-selftest && \ chmod +x /scrutiny/bin/scrutiny-collector-metrics -CMD ["/usr/lib/x86_64-linux-gnu/jobberrunner", "/scrutiny/config/jobber.yaml"] +CMD ["/usr/lib/x86_64-linux-gnu/jobberrunner", "/scrutiny/jobber/jobber.yaml"] diff --git a/collector/cmd/collector-metrics/collector-metrics.go b/collector/cmd/collector-metrics/collector-metrics.go index 0a90000..135f7f0 100644 --- a/collector/cmd/collector-metrics/collector-metrics.go +++ b/collector/cmd/collector-metrics/collector-metrics.go @@ -3,7 +3,7 @@ package main import ( "fmt" "github.com/analogj/scrutiny/collector/pkg/collector" - "github.com/analogj/scrutiny/collector/pkg/version" + "github.com/analogj/scrutiny/webapp/backend/pkg/version" "github.com/sirupsen/logrus" "log" "os" diff --git a/collector/cmd/collector-selftest/collector-selftest.go b/collector/cmd/collector-selftest/collector-selftest.go index 3a1ab6c..63500d6 100644 --- a/collector/cmd/collector-selftest/collector-selftest.go +++ b/collector/cmd/collector-selftest/collector-selftest.go @@ -3,7 +3,7 @@ package main import ( "fmt" "github.com/analogj/scrutiny/collector/pkg/collector" - "github.com/analogj/scrutiny/collector/pkg/version" + "github.com/analogj/scrutiny/webapp/backend/pkg/version" "github.com/sirupsen/logrus" "log" "os" diff --git a/collector/pkg/collector/base.go b/collector/pkg/collector/base.go index 0e66839..6a2f8f3 100644 --- a/collector/pkg/collector/base.go +++ b/collector/pkg/collector/base.go @@ -4,9 +4,9 @@ import ( "bytes" "encoding/json" "errors" - "fmt" "github.com/analogj/scrutiny/collector/pkg/models" "github.com/jaypipes/ghw" + "github.com/sirupsen/logrus" "io" "net/http" "os" @@ -17,13 +17,15 @@ import ( var httpClient = &http.Client{Timeout: 10 * time.Second} -type BaseCollector struct{} +type BaseCollector struct { + logger *logrus.Entry +} func (c *BaseCollector) detectStorageDevices() ([]models.Device, error) { block, err := ghw.Block() if err != nil { - fmt.Printf("Error getting block storage info: %v", err) + c.logger.Errorf("Error getting block storage info: %v", err) return nil, err } @@ -32,31 +34,31 @@ func (c *BaseCollector) detectStorageDevices() ([]models.Device, error) { // ignore optical drives and floppy disks if disk.DriveType == ghw.DRIVE_TYPE_FDD || disk.DriveType == ghw.DRIVE_TYPE_ODD { - fmt.Printf(" => Ignore: Optical or floppy disk - (found %s)\n", disk.DriveType.String()) + c.logger.Debugf(" => Ignore: Optical or floppy disk - (found %s)\n", disk.DriveType.String()) continue } // ignore removable disks if disk.IsRemovable { - fmt.Printf(" => Ignore: Removable disk (%v)\n", disk.IsRemovable) + c.logger.Debugf(" => Ignore: Removable disk (%v)\n", disk.IsRemovable) continue } // ignore virtual disks & mobile phone storage devices if disk.StorageController == ghw.STORAGE_CONTROLLER_VIRTIO || disk.StorageController == ghw.STORAGE_CONTROLLER_MMC { - fmt.Printf(" => Ignore: Virtual/multi-media storage controller - (found %s)\n", disk.StorageController.String()) + c.logger.Debugf(" => Ignore: Virtual/multi-media storage controller - (found %s)\n", disk.StorageController.String()) continue } // ignore NVMe devices (not currently supported) TBA if disk.StorageController == ghw.STORAGE_CONTROLLER_NVME { - fmt.Printf(" => Ignore: NVMe storage controller - (found %s)\n", disk.StorageController.String()) + c.logger.Debugf(" => Ignore: NVMe storage controller - (found %s)\n", disk.StorageController.String()) continue } // Skip unknown storage controllers, not usually S.M.A.R.T compatible. if disk.StorageController == ghw.STORAGE_CONTROLLER_UNKNOWN { - fmt.Printf(" => Ignore: Unknown storage controller - (found %s)\n", disk.StorageController.String()) + c.logger.Debugf(" => Ignore: Unknown storage controller - (found %s)\n", disk.StorageController.String()) continue } diff --git a/collector/pkg/collector/metrics.go b/collector/pkg/collector/metrics.go index a901dc7..56ea24b 100644 --- a/collector/pkg/collector/metrics.go +++ b/collector/pkg/collector/metrics.go @@ -14,9 +14,7 @@ import ( type MetricsCollector struct { BaseCollector - apiEndpoint *url.URL - logger *logrus.Entry } func CreateMetricsCollector(logger *logrus.Entry, apiEndpoint string) (MetricsCollector, error) { @@ -27,7 +25,9 @@ func CreateMetricsCollector(logger *logrus.Entry, apiEndpoint string) (MetricsCo sc := MetricsCollector{ apiEndpoint: apiEndpointUrl, - logger: logger, + BaseCollector: BaseCollector{ + logger: logger, + }, } return sc, nil @@ -44,8 +44,11 @@ func (mc *MetricsCollector) Run() error { deviceRespWrapper := new(models.DeviceWrapper) detectedStorageDevices, err := mc.detectStorageDevices() + if err != nil { + return err + } - fmt.Println("Sending detected devices to API, for filtering & validation") + mc.logger.Infoln("Sending detected devices to API, for filtering & validation") err = mc.postJson(apiEndpoint.String(), models.DeviceWrapper{ Data: detectedStorageDevices, }, &deviceRespWrapper) @@ -54,10 +57,10 @@ func (mc *MetricsCollector) Run() error { } if !deviceRespWrapper.Success { - //TODO print error payload - fmt.Println("An error occurred while retrieving devices") + mc.logger.Errorln("An error occurred while retrieving filtered devices") + return errors.ApiServerCommunicationError("An error occurred while retrieving filtered devices") } else { - fmt.Println(deviceRespWrapper) + mc.logger.Debugln(deviceRespWrapper) var wg sync.WaitGroup for _, device := range deviceRespWrapper.Data { @@ -66,16 +69,16 @@ func (mc *MetricsCollector) Run() error { go mc.Collect(&wg, device.WWN, device.DeviceName) } - fmt.Println("Main: Waiting for workers to finish") + mc.logger.Infoln("Main: Waiting for workers to finish") wg.Wait() - fmt.Println("Main: Completed") + mc.logger.Infoln("Main: Completed") } return nil } func (mc *MetricsCollector) Validate() error { - fmt.Println("Verifying required tools") + mc.logger.Infoln("Verifying required tools") _, lookErr := exec.LookPath("smartctl") if lookErr != nil { @@ -87,14 +90,14 @@ func (mc *MetricsCollector) Validate() error { func (mc *MetricsCollector) Collect(wg *sync.WaitGroup, deviceWWN string, deviceName string) { defer wg.Done() - fmt.Printf("Collecting smartctl results for %s\n", deviceName) + mc.logger.Infof("Collecting smartctl results for %s\n", deviceName) result, err := mc.execCmd("smartctl", []string{"-a", "-j", fmt.Sprintf("/dev/%s", deviceName)}, "", nil) resultBytes := []byte(result) if err != nil { - fmt.Printf("error while retrieving data from smartctl %s\n", deviceName) - fmt.Printf("ERROR MESSAGE: %v", err) - fmt.Printf("RESULT: %v", result) + mc.logger.Errorf("error while retrieving data from smartctl %s\n", deviceName) + mc.logger.Errorf("ERROR MESSAGE: %v", err) + mc.logger.Errorf("RESULT: %v", result) // TODO: error while retrieving data from smartctl. // TODO: we should pass this data on to scrutiny API for recording. return @@ -105,7 +108,7 @@ func (mc *MetricsCollector) Collect(wg *sync.WaitGroup, deviceWWN string, device } func (mc *MetricsCollector) Publish(deviceWWN string, payload []byte) error { - fmt.Printf("Publishing smartctl results for %s\n", deviceWWN) + mc.logger.Infof("Publishing smartctl results for %s\n", deviceWWN) apiEndpoint, _ := url.Parse(mc.apiEndpoint.String()) apiEndpoint.Path = fmt.Sprintf("/api/device/%s/smart", strings.ToLower(deviceWWN)) diff --git a/collector/pkg/errors/errors.go b/collector/pkg/errors/errors.go index 87823e0..5661cdf 100644 --- a/collector/pkg/errors/errors.go +++ b/collector/pkg/errors/errors.go @@ -24,3 +24,10 @@ type DependencyMissingError string func (str DependencyMissingError) Error() string { return fmt.Sprintf("DependencyMissingError: %q", string(str)) } + +// Raised when there was an error communicating with API server +type ApiServerCommunicationError string + +func (str ApiServerCommunicationError) Error() string { + return fmt.Sprintf("ApiServerCommunicationError: %q", string(str)) +} diff --git a/collector/pkg/models/selftest.go b/collector/pkg/models/selftest.go deleted file mode 100644 index 7220847..0000000 --- a/collector/pkg/models/selftest.go +++ /dev/null @@ -1,8 +0,0 @@ -package models - -//type SelfTest struct { -// DeviceWWN string -// Device Device `gorm:"foreignkey:DeviceWWN"` // use DeviceWWN as foreign key -// -// TestDate time.Time -//} diff --git a/collector/pkg/models/smart.go b/collector/pkg/models/smart.go deleted file mode 100644 index c99ab6d..0000000 --- a/collector/pkg/models/smart.go +++ /dev/null @@ -1,14 +0,0 @@ -package models - -//type Smart struct { -// DeviceWWN string -// Device Device `gorm:"foreignkey:DeviceWWN"` // use DeviceWWN as foreign key -// -// TestDate time.Time -// -// Temp float32 -// PowerOnCount int64 -// PowerOnHours int64 -// SmartStatus string -// SmartAttributes string -//} diff --git a/collector/pkg/version/version.go b/collector/pkg/version/version.go deleted file mode 100644 index 4ce3f31..0000000 --- a/collector/pkg/version/version.go +++ /dev/null @@ -1,5 +0,0 @@ -package version - -// VERSION is the app-global version string, which will be replaced with a -// new value during packaging -const VERSION = "0.1.2" diff --git a/rootfs/etc/services.d/jobber/run b/rootfs/etc/services.d/jobber/run index 8c834aa..0408f73 100644 --- a/rootfs/etc/services.d/jobber/run +++ b/rootfs/etc/services.d/jobber/run @@ -2,4 +2,4 @@ echo "starting jobber/cron" -su -c "/usr/lib/x86_64-linux-gnu/jobberrunner /scrutiny/config/jobber.yaml" root +su -c "/usr/lib/x86_64-linux-gnu/jobberrunner /scrutiny/jobber/jobber.yaml" root diff --git a/rootfs/scrutiny/config/jobber.yaml b/rootfs/scrutiny/jobber/jobber.yaml similarity index 100% rename from rootfs/scrutiny/config/jobber.yaml rename to rootfs/scrutiny/jobber/jobber.yaml diff --git a/webapp/backend/pkg/models/testdata/scsi-smart.json b/webapp/backend/pkg/models/testdata/scsi-smart.json new file mode 100644 index 0000000..fe0cfb7 --- /dev/null +++ b/webapp/backend/pkg/models/testdata/scsi-smart.json @@ -0,0 +1,64 @@ +{ + "device": { + "name": "/dev/sdg", + "info_name": "/dev/sdg", + "type": "scsi", + "protocol": "SCSI" + }, + "vendor": "SEAGATE", + "product": "ST4000NM0043", + "model_name": "SEAGATE ST4000NM0043", + "revision": "MS03", + "scsi_version": "SPC-4", + "user_capacity": { + "blocks": 7814037168, + "bytes": 4000787030016 + }, + "logical_block_size": 512, + "rotation_rate": 7200, + "form_factor": { + "scsi_value": 2, + "name": "3.5 inches" + }, + "serial_number": "Z1Z5DWJK0000XXXXXXXX", + "device_type": { + "scsi_value": 0, + "name": "disk" + }, + "local_time": { + "time_t": 1598048822, + "asctime": "Fri Aug 21 22:27:02 2020 UTC" + }, + "smart_status": { + "passed": true + }, + "temperature": { + "current": 34, + "drive_trip": 68 + }, + "scsi_grown_defect_list": 56, + "power_on_time": { + "hours": 43549, + "minutes": 33 + }, + "scsi_error_counter_log": { + "read": { + "errors_corrected_by_eccfast": 300357663, + "errors_corrected_by_eccdelayed": 0, + "errors_corrected_by_rereads_rewrites": 0, + "total_errors_corrected": 300357663, + "correction_algorithm_invocations": 0, + "gigabytes_processed": "176987.332", + "total_uncorrected_errors": 0 + }, + "write": { + "errors_corrected_by_eccfast": 0, + "errors_corrected_by_eccdelayed": 0, + "errors_corrected_by_rereads_rewrites": 0, + "total_errors_corrected": 0, + "correction_algorithm_invocations": 0, + "gigabytes_processed": "86472.611", + "total_uncorrected_errors": 0 + } + } +} diff --git a/webapp/backend/pkg/version/version.go b/webapp/backend/pkg/version/version.go index 3e8773b..4ce3f31 100644 --- a/webapp/backend/pkg/version/version.go +++ b/webapp/backend/pkg/version/version.go @@ -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.1.0" +const VERSION = "0.1.2" diff --git a/webapp/backend/pkg/web/cron.go b/webapp/backend/pkg/web/cron.go deleted file mode 100644 index d1e0a8f..0000000 --- a/webapp/backend/pkg/web/cron.go +++ /dev/null @@ -1,8 +0,0 @@ -package web - -// the following cronjobs need to be defined here: -// - get storage information for all approved disks -// - run short test against approved disks -// - run long test against approved disks -// - get S.M.A.R.T. metrics from approved disks -// - clean up / resolution for time-series data in sqlite database. diff --git a/webapp/backend/pkg/web/handler/get_device_details.go b/webapp/backend/pkg/web/handler/get_device_details.go new file mode 100644 index 0000000..8c7980c --- /dev/null +++ b/webapp/backend/pkg/web/handler/get_device_details.go @@ -0,0 +1,27 @@ +package handler + +import ( + "github.com/analogj/scrutiny/webapp/backend/pkg/metadata" + dbModels "github.com/analogj/scrutiny/webapp/backend/pkg/models/db" + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" + "net/http" +) + +func GetDeviceDetails(c *gin.Context) { + db := c.MustGet("DB").(*gorm.DB) + device := dbModels.Device{} + + db.Debug(). + Preload("SmartResults", func(db *gorm.DB) *gorm.DB { + return db.Order("smarts.created_at DESC").Limit(40) + }). + Preload("SmartResults.SmartAttributes"). + Where("wwn = ?", c.Param("wwn")). + First(&device) + + device.SquashHistory() + device.ApplyMetadataRules() + + c.JSON(http.StatusOK, gin.H{"success": true, "data": device, "lookup": metadata.AtaSmartAttributes}) +} diff --git a/webapp/backend/pkg/web/handler/get_devices_summary.go b/webapp/backend/pkg/web/handler/get_devices_summary.go new file mode 100644 index 0000000..e2e19e9 --- /dev/null +++ b/webapp/backend/pkg/web/handler/get_devices_summary.go @@ -0,0 +1,26 @@ +package handler + +import ( + dbModels "github.com/analogj/scrutiny/webapp/backend/pkg/models/db" + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" + "net/http" +) + +func GetDevicesSummary(c *gin.Context) { + db := c.MustGet("DB").(*gorm.DB) + devices := []dbModels.Device{} + + //We need the last x (for now all) Smart objects for each Device, so that we can graph Temperature + //We also need the last + db.Debug(). + Preload("SmartResults", func(db *gorm.DB) *gorm.DB { + return db.Order("smarts.created_at DESC") //OLD: .Limit(devicesCount) + }). + Find(&devices) + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "data": devices, + }) +} diff --git a/webapp/backend/pkg/web/handler/register_devices.go b/webapp/backend/pkg/web/handler/register_devices.go new file mode 100644 index 0000000..963f08b --- /dev/null +++ b/webapp/backend/pkg/web/handler/register_devices.go @@ -0,0 +1,39 @@ +package handler + +import ( + dbModels "github.com/analogj/scrutiny/webapp/backend/pkg/models/db" + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" + log "github.com/sirupsen/logrus" + "net/http" +) + +// filter devices that are detected by various collectors. +func RegisterDevices(c *gin.Context) { + db := c.MustGet("DB").(*gorm.DB) + + var collectorDeviceWrapper dbModels.DeviceWrapper + err := c.BindJSON(&collectorDeviceWrapper) + if err != nil { + log.Error("Cannot parse detected devices") + c.JSON(http.StatusOK, gin.H{"success": false}) + } + + //TODO: filter devices here (remove excludes, force includes) + + for _, dev := range collectorDeviceWrapper.Data { + //insert devices into DB if not already there. + db.Where(dbModels.Device{WWN: dev.WWN}).FirstOrCreate(&dev) + } + + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + }) + } else { + c.JSON(http.StatusOK, dbModels.DeviceWrapper{ + Success: true, + Data: collectorDeviceWrapper.Data, + }) + } +} diff --git a/webapp/backend/pkg/web/handler/upload_device_metrics.go b/webapp/backend/pkg/web/handler/upload_device_metrics.go new file mode 100644 index 0000000..23fe3a6 --- /dev/null +++ b/webapp/backend/pkg/web/handler/upload_device_metrics.go @@ -0,0 +1,40 @@ +package handler + +import ( + "github.com/analogj/scrutiny/webapp/backend/pkg/models/collector" + dbModels "github.com/analogj/scrutiny/webapp/backend/pkg/models/db" + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" + log "github.com/sirupsen/logrus" + "net/http" +) + +func UploadDeviceMetrics(c *gin.Context) { + db := c.MustGet("DB").(*gorm.DB) + + var collectorSmartData collector.SmartInfo + err := c.BindJSON(&collectorSmartData) + if err != nil { + //TODO: cannot parse smart data + log.Error("Cannot parse SMART data") + c.JSON(http.StatusOK, gin.H{"success": false}) + + } + + //update the device information if necessary + var device dbModels.Device + db.Where("wwn = ?", c.Param("wwn")).First(&device) + device.UpdateFromCollectorSmartInfo(collectorSmartData) + db.Model(&device).Updates(device) + + // insert smart info + deviceSmartData := dbModels.Smart{} + err = deviceSmartData.FromCollectorSmartInfo(c.Param("wwn"), collectorSmartData) + if err != nil { + c.JSON(http.StatusOK, gin.H{"success": false}) + return + } + db.Create(&deviceSmartData) + + c.JSON(http.StatusOK, gin.H{"success": true}) +} diff --git a/webapp/backend/pkg/web/handler/upload_device_self_tests.go b/webapp/backend/pkg/web/handler/upload_device_self_tests.go new file mode 100644 index 0000000..9307212 --- /dev/null +++ b/webapp/backend/pkg/web/handler/upload_device_self_tests.go @@ -0,0 +1,7 @@ +package handler + +import "github.com/gin-gonic/gin" + +func UploadDeviceSelfTests(c *gin.Context) { + +} diff --git a/webapp/backend/pkg/web/server.go b/webapp/backend/pkg/web/server.go index 95fc319..7789db1 100644 --- a/webapp/backend/pkg/web/server.go +++ b/webapp/backend/pkg/web/server.go @@ -4,12 +4,8 @@ import ( "fmt" "github.com/analogj/scrutiny/webapp/backend/pkg/config" "github.com/analogj/scrutiny/webapp/backend/pkg/database" - "github.com/analogj/scrutiny/webapp/backend/pkg/metadata" - "github.com/analogj/scrutiny/webapp/backend/pkg/models/collector" - dbModels "github.com/analogj/scrutiny/webapp/backend/pkg/models/db" + "github.com/analogj/scrutiny/webapp/backend/pkg/web/handler" "github.com/gin-gonic/gin" - "github.com/jinzhu/gorm" - log "github.com/sirupsen/logrus" "net/http" ) @@ -30,13 +26,12 @@ func (ae *AppEngine) Start() error { }) }) - //TODO: notifications - api.POST("/devices/register", RegisterDevices) - api.GET("/summary", GetDevicesSummary) - api.POST("/device/:wwn/smart", UploadDeviceSmartData) - api.POST("/device/:wwn/selftest", UploadDeviceSelfTestData) + api.POST("/devices/register", handler.RegisterDevices) + api.GET("/summary", handler.GetDevicesSummary) + api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) + api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests) - api.GET("/device/:wwn/details", GetDeviceDetails) + api.GET("/device/:wwn/details", handler.GetDeviceDetails) } //Static request routing @@ -52,124 +47,5 @@ func (ae *AppEngine) Start() error { c.File(fmt.Sprintf("%s/index.html", ae.Config.GetString("web.src.frontend.path"))) }) - return r.Run(fmt.Sprintf("%s:%s", ae.Config.GetString("web.listen.host"), ae.Config.GetString("web.listen.port"))) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") -} - -// filter devices that are detected by various collectors. -func RegisterDevices(c *gin.Context) { - db := c.MustGet("DB").(*gorm.DB) - - var collectorDeviceWrapper dbModels.DeviceWrapper - err := c.BindJSON(&collectorDeviceWrapper) - if err != nil { - log.Error("Cannot parse detected devices") - c.JSON(http.StatusOK, gin.H{"success": false}) - } - - //TODO: filter devices here (remove excludes, force includes) - - for _, dev := range collectorDeviceWrapper.Data { - //insert devices into DB if not already there. - db.Where(dbModels.Device{WWN: dev.WWN}).FirstOrCreate(&dev) - } - - if err != nil { - c.JSON(http.StatusOK, gin.H{ - "success": false, - }) - } else { - c.JSON(http.StatusOK, dbModels.DeviceWrapper{ - Success: true, - Data: collectorDeviceWrapper.Data, - }) - } -} - -func UploadDeviceSmartData(c *gin.Context) { - db := c.MustGet("DB").(*gorm.DB) - - var collectorSmartData collector.SmartInfo - err := c.BindJSON(&collectorSmartData) - if err != nil { - //TODO: cannot parse smart data - log.Error("Cannot parse SMART data") - c.JSON(http.StatusOK, gin.H{"success": false}) - - } - - //update the device information if necessary - var device dbModels.Device - db.Where("wwn = ?", c.Param("wwn")).First(&device) - device.UpdateFromCollectorSmartInfo(collectorSmartData) - db.Model(&device).Updates(device) - - // insert smart info - deviceSmartData := dbModels.Smart{} - err = deviceSmartData.FromCollectorSmartInfo(c.Param("wwn"), collectorSmartData) - if err != nil { - c.JSON(http.StatusOK, gin.H{"success": false}) - return - } - db.Create(&deviceSmartData) - - c.JSON(http.StatusOK, gin.H{"success": true}) -} - -func UploadDeviceSelfTestData(c *gin.Context) { - -} - -func GetDeviceDetails(c *gin.Context) { - db := c.MustGet("DB").(*gorm.DB) - device := dbModels.Device{} - - db.Debug(). - Preload("SmartResults", func(db *gorm.DB) *gorm.DB { - return db.Order("smarts.created_at DESC").Limit(40) - }). - Preload("SmartResults.SmartAttributes"). - Where("wwn = ?", c.Param("wwn")). - First(&device) - - device.SquashHistory() - device.ApplyMetadataRules() - - c.JSON(http.StatusOK, gin.H{"success": true, "data": device, "lookup": metadata.AtaSmartAttributes}) - -} - -func GetDevicesSummary(c *gin.Context) { - db := c.MustGet("DB").(*gorm.DB) - - devices := []dbModels.Device{} - - //OLD: cant seem to figure out how to get the latest SmartResults for each Device, so instead - // we're going to assume that results were retrieved at the same time, so we'll just get the last x number of results - //var devicesCount int - //db.Table("devices").Count(&devicesCount) - - //We need the last x (for now all) Smart objects for each Device, so that we can graph Temperature - //We also need the last - db.Debug(). - Preload("SmartResults", func(db *gorm.DB) *gorm.DB { - return db.Order("smarts.created_at DESC") //OLD: .Limit(devicesCount) - }). - //Preload("SmartResults"). - // Preload("SmartResults.SmartAttributes"). - Find(&devices) - - //for _, dev := range devices { - // log.Printf("===== device: %s\n", dev.WWN) - // log.Print(len(dev.SmartResults)) - //} - //a, _ := json.Marshal(devices) //get json byte array - //n := len(a) //Find the length of the byte array - //s := string(a[:n]) //convert to string - //log.Print(s) //write to response - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "data": devices, - }) - //c.Data(http.StatusOK, "application/json", a) + return r.Run(fmt.Sprintf("%s:%s", ae.Config.GetString("web.listen.host"), ae.Config.GetString("web.listen.port"))) } diff --git a/webapp/frontend/src/app/modules/admin/dashboard/dashboard.component.html b/webapp/frontend/src/app/modules/admin/dashboard/dashboard.component.html index 22ab758..4817b6a 100644 --- a/webapp/frontend/src/app/modules/admin/dashboard/dashboard.component.html +++ b/webapp/frontend/src/app/modules/admin/dashboard/dashboard.component.html @@ -11,12 +11,14 @@
- - - -