@@ -6,6 +6,9 @@ package linux
66import (
77 "fmt"
88 "io"
9+ "os"
10+ "path"
11+ "path/filepath"
912 "strconv"
1013 "strings"
1114
@@ -22,6 +25,49 @@ type scsiDeviceInfo struct {
2225type scsiDevices = []scsiDeviceInfo
2326
2427func (c * LinuxDeviceManager ) listScsiDevices () ([]scsiDeviceInfo , error ) {
28+ devices , err := c .listScsiDevicesFromSys ()
29+ if err == nil {
30+ return devices , nil
31+ }
32+
33+ log .Warn ().Err (err ).Msg ("failed to list scsi devices from sys, trying lsscsi" )
34+ return c .listScsiDevicesFromCLI ()
35+ }
36+
37+ func (c * LinuxDeviceManager ) listScsiDevicesFromSys () ([]scsiDeviceInfo , error ) {
38+ blocks , err := os .ReadDir ("/sys/block/" )
39+ if err != nil {
40+ return nil , err
41+ }
42+ scsiDevices := []scsiDeviceInfo {}
43+ for _ , block := range blocks {
44+ if block .Type () != os .ModeSymlink {
45+ continue
46+ }
47+
48+ entry , err := os .Readlink (path .Join ("/sys/block/" , block .Name ()))
49+ if err != nil {
50+ log .Warn ().Err (err ).Str ("block" , block .Name ()).Msg ("failed to readlink" )
51+ continue
52+ }
53+ entry , err = filepath .Abs (path .Join ("/sys/block/" , entry ))
54+ if err != nil {
55+ log .Warn ().Err (err ).Str ("block" , block .Name ()).Msg ("failed to get absolute path" )
56+ continue
57+ }
58+
59+ device , err := parseScsiDevicePath (entry )
60+ if err != nil {
61+ log .Debug ().Err (err ).Str ("block" , block .Name ()).Msg ("failed to parse device path" )
62+ continue
63+ }
64+ scsiDevices = append (scsiDevices , device )
65+ }
66+
67+ return scsiDevices , nil
68+ }
69+
70+ func (c * LinuxDeviceManager ) listScsiDevicesFromCLI () ([]scsiDeviceInfo , error ) {
2571 cmd , err := c .cmdRunner .RunCommand ("lsscsi --brief" )
2672 if err != nil {
2773 return nil , err
@@ -83,3 +129,29 @@ func parseLsscsiOutput(output string) (scsiDevices, error) {
83129
84130 return mountedDevices , nil
85131}
132+
133+ // parses the device path from the sysfs block device path, e.g. /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/host2/target2:0:0/2:0:0:0/block/sdb
134+ // and returns the LUN and the path to the device, e.g. 0 and /dev/sdb
135+ func parseScsiDevicePath (entry string ) (res scsiDeviceInfo , err error ) {
136+ chunks := strings .Split (entry , "/" )
137+ if len (chunks ) < 5 {
138+ return res , fmt .Errorf ("unexpected entry: %s" , entry )
139+ }
140+
141+ if chunks [len (chunks )- 2 ] != "block" {
142+ return res , fmt .Errorf ("unexpected entry, expected block: %s" , entry )
143+ }
144+
145+ res .VolumePath = path .Join ("/dev" , chunks [len (chunks )- 1 ])
146+ hbtl := strings .Split (chunks [len (chunks )- 3 ], ":" )
147+ if len (hbtl ) != 4 {
148+ return res , fmt .Errorf ("unexpected entry, expected 4 fields for H:B:T:L: %s" , entry )
149+ }
150+
151+ res .Lun , err = strconv .Atoi (hbtl [3 ])
152+ if err != nil {
153+ return res , fmt .Errorf ("unexpected entry, expected integer for LUN: %s" , entry )
154+ }
155+
156+ return res , nil
157+ }
0 commit comments