Skip to content

Commit 769bfbb

Browse files
authored
Require healthy paths during iscsi volume expansion
Trident now checks the state of all paths to a LUN before rescanning or resizing the multipath map. If any path is unhealthy, expansion is deferred until all paths are remediated. Trident now also requires stable reads of path health, SCSI disk size, and multipath device size before returning success.
1 parent cb9a80c commit 769bfbb

17 files changed

Lines changed: 2174 additions & 911 deletions

frontend/csi/node_server.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -742,24 +742,16 @@ func (p *Plugin) nodePrepareISCSIVolumeForExpansion(
742742
"filesystemType": publishInfo.FilesystemType,
743743
}).Debug("PublishInfo for block device to expand.")
744744

745-
var err error
746-
747-
// Make sure device is ready.
748-
if p.iscsi.IsAlreadyAttached(ctx, lunID, publishInfo.IscsiTargetIQN) {
749-
// Rescan device to detect increased size.
750-
if err = p.iscsi.RescanDevices(
751-
ctx, publishInfo.IscsiTargetIQN, publishInfo.IscsiLunNumber, requiredBytes); err != nil {
752-
Logc(ctx).WithField("device", publishInfo.DevicePath).WithError(err).
753-
Error("Unable to scan device.")
754-
err = status.Error(codes.Internal, err.Error())
755-
}
756-
} else {
757-
err = fmt.Errorf("device %s to expand is not attached", publishInfo.DevicePath)
758-
Logc(ctx).WithField("devicePath", publishInfo.DevicePath).WithError(err).Error(
759-
"Unable to expand volume.")
745+
// Resize the volume.
746+
if err := p.iscsi.ExpandVolume(ctx, publishInfo, requiredBytes); err != nil {
747+
Logc(ctx).WithFields(LogFields{
748+
"lunID": publishInfo.IscsiLunNumber,
749+
"devicePath": publishInfo.DevicePath,
750+
}).WithError(err).Error("Unable to resize device(s) for LUN.")
760751
return status.Error(codes.Internal, err.Error())
761752
}
762-
return err
753+
754+
return nil
763755
}
764756

