@@ -73,12 +73,10 @@ type Connector struct {
73
73
MountTargetDevice * Device `json:"mount_target_device"`
74
74
Devices []Device `json:"devices"`
75
75
76
- RetryCount int32 `json:"retry_count"`
77
- CheckInterval int32 `json:"check_interval"`
78
- DoDiscovery bool `json:"do_discovery"`
79
- DoCHAPDiscovery bool `json:"do_chap_discovery"`
80
- TargetIqn string `json:"target_iqn"`
81
- TargetPortals []string `json:"target_portals"`
76
+ RetryCount uint `json:"retry_count"`
77
+ CheckInterval uint `json:"check_interval"`
78
+ DoDiscovery bool `json:"do_discovery"`
79
+ DoCHAPDiscovery bool `json:"do_chap_discovery"`
82
80
}
83
81
84
82
func init () {
@@ -161,47 +159,60 @@ func getCurrentSessions() ([]iscsiSession, error) {
161
159
return sessions , err
162
160
}
163
161
164
- func waitForPathToExist (devicePath * string , maxRetries , intervalSeconds int , deviceTransport string ) (bool , error ) {
162
+ // waitForPathToExist wait for a file at a path to exists on disk
163
+ func waitForPathToExist (devicePath * string , maxRetries , intervalSeconds uint , deviceTransport string ) error {
165
164
return waitForPathToExistImpl (devicePath , maxRetries , intervalSeconds , deviceTransport , os .Stat , filepath .Glob )
166
165
}
167
166
168
- func waitForPathToExistImpl (devicePath * string , maxRetries , intervalSeconds int , deviceTransport string , osStat statFunc , filepathGlob globFunc ) ( bool , error ) {
167
+ func waitForPathToExistImpl (devicePath * string , maxRetries , intervalSeconds uint , deviceTransport string , osStat statFunc , filepathGlob globFunc ) error {
169
168
if devicePath == nil {
170
- return false , fmt .Errorf ("unable to check unspecified devicePath" )
169
+ return fmt .Errorf ("unable to check unspecified devicePath" )
171
170
}
172
171
173
- var err error
174
- for i := 0 ; i < maxRetries ; i ++ {
175
- err = nil
176
- if deviceTransport == "tcp" {
177
- _ , err = osStat (* devicePath )
178
- if err != nil && ! os .IsNotExist (err ) {
179
- debug .Printf ("Error attempting to stat device: %s" , err .Error ())
180
- return false , err
181
- } else if err != nil {
182
- debug .Printf ("Device not found for: %s" , * devicePath )
183
- }
172
+ for i := uint (0 ); i <= maxRetries ; i ++ {
173
+ if i != 0 {
174
+ debug .Printf ("Device path %q doesn't exists yet, retrying in %d seconds (%d/%d)" , * devicePath , intervalSeconds , i , maxRetries )
175
+ time .Sleep (time .Second * time .Duration (intervalSeconds ))
176
+ }
184
177
185
- } else {
186
- fpath , _ := filepathGlob (* devicePath )
187
- if fpath == nil {
188
- err = os .ErrNotExist
189
- } else {
190
- // There might be a case that fpath contains multiple device paths if
191
- // multiple PCI devices connect to same iSCSI target. We handle this
192
- // case at subsequent logic. Pick up only first path here.
193
- * devicePath = fpath [0 ]
194
- }
178
+ if err := pathExistsImpl (devicePath , deviceTransport , osStat , filepathGlob ); err == nil {
179
+ return nil
180
+ } else if ! os .IsNotExist (err ) {
181
+ return err
195
182
}
196
- if err == nil {
197
- return true , nil
183
+ }
184
+
185
+ return os .ErrNotExist
186
+ }
187
+
188
+ // pathExists checks if a file at a path exists on disk
189
+ func pathExists (devicePath * string , deviceTransport string ) error {
190
+ return pathExistsImpl (devicePath , deviceTransport , os .Stat , filepath .Glob )
191
+ }
192
+
193
+ func pathExistsImpl (devicePath * string , deviceTransport string , osStat statFunc , filepathGlob globFunc ) error {
194
+ if deviceTransport == "tcp" {
195
+ _ , err := osStat (* devicePath )
196
+ if err != nil {
197
+ if ! os .IsNotExist (err ) {
198
+ debug .Printf ("Error attempting to stat device: %s" , err .Error ())
199
+ return err
200
+ }
201
+ debug .Printf ("Device not found for: %s" , * devicePath )
202
+ return err
198
203
}
199
- if i == maxRetries - 1 {
200
- break
204
+ } else {
205
+ fpath , _ := filepathGlob (* devicePath )
206
+ if fpath == nil {
207
+ return os .ErrNotExist
201
208
}
202
- time .Sleep (time .Second * time .Duration (intervalSeconds ))
209
+ // There might be a case that fpath contains multiple device paths if
210
+ // multiple PCI devices connect to same iscsi target. We handle this
211
+ // case at subsequent logic. Pick up only first path here.
212
+ * devicePath = fpath [0 ]
203
213
}
204
- return false , err
214
+
215
+ return nil
205
216
}
206
217
207
218
// getMultipathDevice returns a multipath device for the configured targets if it exists
@@ -244,20 +255,14 @@ func getMultipathDevice(devices []Device) (*Device, error) {
244
255
}
245
256
246
257
// Connect attempts to connect a volume to this node using the provided Connector info
247
- func Connect (c * Connector ) (string , error ) {
248
- var lastErr error
258
+ func (c * Connector ) Connect () (string , error ) {
249
259
if c .RetryCount == 0 {
250
260
c .RetryCount = 10
251
261
}
252
262
if c .CheckInterval == 0 {
253
263
c .CheckInterval = 1
254
264
}
255
265
256
- if c .RetryCount < 0 || c .CheckInterval < 0 {
257
- return "" , fmt .Errorf ("invalid RetryCount and CheckInterval combination, both must be positive integers. " +
258
- "RetryCount: %d, CheckInterval: %d" , c .RetryCount , c .CheckInterval )
259
- }
260
-
261
266
iFace := "default"
262
267
if c .Interface != "" {
263
268
iFace = c .Interface
@@ -270,89 +275,31 @@ func Connect(c *Connector) (string, error) {
270
275
}
271
276
iscsiTransport := extractTransportName (out )
272
277
278
+ var lastErr error
273
279
var devicePaths []string
274
280
for _ , target := range c .Targets {
275
- debug .Printf ("process targetIqn: %s, portal: %s\n " , target .Iqn , target .Portal )
276
- baseArgs := []string {"-m" , "node" , "-T" , target .Iqn , "-p" , target .Portal }
277
- // Rescan sessions to discover newly mapped LUNs. Do not specify the interface when rescanning
278
- // to avoid establishing additional sessions to the same target.
279
- if _ , err := iscsiCmd (append (baseArgs , []string {"-R" }... )... ); err != nil {
280
- debug .Printf ("failed to rescan session, err: %v" , err )
281
- }
282
-
283
- // create our devicePath that we'll be looking for based on the transport being used
284
- port := defaultPort
285
- if target .Port != "" {
286
- port = target .Port
287
- }
288
- // portal with port
289
- p := strings .Join ([]string {target .Portal , port }, ":" )
290
- devicePath := strings .Join ([]string {"/dev/disk/by-path/ip" , p , "iscsi" , target .Iqn , "lun" , fmt .Sprint (c .Lun )}, "-" )
291
- if iscsiTransport != "tcp" {
292
- devicePath = strings .Join ([]string {"/dev/disk/by-path/pci" , "*" , "ip" , p , "iscsi" , target .Iqn , "lun" , fmt .Sprint (c .Lun )}, "-" )
293
- }
294
-
295
- exists , _ := sessionExists (p , target .Iqn )
296
- if exists {
297
- if exists , err := waitForPathToExist (& devicePath , 1 , 1 , iscsiTransport ); exists {
298
- debug .Printf ("Appending device path: %s" , devicePath )
299
- devicePaths = append (devicePaths , devicePath )
300
- continue
301
- } else if err != nil {
302
- return "" , err
303
- }
304
- }
305
-
306
- if c .DoDiscovery {
307
- // build discoverydb and discover iSCSI target
308
- if err := Discoverydb (p , iFace , c .DiscoverySecrets , c .DoCHAPDiscovery ); err != nil {
309
- debug .Printf ("Error in discovery of the target: %s\n " , err .Error ())
310
- lastErr = err
311
- continue
312
- }
313
- }
314
-
315
- if c .DoCHAPDiscovery {
316
- // Make sure we don't log the secrets
317
- err := CreateDBEntry (target .Iqn , p , iFace , c .DiscoverySecrets , c .SessionSecrets )
318
- if err != nil {
319
- debug .Printf ("Error creating db entry: %s\n " , err .Error ())
320
- continue
321
- }
322
- }
323
-
324
- // perform the login
325
- err = Login (target .Iqn , p )
281
+ devicePath , err := c .connectTarget (& target , iFace , iscsiTransport )
326
282
if err != nil {
327
- debug .Printf ("failed to login, err: %v" , err )
328
283
lastErr = err
329
- continue
330
- }
331
- retries := int (c .RetryCount / c .CheckInterval )
332
- if exists , err := waitForPathToExist (& devicePath , retries , int (c .CheckInterval ), iscsiTransport ); exists {
284
+ } else {
285
+ debug .Printf ("Appending device path: %s" , devicePath )
333
286
devicePaths = append (devicePaths , devicePath )
334
- continue
335
- } else if err != nil {
336
- lastErr = fmt .Errorf ("couldn't attach disk, err: %v" , err )
337
287
}
338
288
}
339
289
340
290
// GetISCSIDevices returns all devices if no paths are given
341
291
if len (devicePaths ) < 1 {
342
292
c .Devices = []Device {}
343
- } else {
344
- c .Devices , err = GetISCSIDevices (devicePaths )
345
- if err != nil {
346
- return "" , err
347
- }
293
+ } else if c .Devices , err = GetISCSIDevices (devicePaths ); err != nil {
294
+ return "" , err
348
295
}
349
296
350
297
if len (c .Devices ) < 1 {
351
298
iscsiCmd ([]string {"-m" , "iface" , "-I" , iFace , "-o" , "delete" }... )
352
299
return "" , fmt .Errorf ("failed to find device path: %s, last error seen: %v" , devicePaths , lastErr )
353
300
}
354
301
355
- mountTargetDevice , err := getMountTargetDevice (c )
302
+ mountTargetDevice , err := c . getMountTargetDevice ()
356
303
c .MountTargetDevice = mountTargetDevice
357
304
if err != nil {
358
305
debug .Printf ("Connect failed: %v" , err )
@@ -365,18 +312,95 @@ func Connect(c *Connector) (string, error) {
365
312
return c .MountTargetDevice .GetPath (), nil
366
313
}
367
314
368
- //Disconnect performs a disconnect operation on a volume
369
- func Disconnect (tgtIqn string , portals []string ) error {
370
- err := Logout (tgtIqn , portals )
315
+ func (c * Connector ) connectTarget (target * TargetInfo , iFace string , iscsiTransport string ) (string , error ) {
316
+ debug .Printf ("Process targetIqn: %s, portal: %s\n " , target .Iqn , target .Portal )
317
+ baseArgs := []string {"-m" , "node" , "-T" , target .Iqn , "-p" , target .Portal }
318
+ // Rescan sessions to discover newly mapped LUNs. Do not specify the interface when rescanning
319
+ // to avoid establishing additional sessions to the same target.
320
+ if _ , err := iscsiCmd (append (baseArgs , []string {"-R" }... )... ); err != nil {
321
+ debug .Printf ("Failed to rescan session, err: %v" , err )
322
+ }
323
+
324
+ // create our devicePath that we'll be looking for based on the transport being used
325
+ port := defaultPort
326
+ if target .Port != "" {
327
+ port = target .Port
328
+ }
329
+ // portal with port
330
+ portal := strings .Join ([]string {target .Portal , port }, ":" )
331
+ devicePath := strings .Join ([]string {"/dev/disk/by-path/ip" , portal , "iscsi" , target .Iqn , "lun" , fmt .Sprint (c .Lun )}, "-" )
332
+ if iscsiTransport != "tcp" {
333
+ devicePath = strings .Join ([]string {"/dev/disk/by-path/pci" , "*" , "ip" , portal , "iscsi" , target .Iqn , "lun" , fmt .Sprint (c .Lun )}, "-" )
334
+ }
335
+
336
+ exists , _ := sessionExists (portal , target .Iqn )
337
+ if exists {
338
+ debug .Printf ("Session already exists, checking if device path %q exists" , devicePath )
339
+ if err := waitForPathToExist (& devicePath , c .RetryCount , c .CheckInterval , iscsiTransport ); err != nil {
340
+ return "" , err
341
+ }
342
+ return devicePath , nil
343
+ }
344
+
345
+ if err := c .discoverTarget (target , iFace , portal ); err != nil {
346
+ return "" , err
347
+ }
348
+
349
+ // perform the login
350
+ err := Login (target .Iqn , portal )
371
351
if err != nil {
372
- return err
352
+ debug .Printf ("Failed to login: %v" , err )
353
+ return "" , err
354
+ }
355
+
356
+ debug .Printf ("Waiting for device path %q to exist" , devicePath )
357
+ if err := waitForPathToExist (& devicePath , c .RetryCount , c .CheckInterval , iscsiTransport ); err != nil {
358
+ return "" , err
359
+ }
360
+
361
+ return devicePath , nil
362
+ }
363
+
364
+ func (c * Connector ) discoverTarget (target * TargetInfo , iFace string , portal string ) error {
365
+ if c .DoDiscovery {
366
+ // build discoverydb and discover iscsi target
367
+ if err := Discoverydb (portal , iFace , c .DiscoverySecrets , c .DoCHAPDiscovery ); err != nil {
368
+ debug .Printf ("Error in discovery of the target: %s\n " , err .Error ())
369
+ return err
370
+ }
371
+ }
372
+
373
+ if c .DoCHAPDiscovery {
374
+ // Make sure we don't log the secrets
375
+ err := CreateDBEntry (target .Iqn , portal , iFace , c .DiscoverySecrets , c .SessionSecrets )
376
+ if err != nil {
377
+ debug .Printf ("Error creating db entry: %s\n " , err .Error ())
378
+ return err
379
+ }
380
+ }
381
+
382
+ return nil
383
+ }
384
+
385
+ // Disconnect performs a disconnect operation from an appliance.
386
+ // Be sure to disconnect all deivces properly before doing this as it can result in data loss.
387
+ func (c * Connector ) Disconnect () {
388
+ for _ , target := range c .Targets {
389
+ Logout (target .Iqn , target .Portal )
390
+ }
391
+
392
+ deleted := map [string ]bool {}
393
+ for _ , target := range c .Targets {
394
+ if _ , ok := deleted [target .Iqn ]; ok {
395
+ continue
396
+ }
397
+ deleted [target .Iqn ] = true
398
+ DeleteDBEntry (target .Iqn )
373
399
}
374
- err = DeleteDBEntry (tgtIqn )
375
- return err
376
400
}
377
401
378
402
// DisconnectVolume removes a volume from a Linux host.
379
- func DisconnectVolume (c * Connector ) error {
403
+ func (c * Connector ) DisconnectVolume ( ) error {
380
404
// Steps to safely remove an iSCSI storage volume from a Linux host are as following:
381
405
// 1. Unmount the disk from a filesystem on the system.
382
406
// 2. Flush the multipath map for the disk we’re removing (if multipath is enabled).
@@ -387,13 +411,12 @@ func DisconnectVolume(c *Connector) error {
387
411
// DisconnectVolume focuses on step 2 and 3.
388
412
// Note: make sure the volume is already unmounted before calling this method.
389
413
390
- if len ( c . Devices ) > 1 {
414
+ if c . IsMultipathEnabled () {
391
415
debug .Printf ("Removing multipath device in path %s.\n " , c .MountTargetDevice .GetPath ())
392
416
err := FlushMultipathDevice (c .MountTargetDevice )
393
417
if err != nil {
394
418
return err
395
419
}
396
-
397
420
if err := RemoveSCSIDevices (c .Devices ... ); err != nil {
398
421
return err
399
422
}
@@ -413,8 +436,9 @@ func DisconnectVolume(c *Connector) error {
413
436
return nil
414
437
}
415
438
416
- func getMountTargetDevice (c * Connector ) (* Device , error ) {
417
- if len (c .Devices ) > 1 {
439
+ // getMountTargetDevice returns the device to be mounted among the configured devices
440
+ func (c * Connector ) getMountTargetDevice () (* Device , error ) {
441
+ if c .IsMultipathEnabled () {
418
442
multipathDevice , err := getMultipathDevice (c .Devices )
419
443
if err != nil {
420
444
debug .Printf ("mount target is not a multipath device: %v" , err )
@@ -431,7 +455,12 @@ func getMountTargetDevice(c *Connector) (*Device, error) {
431
455
return & c .Devices [0 ], nil
432
456
}
433
457
434
- // GetISCSIDevice get an iSCSI device from a device name
458
+ // IsMultipathEnabled check if multipath is enabled on devices handled by this connector
459
+ func (c * Connector ) IsMultipathEnabled () bool {
460
+ return len (c .Devices ) > 1
461
+ }
462
+
463
+ // GetISCSIDevice get an SCSI device from a device name
435
464
func GetISCSIDevice (deviceName string ) (* Device , error ) {
436
465
iscsiDevices , err := GetISCSIDevices ([]string {deviceName })
437
466
if err != nil {
@@ -551,8 +580,8 @@ func RemoveSCSIDevices(devices ...Device) error {
551
580
return nil
552
581
}
553
582
554
- // PersistConnector persists the provided Connector to the specified file (ie /var/lib/pfile/myConnector.json)
555
- func PersistConnector (c * Connector , filePath string ) error {
583
+ // Persist persists the Connector to the specified file (ie /var/lib/pfile/myConnector.json)
584
+ func (c * Connector ) Persist ( filePath string ) error {
556
585
//file := path.Join("mnt", c.VolumeName+".json")
557
586
f , err := os .Create (filePath )
558
587
if err != nil {
0 commit comments