Skip to content

Commit d23b324

Browse files
committed
refactor and simplify Connector
- refactor connector's functions to use it as a receiver - refactor waitForPathToExists - refactor Connect to make it smaller and more readable
1 parent 8f2b4d0 commit d23b324

File tree

2 files changed

+159
-134
lines changed

2 files changed

+159
-134
lines changed

iscsi/iscsi.go

Lines changed: 151 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,10 @@ type Connector struct {
7373
MountTargetDevice *Device `json:"mount_target_device"`
7474
Devices []Device `json:"devices"`
7575

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"`
8280
}
8381

8482
func init() {
@@ -161,47 +159,60 @@ func getCurrentSessions() ([]iscsiSession, error) {
161159
return sessions, err
162160
}
163161

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 {
165164
return waitForPathToExistImpl(devicePath, maxRetries, intervalSeconds, deviceTransport, os.Stat, filepath.Glob)
166165
}
167166

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 {
169168
if devicePath == nil {
170-
return false, fmt.Errorf("unable to check unspecified devicePath")
169+
return fmt.Errorf("unable to check unspecified devicePath")
171170
}
172171

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+
}
184177

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
195182
}
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
198203
}
199-
if i == maxRetries-1 {
200-
break
204+
} else {
205+
fpath, _ := filepathGlob(*devicePath)
206+
if fpath == nil {
207+
return os.ErrNotExist
201208
}
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]
203213
}
204-
return false, err
214+
215+
return nil
205216
}
206217

207218
// getMultipathDevice returns a multipath device for the configured targets if it exists
@@ -244,20 +255,14 @@ func getMultipathDevice(devices []Device) (*Device, error) {
244255
}
245256

246257
// 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) {
249259
if c.RetryCount == 0 {
250260
c.RetryCount = 10
251261
}
252262
if c.CheckInterval == 0 {
253263
c.CheckInterval = 1
254264
}
255265

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-
261266
iFace := "default"
262267
if c.Interface != "" {
263268
iFace = c.Interface
@@ -270,89 +275,31 @@ func Connect(c *Connector) (string, error) {
270275
}
271276
iscsiTransport := extractTransportName(out)
272277

278+
var lastErr error
273279
var devicePaths []string
274280
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)
326282
if err != nil {
327-
debug.Printf("failed to login, err: %v", err)
328283
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)
333286
devicePaths = append(devicePaths, devicePath)
334-
continue
335-
} else if err != nil {
336-
lastErr = fmt.Errorf("couldn't attach disk, err: %v", err)
337287
}
338288
}
339289

340290
// GetISCSIDevices returns all devices if no paths are given
341291
if len(devicePaths) < 1 {
342292
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
348295
}
349296

350297
if len(c.Devices) < 1 {
351298
iscsiCmd([]string{"-m", "iface", "-I", iFace, "-o", "delete"}...)
352299
return "", fmt.Errorf("failed to find device path: %s, last error seen: %v", devicePaths, lastErr)
353300
}
354301

355-
mountTargetDevice, err := getMountTargetDevice(c)
302+
mountTargetDevice, err := c.getMountTargetDevice()
356303
c.MountTargetDevice = mountTargetDevice
357304
if err != nil {
358305
debug.Printf("Connect failed: %v", err)
@@ -365,18 +312,95 @@ func Connect(c *Connector) (string, error) {
365312
return c.MountTargetDevice.GetPath(), nil
366313
}
367314

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)
371351
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)
373399
}
374-
err = DeleteDBEntry(tgtIqn)
375-
return err
376400
}
377401

378402
// DisconnectVolume removes a volume from a Linux host.
379-
func DisconnectVolume(c *Connector) error {
403+
func (c *Connector) DisconnectVolume() error {
380404
// Steps to safely remove an iSCSI storage volume from a Linux host are as following:
381405
// 1. Unmount the disk from a filesystem on the system.
382406
// 2. Flush the multipath map for the disk we’re removing (if multipath is enabled).
@@ -387,13 +411,12 @@ func DisconnectVolume(c *Connector) error {
387411
// DisconnectVolume focuses on step 2 and 3.
388412
// Note: make sure the volume is already unmounted before calling this method.
389413

390-
if len(c.Devices) > 1 {
414+
if c.IsMultipathEnabled() {
391415
debug.Printf("Removing multipath device in path %s.\n", c.MountTargetDevice.GetPath())
392416
err := FlushMultipathDevice(c.MountTargetDevice)
393417
if err != nil {
394418
return err
395419
}
396-
397420
if err := RemoveSCSIDevices(c.Devices...); err != nil {
398421
return err
399422
}
@@ -413,8 +436,9 @@ func DisconnectVolume(c *Connector) error {
413436
return nil
414437
}
415438

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() {
418442
multipathDevice, err := getMultipathDevice(c.Devices)
419443
if err != nil {
420444
debug.Printf("mount target is not a multipath device: %v", err)
@@ -431,7 +455,12 @@ func getMountTargetDevice(c *Connector) (*Device, error) {
431455
return &c.Devices[0], nil
432456
}
433457

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
435464
func GetISCSIDevice(deviceName string) (*Device, error) {
436465
iscsiDevices, err := GetISCSIDevices([]string{deviceName})
437466
if err != nil {
@@ -551,8 +580,8 @@ func RemoveSCSIDevices(devices ...Device) error {
551580
return nil
552581
}
553582

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 {
556585
//file := path.Join("mnt", c.VolumeName+".json")
557586
f, err := os.Create(filePath)
558587
if err != nil {

0 commit comments

Comments
 (0)