@@ -18,22 +18,23 @@ limitations under the License.
1818package driver
1919
2020import (
21+ "context"
2122 "encoding/json"
2223 "errors"
2324 "fmt"
24- "io/ioutil"
25- "k8s.io/mount-utils"
26- kexec "k8s.io/utils/exec"
2725 "os"
2826 "os/exec"
2927 "path/filepath"
3028 "strconv"
3129 "strings"
30+ "sync"
3231 "syscall"
3332 "time"
3433
3534 "github.com/sirupsen/logrus"
3635 "golang.org/x/sys/unix"
36+ "k8s.io/mount-utils"
37+ kexec "k8s.io/utils/exec"
3738)
3839
3940const (
@@ -86,7 +87,7 @@ type Mounter interface {
8687
8788 // Used to find a path in /dev/disk/by-id with a serial that we have from
8889 // the cloudscale API.
89- FinalizeVolumeAttachmentAndFindPath (logger * logrus.Entry , VolumeId string ) (* string , error )
90+ FinalizeVolumeAttachmentAndFindPath (logger * logrus.Entry , VolumeId string ) (string , error )
9091
9192 // GetStatistics returns capacity-related volume statistics for the given
9293 // volume path.
@@ -221,7 +222,29 @@ func (m *mounter) Mount(source, target, fsType string, luksContext LuksContext,
221222 source = luksSource
222223 }
223224
225+ // Resolve source symlink for debug logging
226+ resolvedSource , resolveErr := filepath .EvalSymlinks (source )
227+ if resolveErr != nil {
228+ m .log .WithFields (logrus.Fields {
229+ "source" : source ,
230+ "target" : target ,
231+ "fs_type" : fsType ,
232+ "options" : options ,
233+ "resolve_error" : resolveErr ,
234+ }).Debug ("Mount: failed to resolve source symlink" )
235+ } else {
236+ m .log .WithFields (logrus.Fields {
237+ "source" : source ,
238+ "resolved_source" : resolvedSource ,
239+ "target" : target ,
240+ "fs_type" : fsType ,
241+ "options" : options ,
242+ }).Debug ("Mount: resolved source device" )
243+ }
244+
224245 m .log .WithFields (logrus.Fields {
246+ "source" : source ,
247+ "target" : target ,
225248 "options" : options ,
226249 }).Info ("executing mount command" )
227250 err := m .kMounter .Mount (source , target , fsType , options )
@@ -418,72 +441,111 @@ See the License for the specific language governing permissions and
418441limitations under the License.
419442*/
420443
421- func guessDiskIDPathByVolumeID (volumeID string ) * string {
444+ func guessDiskIDPathByVolumeID (volumeID string , logger * logrus. Entry ) string {
422445 // Get the first part of the UUID.
423446 // The linux kernel limits volume serials to 20 bytes:
424447 // include/uapi/linux/virtio_blk.h:#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */
425448 linuxSerial := volumeID [:20 ]
426449
427450 globExpr := diskIDPath + "/*" + linuxSerial + "*"
428451 matches , _ := filepath .Glob (globExpr )
452+
453+ logger .WithFields (logrus.Fields {
454+ "volumeID" : volumeID ,
455+ "linuxSerial" : linuxSerial ,
456+ "matches" : matches ,
457+ }).Debug ("guessDiskIDPathByVolumeID" )
458+
429459 if len (matches ) > 0 {
430- return & matches [0 ]
460+ return matches [0 ]
431461 }
432- return nil
462+ return ""
433463}
434464
435- func (m * mounter ) FinalizeVolumeAttachmentAndFindPath (logger * logrus.Entry , volumeID string ) (* string , error ) {
465+ func (m * mounter ) FinalizeVolumeAttachmentAndFindPath (logger * logrus.Entry , volumeID string ) (string , error ) {
436466 numTries := 0
437467 for {
438- probeAttachedVolume (logger )
439-
440- diskIDPath := guessDiskIDPathByVolumeID (volumeID )
441- if diskIDPath != nil {
468+ diskIDPath := guessDiskIDPathByVolumeID (volumeID , logger )
469+ if diskIDPath != "" {
470+ // Resolve and log the actual device for debugging
471+ resolved , err := filepath .EvalSymlinks (diskIDPath )
472+ if err != nil {
473+ logger .WithFields (logrus.Fields {
474+ "disk_id_path" : diskIDPath ,
475+ "error" : err ,
476+ }).Debug ("FinalizeVolumeAttachmentAndFindPath: found path but failed to resolve symlink" )
477+ } else {
478+ logger .WithFields (logrus.Fields {
479+ "disk_id_path" : diskIDPath ,
480+ "resolved_device" : resolved ,
481+ "num_tries" : numTries ,
482+ }).Debug ("FinalizeVolumeAttachmentAndFindPath: found device path" )
483+ }
442484 return diskIDPath , nil
443485 }
444486
487+ logger .WithFields (logrus.Fields {
488+ "num_tries" : numTries ,
489+ }).Debug ("FinalizeVolumeAttachmentAndFindPath: device not found, probing" )
490+
491+ probeAttachedVolume (logger )
492+
445493 numTries ++
446- if numTries == 10 {
494+ if numTries == 30 {
447495 break
448496 }
449497 time .Sleep (time .Second )
450498 }
451- return nil , errors .New ("Could not attach disk : Timeout after 10s" )
499+ return "" , errors .New ("FinalizeVolumeAttachmentAndFindPath : Timeout after 10s" )
452500}
453501
454- func probeAttachedVolume ( logger * logrus.Entry ) error {
455- // rescan scsi bus
456- scsiHostRescan ()
502+ func runCmdWithTimeout ( name string , args [] string , logger * logrus.Entry , timeout time. Duration ) {
503+ ctx , cancel := context . WithTimeout ( context . Background (), timeout )
504+ defer cancel ()
457505
458- // udevadm settle waits for udevd to process the device creation
459- // events for all hardware devices, thus ensuring that any device
460- // nodes have been created successfully before proceeding.
461- argsSettle := []string {"settle" }
462- cmdSettle := exec .Command ("udevadm" , argsSettle ... )
463- _ , errSettle := cmdSettle .CombinedOutput ()
464- if errSettle != nil {
465- logger .Errorf ("error running udevadm settle %v\n " , errSettle )
506+ out , err := exec .CommandContext (ctx , name , args ... ).CombinedOutput ()
507+ if err != nil {
508+ logger .WithError (err ).
509+ WithFields (logrus.Fields {"out" : out , "name" : name , "args" : args }).
510+ Warn ("unable to run cmd " + name )
466511 }
512+ }
513+
514+ var probeLock sync.Mutex
515+
516+ func probeAttachedVolume (logger * logrus.Entry ) {
517+ const triggerTimeout = 15 * time .Second
518+
519+ // host rescan and udevadm are global actions and if run concurrently, may run into issues with
520+ // symlinking and partial updates.
521+ probeLock .Lock ()
522+ defer probeLock .Unlock ()
523+
524+ // rescan scsi bus
525+ logger .Debug ("probeAttachedVolume: rescanning SCSI hosts" )
526+ scsiHostRescan (logger )
467527
468- args := []string {"trigger" }
469- cmd := exec .Command ("udevadm" , args ... )
470- _ , err := cmd .CombinedOutput ()
528+ logger .Debug ("probeAttachedVolume: running udevadm trigger" )
529+ runCmdWithTimeout ("udevadm" , []string {"trigger" }, logger , triggerTimeout )
530+
531+ logger .Debug ("probeAttachedVolume: running udevadm settle" )
532+ runCmdWithTimeout ("udevadm" , []string {"settle" }, logger , triggerTimeout )
533+
534+ logger .Debugf ("probeAttachedVolume: done" )
535+ }
536+
537+ func scsiHostRescan (logger * logrus.Entry ) {
538+ const scsiPath = "/sys/class/scsi_host/"
539+ dirs , err := os .ReadDir (scsiPath )
471540 if err != nil {
472- logger .Errorf ( "error running udevadm trigger %v \n " , err )
473- return err
541+ logger .WithError ( err ). Warn ( "scsiHostRescan: cannot read scsi_host directory" )
542+ return
474543 }
475- logger .Debugf ("Successfully probed all attachments" )
476- return nil
477- }
478544
479- func scsiHostRescan () {
480- scsiPath := "/sys/class/scsi_host/"
481- if dirs , err := ioutil .ReadDir (scsiPath ); err == nil {
482- for _ , f := range dirs {
483- name := scsiPath + f .Name () + "/scan"
484- data := []byte ("- - -" )
485- ioutil .WriteFile (name , data , 0666 )
486- }
545+ for _ , f := range dirs {
546+ name := scsiPath + f .Name () + "/scan"
547+ data := []byte ("- - -" )
548+ _ = os .WriteFile (name , data , 0666 )
487549 }
488550}
489551
@@ -494,20 +556,20 @@ func (m *mounter) GetDeviceName(mounter mount.Interface, mountPath string) (stri
494556
495557// FindAbsoluteDeviceByIDPath follows the /dev/disk/by-id symlink to find the absolute path of a device
496558func (m * mounter ) FindAbsoluteDeviceByIDPath (volumeName string ) (string , error ) {
497- path := guessDiskIDPathByVolumeID (volumeName )
498- if path == nil {
559+ path := guessDiskIDPathByVolumeID (volumeName , m . log )
560+ if path == "" {
499561 return "" , fmt .Errorf ("could not find device-path for volume: %s" , volumeName )
500562 }
501563
502564 // EvalSymlinks returns relative link if the file is not a symlink
503565 // so we do not have to check if it is symlink prior to evaluation
504- resolved , err := filepath .EvalSymlinks (* path )
566+ resolved , err := filepath .EvalSymlinks (path )
505567 if err != nil {
506- return "" , fmt .Errorf ("could not resolve symlink %q: %v" , * path , err )
568+ return "" , fmt .Errorf ("could not resolve symlink %q: %v" , path , err )
507569 }
508570
509571 if ! strings .HasPrefix (resolved , "/dev" ) {
510- return "" , fmt .Errorf ("resolved symlink %q for %q was unexpected" , resolved , * path )
572+ return "" , fmt .Errorf ("resolved symlink %q for %q was unexpected" , resolved , path )
511573 }
512574
513575 return resolved , nil
0 commit comments