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.
This commit is contained in:
@@ -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"`
|
||||||
|
|||||||
Reference in New Issue
Block a user