@@ -60,6 +60,13 @@ type Device struct {
60
60
Size string `json:"size,omitempty"`
61
61
}
62
62
63
+ type HCTL struct {
64
+ HBA int
65
+ Channel int
66
+ Target int
67
+ LUN int
68
+ }
69
+
63
70
// Connector provides a struct to hold all of the needed parameters to make our iSCSI connection
64
71
type Connector struct {
65
72
VolumeName string `json:"volume_name"`
@@ -291,6 +298,12 @@ func (c *Connector) Connect() (string, error) {
291
298
return "" , err
292
299
}
293
300
301
+ if c .IsMultipathEnabled () {
302
+ if err := c .IsMultipathConsistent (); err != nil {
303
+ return "" , fmt .Errorf ("multipath is inconsistent: %v" , err )
304
+ }
305
+ }
306
+
294
307
return c .MountTargetDevice .GetPath (), nil
295
308
}
296
309
@@ -394,6 +407,10 @@ func (c *Connector) DisconnectVolume() error {
394
407
// Note: make sure the volume is already unmounted before calling this method.
395
408
396
409
if c .IsMultipathEnabled () {
410
+ if err := c .IsMultipathConsistent (); err != nil {
411
+ return fmt .Errorf ("multipath is inconsistent: %v" , err )
412
+ }
413
+
397
414
debug .Printf ("Removing multipath device in path %s.\n " , c .MountTargetDevice .GetPath ())
398
415
err := FlushMultipathDevice (c .MountTargetDevice )
399
416
if err != nil {
@@ -599,6 +616,52 @@ func GetConnectorFromFile(filePath string) (*Connector, error) {
599
616
return & c , nil
600
617
}
601
618
619
+ // IsMultipathConsistent check if the currently used device is using a consistent multipath mapping
620
+ func (c * Connector ) IsMultipathConsistent () error {
621
+ devices := append ([]Device {* c .MountTargetDevice }, c .Devices ... )
622
+
623
+ referenceLUN := struct {
624
+ LUN int
625
+ Name string
626
+ }{LUN : - 1 , Name : "" }
627
+ HBA := map [int ]string {}
628
+ referenceDevice := devices [0 ]
629
+ for _ , device := range devices {
630
+ if device .Size != referenceDevice .Size {
631
+ return fmt .Errorf ("devices size differ: %s (%s) != %s (%s)" , device .Name , device .Size , referenceDevice .Name , referenceDevice .Size )
632
+ }
633
+
634
+ if device .Type != "mpath" {
635
+ hctl , err := device .HCTL ()
636
+ if err != nil {
637
+ return err
638
+ }
639
+ if referenceLUN .LUN == - 1 {
640
+ referenceLUN .LUN = hctl .LUN
641
+ referenceLUN .Name = device .Name
642
+ } else if hctl .LUN != referenceLUN .LUN {
643
+ return fmt .Errorf ("devices LUNs differ: %s (%d) != %s (%d)" , device .Name , hctl .LUN , referenceLUN .Name , referenceLUN .LUN )
644
+ }
645
+
646
+ if name , ok := HBA [hctl .HBA ]; ! ok {
647
+ HBA [hctl .HBA ] = device .Name
648
+ } else {
649
+ return fmt .Errorf ("two devices are using the same controller (%d): %s and %s" , hctl .HBA , device .Name , name )
650
+ }
651
+ }
652
+
653
+ wwid , err := device .WWID ()
654
+ if err != nil {
655
+ return fmt .Errorf ("could not find WWID for device %s: %v" , device .Name , err )
656
+ }
657
+ if wwid != referenceDevice .Name {
658
+ return fmt .Errorf ("devices WWIDs differ: %s (wwid:%s) != %s (wwid:%s)" , device .Name , wwid , referenceDevice .Name , referenceDevice .Name )
659
+ }
660
+ }
661
+
662
+ return nil
663
+ }
664
+
602
665
// Exists check if the device exists at its path and returns an error otherwise
603
666
func (d * Device ) Exists () error {
604
667
_ , err := osStat (d .GetPath ())
@@ -614,6 +677,42 @@ func (d *Device) GetPath() string {
614
677
return filepath .Join ("/dev" , d .Name )
615
678
}
616
679
680
+ // WWID returns the WWID of a device
681
+ func (d * Device ) WWID () (string , error ) {
682
+ timeout := 1 * time .Second
683
+ out , err := execWithTimeout ("scsi_id" , []string {"-g" , "-u" , d .GetPath ()}, timeout )
684
+ if err != nil {
685
+ return "" , err
686
+ }
687
+
688
+ return string (out [:len (out )- 1 ]), nil
689
+ }
690
+
691
+ // HCTL returns the HCTL of a device
692
+ func (d * Device ) HCTL () (* HCTL , error ) {
693
+ var hctl []int
694
+
695
+ for _ , idstr := range strings .Split (d .Hctl , ":" ) {
696
+ id , err := strconv .Atoi (idstr )
697
+ if err != nil {
698
+ hctl = []int {}
699
+ break
700
+ }
701
+ hctl = append (hctl , id )
702
+ }
703
+
704
+ if len (hctl ) != 4 {
705
+ return nil , fmt .Errorf ("invalid HCTL (%s) for device %q" , d .Hctl , d .Name )
706
+ }
707
+
708
+ return & HCTL {
709
+ HBA : hctl [0 ],
710
+ Channel : hctl [1 ],
711
+ Target : hctl [2 ],
712
+ LUN : hctl [3 ],
713
+ }, nil
714
+ }
715
+
617
716
// WriteDeviceFile write in a device file
618
717
func (d * Device ) WriteDeviceFile (name string , content string ) error {
619
718
return writeInSCSIDeviceFile (d .Hctl , name , content )
0 commit comments