Skip to content

Commit 52ccaa1

Browse files
authored
Allow Attach volume call to handle undefined errors (#3761)
Signed-off-by: Deepak Kinni <[email protected]>
1 parent ae6ed9a commit 52ccaa1

File tree

2 files changed

+153
-34
lines changed

2 files changed

+153
-34
lines changed

pkg/common/cns-lib/volume/manager.go

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,47 +1118,50 @@ func (m *defaultManager) AttachVolume(ctx context.Context,
11181118
}
11191119

11201120
volumeOperationRes := taskResult.GetCnsVolumeOperationResult()
1121-
if volumeOperationRes.Fault != nil && volumeOperationRes.Fault.Fault != nil {
1121+
if volumeOperationRes.Fault != nil {
11221122
faultType = ExtractFaultTypeFromVolumeResponseResult(ctx, volumeOperationRes)
1123-
_, isResourceInUseFault := volumeOperationRes.Fault.Fault.(*vim25types.ResourceInUse)
1124-
if isResourceInUseFault {
1125-
log.Infof("observed ResourceInUse fault while attaching volume: %q with vm: %q", volumeID, vm.String())
1126-
// Check if volume is already attached to the requested node.
1127-
diskUUID, err := IsDiskAttached(ctx, vm, volumeID, checkNVMeController)
1128-
if err != nil {
1129-
return "", faultType, err
1130-
}
1131-
if diskUUID != "" {
1132-
return diskUUID, "", nil
1123+
1124+
if volumeOperationRes.Fault.Fault != nil {
1125+
_, isResourceInUseFault := volumeOperationRes.Fault.Fault.(*vim25types.ResourceInUse)
1126+
if isResourceInUseFault {
1127+
log.Infof("observed ResourceInUse fault while attaching volume: %q with vm: %q", volumeID, vm.String())
1128+
// Check if volume is already attached to the requested node.
1129+
diskUUID, err := IsDiskAttached(ctx, vm, volumeID, checkNVMeController)
1130+
if err != nil {
1131+
return "", faultType, err
1132+
}
1133+
if diskUUID != "" {
1134+
return diskUUID, "", nil
1135+
}
11331136
}
1134-
}
11351137

1136-
// Check if this is a CnsFault with NotSupported fault cause
1137-
if cnsFault, isCnsFault := volumeOperationRes.Fault.Fault.(*cnstypes.CnsFault); isCnsFault {
1138-
if cnsFault.FaultCause != nil {
1139-
notSupportedFault, isNotSupportedFault := cnsFault.FaultCause.Fault.(*vim25types.NotSupported)
1140-
if isNotSupportedFault {
1141-
log.Infof("observed CnsFault with NotSupported fault cause while attaching volume: %q with vm: %q",
1142-
volumeID, vm.String())
1138+
// Check if this is a CnsFault with NotSupported fault cause
1139+
if cnsFault, isCnsFault := volumeOperationRes.Fault.Fault.(*cnstypes.CnsFault); isCnsFault {
1140+
if cnsFault.FaultCause != nil {
1141+
notSupportedFault, isNotSupportedFault := cnsFault.FaultCause.Fault.(*vim25types.NotSupported)
1142+
if isNotSupportedFault {
1143+
log.Infof("observed CnsFault with NotSupported fault cause while attaching volume: %q with vm: %q",
1144+
volumeID, vm.String())
1145+
1146+
// Extract the specific error message from NotSupported fault's FaultMessage array
1147+
var errorMessages []string
1148+
for _, faultMsg := range notSupportedFault.FaultMessage {
1149+
if faultMsg.Message != "" {
1150+
errorMessages = append(errorMessages, faultMsg.Message)
1151+
}
1152+
}
11431153

1144-
// Extract the specific error message from NotSupported fault's FaultMessage array
1145-
var errorMessages []string
1146-
for _, faultMsg := range notSupportedFault.FaultMessage {
1147-
if faultMsg.Message != "" {
1148-
errorMessages = append(errorMessages, faultMsg.Message)
1154+
if len(errorMessages) > 0 {
1155+
extractedMessage := strings.Join(errorMessages, " - ")
1156+
log.Infof("NotSupported fault extracted message: %s", extractedMessage)
1157+
return "", faultType, logger.LogNewErrorf(log,
1158+
"%q Failed to attach cns volume: %q to node vm: %q. fault: %q. opId: %q",
1159+
extractedMessage, volumeID, vm.String(), spew.Sdump(volumeOperationRes.Fault), taskInfo.ActivationId)
11491160
}
1150-
}
11511161

1152-
if len(errorMessages) > 0 {
1153-
extractedMessage := strings.Join(errorMessages, " - ")
1154-
log.Infof("NotSupported fault extracted message: %s", extractedMessage)
1155-
return "", faultType, logger.LogNewErrorf(log,
1156-
"%q Failed to attach cns volume: %q to node vm: %q. fault: %q. opId: %q",
1157-
extractedMessage, volumeID, vm.String(), spew.Sdump(volumeOperationRes.Fault), taskInfo.ActivationId)
1162+
// Fallback to detailed dump for debugging
1163+
log.Debugf("NotSupported fault details: %+v", spew.Sdump(cnsFault.FaultCause))
11581164
}
1159-
1160-
// Fallback to detailed dump for debugging
1161-
log.Debugf("NotSupported fault details: %+v", spew.Sdump(cnsFault.FaultCause))
11621165
}
11631166
}
11641167
}

pkg/common/cns-lib/volume/manager_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,119 @@ func TestCnsFaultNotSupportedDetection(t *testing.T) {
168168
})
169169
}
170170
}
171+
172+
// TestAttachVolumeFaultHandling tests that AttachVolume correctly handles faults,
173+
// including unknown fault types that govmomi cannot deserialize.
174+
func TestAttachVolumeFaultHandling(t *testing.T) {
175+
tests := []struct {
176+
name string
177+
volumeOperationResult *cnstypes.CnsVolumeOperationResult
178+
expectFaultHandled bool
179+
expectSpecificHandling bool
180+
faultDescription string
181+
}{
182+
{
183+
name: "Known fault - ResourceInUse",
184+
volumeOperationResult: &cnstypes.CnsVolumeOperationResult{
185+
VolumeId: cnstypes.CnsVolumeId{Id: "test-volume-1"},
186+
Fault: &vim25types.LocalizedMethodFault{
187+
LocalizedMessage: "Resource is in use",
188+
Fault: &vim25types.ResourceInUse{
189+
Type: "Disk",
190+
Name: "test-disk",
191+
},
192+
},
193+
},
194+
expectFaultHandled: true,
195+
expectSpecificHandling: true,
196+
faultDescription: "ResourceInUse fault with Fault.Fault populated",
197+
},
198+
{
199+
name: "Known fault - CnsFault with NotSupported cause",
200+
volumeOperationResult: &cnstypes.CnsVolumeOperationResult{
201+
VolumeId: cnstypes.CnsVolumeId{Id: "test-volume-2"},
202+
Fault: &vim25types.LocalizedMethodFault{
203+
LocalizedMessage: "Operation not supported",
204+
Fault: &cnstypes.CnsFault{
205+
MethodFault: vim25types.MethodFault{
206+
FaultCause: &vim25types.LocalizedMethodFault{
207+
Fault: &vim25types.NotSupported{},
208+
},
209+
},
210+
Reason: "Not supported",
211+
},
212+
},
213+
},
214+
expectFaultHandled: true,
215+
expectSpecificHandling: true,
216+
faultDescription: "CnsFault with NotSupported cause",
217+
},
218+
{
219+
name: "Unknown fault - Fault.Fault is nil (e.g., CnsNotRegisteredFault)",
220+
volumeOperationResult: &cnstypes.CnsVolumeOperationResult{
221+
VolumeId: cnstypes.CnsVolumeId{Id: "test-volume-3"},
222+
Fault: &vim25types.LocalizedMethodFault{
223+
LocalizedMessage: "The input volume is not registered as a CNS volume",
224+
// Fault field is nil because govmomi doesn't recognize CnsNotRegisteredFault
225+
Fault: nil,
226+
},
227+
},
228+
expectFaultHandled: true,
229+
expectSpecificHandling: false,
230+
faultDescription: "Unknown fault type with Fault.Fault nil (simulates CnsNotRegisteredFault)",
231+
},
232+
{
233+
name: "Generic CnsFault",
234+
volumeOperationResult: &cnstypes.CnsVolumeOperationResult{
235+
VolumeId: cnstypes.CnsVolumeId{Id: "test-volume-4"},
236+
Fault: &vim25types.LocalizedMethodFault{
237+
LocalizedMessage: "Generic CNS fault",
238+
Fault: &cnstypes.CnsFault{
239+
Reason: "Generic error",
240+
},
241+
},
242+
},
243+
expectFaultHandled: true,
244+
expectSpecificHandling: false,
245+
faultDescription: "Generic CnsFault without special handling",
246+
},
247+
{
248+
name: "No fault",
249+
volumeOperationResult: &cnstypes.CnsVolumeOperationResult{
250+
VolumeId: cnstypes.CnsVolumeId{Id: "test-volume-5"},
251+
Fault: nil,
252+
},
253+
expectFaultHandled: false,
254+
expectSpecificHandling: false,
255+
faultDescription: "No fault present",
256+
},
257+
}
258+
259+
for _, tt := range tests {
260+
t.Run(tt.name, func(t *testing.T) {
261+
// Test the fault handling logic structure that AttachVolume uses
262+
if tt.volumeOperationResult.Fault != nil {
263+
// This should handle ALL faults, including unknown ones
264+
assert.True(t, tt.expectFaultHandled, "Fault should be detected: %s", tt.faultDescription)
265+
266+
// Test nested handling for known fault types
267+
if tt.volumeOperationResult.Fault.Fault != nil {
268+
// Can perform specific type assertions here
269+
_, isResourceInUse := tt.volumeOperationResult.Fault.Fault.(*vim25types.ResourceInUse)
270+
_, isCnsFault := tt.volumeOperationResult.Fault.Fault.(*cnstypes.CnsFault)
271+
272+
hasSpecificHandling := isResourceInUse || isCnsFault
273+
if tt.expectSpecificHandling {
274+
assert.True(t, hasSpecificHandling, "Should have specific fault handling")
275+
}
276+
} else {
277+
// Unknown fault types will have Fault.Fault == nil
278+
// These should still be caught by the outer check
279+
assert.False(t, tt.expectSpecificHandling, "Unknown faults should not have specific handling")
280+
}
281+
} else {
282+
assert.False(t, tt.expectFaultHandled, "No fault should be detected")
283+
}
284+
})
285+
}
286+
}

0 commit comments

Comments
 (0)