added a way to retrieve raw udev data. Can be used to retrieve disk label, UUID and "disk/by-id/*" device info.

Storing it in the database during device registration.
pull/262/head
Jason Kulatunga 2 years ago
parent 6f283fd736
commit 999c12748c

@ -13,7 +13,7 @@ func DevicePrefix() string {
func (d *Detect) Start() ([]models.Device, error) { func (d *Detect) Start() ([]models.Device, error) {
d.Shell = shell.Create() d.Shell = shell.Create()
// call the base/common functionality to get a list of devicess // call the base/common functionality to get a list of devices
detectedDevices, err := d.SmartctlScan() detectedDevices, err := d.SmartctlScan()
if err != nil { if err != nil {
return nil, err return nil, err

@ -1,9 +1,12 @@
package detect package detect
import ( import (
"fmt"
"github.com/analogj/scrutiny/collector/pkg/common/shell" "github.com/analogj/scrutiny/collector/pkg/common/shell"
"github.com/analogj/scrutiny/collector/pkg/models" "github.com/analogj/scrutiny/collector/pkg/models"
"github.com/jaypipes/ghw" "github.com/jaypipes/ghw"
"io/ioutil"
"path/filepath"
"strings" "strings"
) )
@ -22,6 +25,7 @@ func (d *Detect) Start() ([]models.Device, error) {
//inflate device info for detected devices. //inflate device info for detected devices.
for ndx, _ := range detectedDevices { for ndx, _ := range detectedDevices {
d.SmartCtlInfo(&detectedDevices[ndx]) //ignore errors. d.SmartCtlInfo(&detectedDevices[ndx]) //ignore errors.
populateUdevInfo(&detectedDevices[ndx]) //ignore errors.
} }
return detectedDevices, nil return detectedDevices, nil
@ -49,3 +53,51 @@ func (d *Detect) wwnFallback(detectedDevice *models.Device) {
//wwn must always be lowercase. //wwn must always be lowercase.
detectedDevice.WWN = strings.ToLower(detectedDevice.WWN) detectedDevice.WWN = strings.ToLower(detectedDevice.WWN)
} }
// as discussed in
// - https://github.com/AnalogJ/scrutiny/issues/225
// - https://github.com/jaypipes/ghw/issues/59#issue-361915216
// udev exposes its data in a standardized way under /run/udev/data/....
func populateUdevInfo(detectedDevice *models.Device) error {
// Get device major:minor numbers
// `cat /sys/class/block/sda/dev`
devNo, err := ioutil.ReadFile(filepath.Join("/sys/class/block/", detectedDevice.DeviceName, "dev"))
if err != nil {
return err
}
// Look up block device in udev runtime database
// `cat /run/udev/data/b8:0`
udevID := "b" + strings.TrimSpace(string(devNo))
udevBytes, err := ioutil.ReadFile(filepath.Join("/run/udev/data/", udevID))
if err != nil {
return err
}
deviceMountPaths := []string{}
udevInfo := make(map[string]string)
for _, udevLine := range strings.Split(string(udevBytes), "\n") {
if strings.HasPrefix(udevLine, "E:") {
if s := strings.SplitN(udevLine[2:], "=", 2); len(s) == 2 {
udevInfo[s[0]] = s[1]
}
} else if strings.HasPrefix(udevLine, "S:") {
deviceMountPaths = append(deviceMountPaths, udevLine[2:])
}
}
//Set additional device information.
if deviceLabel, exists := udevInfo["ID_FS_LABEL"]; exists {
detectedDevice.DeviceLabel = deviceLabel
}
if deviceUUID, exists := udevInfo["ID_FS_UUID"]; exists {
detectedDevice.DeviceUUID = deviceUUID
}
if deviceSerialID, exists := udevInfo["ID_SERIAL"]; exists {
detectedDevice.DeviceSerialID = fmt.Sprintf("%s-%s", udevInfo["ID_BUS"], deviceSerialID)
}
return nil
}

@ -1,10 +1,13 @@
package models package models
type Device struct { type Device struct {
WWN string `json:"wwn"` WWN string `json:"wwn"`
HostId string `json:"host_id"`
DeviceName string `json:"device_name"` DeviceName string `json:"device_name"`
DeviceUUID string `json:"device_uuid"`
DeviceSerialID string `json:"device_serial_id"`
DeviceLabel string `json:"device_label"`
Manufacturer string `json:"manufacturer"` Manufacturer string `json:"manufacturer"`
ModelName string `json:"model_name"` ModelName string `json:"model_name"`
InterfaceType string `json:"interface_type"` InterfaceType string `json:"interface_type"`
@ -17,6 +20,10 @@ type Device struct {
SmartSupport bool `json:"smart_support"` SmartSupport bool `json:"smart_support"`
DeviceProtocol string `json:"device_protocol"` //protocol determines which smart attribute types are available (ATA, NVMe, SCSI) DeviceProtocol string `json:"device_protocol"` //protocol determines which smart attribute types are available (ATA, NVMe, SCSI)
DeviceType string `json:"device_type"` //device type is used for querying with -d/t flag, should only be used by collector. DeviceType string `json:"device_type"` //device type is used for querying with -d/t flag, should only be used by collector.
// User provided metadata
Label string `json:"label"`
HostId string `json:"host_id"`
} }
type DeviceWrapper struct { type DeviceWrapper struct {

@ -5,6 +5,7 @@ import (
"time" "time"
) )
// Deprecated: m20220503120000.Device is deprecated, only used by db migrations
type Device struct { type Device struct {
//GORM attributes, see: http://gorm.io/docs/conventions.html //GORM attributes, see: http://gorm.io/docs/conventions.html
CreatedAt time.Time CreatedAt time.Time

@ -18,7 +18,7 @@ import (
func (sr *scrutinyRepository) RegisterDevice(ctx context.Context, dev models.Device) error { func (sr *scrutinyRepository) RegisterDevice(ctx context.Context, dev models.Device) error {
if err := sr.gormClient.WithContext(ctx).Clauses(clause.OnConflict{ if err := sr.gormClient.WithContext(ctx).Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "wwn"}}, Columns: []clause.Column{{Name: "wwn"}},
DoUpdates: clause.AssignmentColumns([]string{"host_id", "device_name", "device_type"}), DoUpdates: clause.AssignmentColumns([]string{"host_id", "device_name", "device_type", "device_uuid", "device_serial_id", "device_label"}),
}).Create(&dev).Error; err != nil { }).Create(&dev).Error; err != nil {
return err return err
} }

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20201107210306" "github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20201107210306"
"github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20220503120000" "github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20220503120000"
"github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20220509170100"
"github.com/analogj/scrutiny/webapp/backend/pkg/models" "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/collector"
"github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements" "github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements"
@ -257,10 +258,19 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error {
return err return err
} }
//migrate the device database to the current version //migrate the device database
return tx.AutoMigrate(m20220503120000.Device{}) return tx.AutoMigrate(m20220503120000.Device{})
}, },
}, },
{
ID: "m20220509170100", // addl udev device data
Migrate: func(tx *gorm.DB) error {
//migrate the device database.
// adding addl columns (device_label, device_uuid, device_serial_id)
return tx.AutoMigrate(m20220509170100.Device{})
},
},
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {

@ -21,6 +21,10 @@ type Device struct {
WWN string `json:"wwn" gorm:"primary_key"` WWN string `json:"wwn" gorm:"primary_key"`
DeviceName string `json:"device_name"` DeviceName string `json:"device_name"`
DeviceUUID string `json:"device_uuid"`
DeviceSerialID string `json:"device_serial_id"`
DeviceLabel string `json:"device_label"`
Manufacturer string `json:"manufacturer"` Manufacturer string `json:"manufacturer"`
ModelName string `json:"model_name"` ModelName string `json:"model_name"`
InterfaceType string `json:"interface_type"` InterfaceType string `json:"interface_type"`

Loading…
Cancel
Save