765757
func (p *Plugin) NodeGetCapabilities(

frontend/csi/node_server_test.go

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025 NetApp, Inc. All Rights Reserved.
1+
// Copyright 2026 NetApp, Inc. All Rights Reserved.
22

33
package csi
44

@@ -75,8 +75,7 @@ func TestNodeStageVolume(t *testing.T) {
7575
mockISCSIClient.EXPECT().AttachVolumeRetry(
7676
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
7777
).Return(int64(1), nil)
78-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
79-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
78+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
8079
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
8180
return mockISCSIClient
8281
},
@@ -252,8 +251,7 @@ func TestNodeStageISCSIVolume(t *testing.T) {
252251
mockISCSIClient.EXPECT().AttachVolumeRetry(
253252
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
254253
).Return(int64(1), nil)
255-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
256-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
254+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
257255
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
258256
return mockISCSIClient
259257
},
@@ -520,7 +518,9 @@ func TestNodeStageISCSIVolume(t *testing.T) {
520518
mockISCSIClient.EXPECT().AttachVolumeRetry(
521519
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
522520
).Return(int64(1), nil)
523-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(false)
521+
mockISCSIClient.EXPECT().ExpandVolume(
522+
gomock.Any(), gomock.Any(), gomock.Any(),
523+
).Return(errors.New("volume not attached"))
524524
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
525525
return mockISCSIClient
526526
},
@@ -538,9 +538,9 @@ func TestNodeStageISCSIVolume(t *testing.T) {
538538
mockISCSIClient.EXPECT().AttachVolumeRetry(
539539
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
540540
).Return(int64(1), nil)
541-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
542-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(),
543-
gomock.Any()).Return(errors.New("some error"))
541+
mockISCSIClient.EXPECT().ExpandVolume(
542+
gomock.Any(), gomock.Any(), gomock.Any(),
543+
).Return(errors.New("volume resize failed"))
544544
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
545545
return mockISCSIClient
546546
},
@@ -558,8 +558,7 @@ func TestNodeStageISCSIVolume(t *testing.T) {
558558
mockISCSIClient.EXPECT().AttachVolumeRetry(
559559
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
560560
).Return(int64(1), nil)
561-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
562-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
561+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
563562
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
564563
return mockISCSIClient
565564
},
@@ -2733,8 +2732,7 @@ func TestNodeStageVolume_Multithreaded(t *testing.T) {
27332732
mockISCSIClient.EXPECT().AttachVolumeRetry(
27342733
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
27352734
).Return(int64(1), nil)
2736-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
2737-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
2735+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
27382736
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
27392737
mockTrackingClient.EXPECT().WriteTrackingInfo(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(nil)
27402738
}
@@ -2881,8 +2879,7 @@ func TestNodeStageVolume_Multithreaded(t *testing.T) {
28812879
mockISCSIClient.EXPECT().AttachVolumeRetry(
28822880
gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
28832881
).Return(int64(1), nil)
2884-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
2885-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
2882+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
28862883
mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
28872884
mockTrackingClient.EXPECT().WriteTrackingInfo(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(nil)
28882885
}
@@ -11662,8 +11659,7 @@ func TestNodeExpandVolume(t *testing.T) {
1166211659
},
1166311660
setupISCSIMock: func() iscsi.ISCSI {
1166411661
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
11665-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
11666-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
11662+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
1166711663
return mockISCSIClient
1166811664
},
1166911665
mockFilesystem: func() filesystem.Filesystem {
@@ -11703,8 +11699,8 @@ func TestNodeExpandVolume(t *testing.T) {
1170311699
},
1170411700
setupISCSIMock: func() iscsi.ISCSI {
1170511701
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
11706-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes()
11707-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
11702+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(),
11703+
gomock.Any()).Return(errors.New("failure")).AnyTimes()
1170811704
return mockISCSIClient
1170911705
},
1171011706

@@ -11738,8 +11734,7 @@ func TestNodeExpandVolume(t *testing.T) {
1173811734
},
1173911735
setupISCSIMock: func() iscsi.ISCSI {
1174011736
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
11741-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
11742-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes()
11737+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
1174311738
return mockISCSIClient
1174411739
},
1174511740

@@ -11773,8 +11768,7 @@ func TestNodeExpandVolume(t *testing.T) {
1177311768
},
1177411769
setupISCSIMock: func() iscsi.ISCSI {
1177511770
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
11776-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
11777-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
11771+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
1177811772
return mockISCSIClient
1177911773
},
1178011774
mockFilesystem: func() filesystem.Filesystem {
@@ -11924,8 +11918,7 @@ func TestNodeExpandVolume(t *testing.T) {
1192411918
},
1192511919
setupISCSIMock: func() iscsi.ISCSI {
1192611920
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
11927-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
11928-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
11921+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
1192911922
return mockISCSIClient
1193011923
},
1193111924
mockFilesystem: func() filesystem.Filesystem {
@@ -11972,8 +11965,7 @@ func TestNodeExpandVolume(t *testing.T) {
1197211965
},
1197311966
setupISCSIMock: func() iscsi.ISCSI {
1197411967
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
11975-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
11976-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
11968+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
1197711969
return mockISCSIClient
1197811970
},
1197911971
mockFilesystem: func() filesystem.Filesystem {
@@ -12020,8 +12012,8 @@ func TestNodeExpandVolume(t *testing.T) {
1202012012
},
1202112013
setupISCSIMock: func() iscsi.ISCSI {
1202212014
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
12023-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
12024-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
12015+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(),
12016+
gomock.Any()).Return(nil).AnyTimes()
1202512017
return mockISCSIClient
1202612018
},
1202712019
mockFilesystem: func() filesystem.Filesystem {
@@ -12068,8 +12060,8 @@ func TestNodeExpandVolume(t *testing.T) {
1206812060
},
1206912061
setupISCSIMock: func() iscsi.ISCSI {
1207012062
mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t))
12071-
mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
12072-
mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
12063+
mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(),
12064+
gomock.Any()).Return(errors.New("failure")).AnyTimes()
1207312065
return mockISCSIClient
1207412066
},
1207512067
mockFilesystem: func() filesystem.Filesystem {

mocks/mock_utils/mock_devices/mock_devices_client.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mocks/mock_utils/mock_iscsi/mock_iscsi_client.go

Lines changed: 28 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/collection/list.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025 NetApp, Inc. All Rights Reserved.
1+
// Copyright 2026 NetApp, Inc. All Rights Reserved.
22

33
package collection
44

@@ -98,7 +98,7 @@ func RemoveStringConditionally(slice []string, s string, fn func(string, string)
9898
}
9999
result = append(result, item)
100100
}
101-
return
101+
return result
102102
}
103103

104104
// ReplaceAtIndex returns a string with the rune at the specified index replaced.
@@ -142,3 +142,24 @@ func StringInSlice(s string, list []string) bool {
142142
}
143143
return false
144144
}
145+
146+
// EqualValues accepts 2 slices of any standard comparable type and returns whether their values are equivalent.
147+
func EqualValues[C comparable](s1, s2 []C) bool {
148+
if len(s1) != len(s2) {
149+
return false
150+
}
151+
152+
elemOccurrences := make(map[any]int, len(s1))
153+
for _, elem := range s1 {
154+
elemOccurrences[elem] += 1
155+
}
156+
157+
for _, v := range s2 {
158+
elemOccurrences[v]--
159+
if elemOccurrences[v] < 0 {
160+
return false
161+
}
162+
}
163+
164+
return true
165+
}

0 commit comments

Comments
 (0)