@@ -156,6 +156,9 @@ type Manager interface {
156
156
// BatchAttachVolumes attaches multiple volumes to a virtual machine.
157
157
BatchAttachVolumes (ctx context.Context ,
158
158
vm * cnsvsphere.VirtualMachine , batchAttachRequest []BatchAttachRequest ) ([]BatchAttachResult , string , error )
159
+ // UnregisterVolume unregisters a volume from CNS.
160
+ // If unregisterDisk is true, it will also unregister the disk from FCD.
161
+ UnregisterVolume (ctx context.Context , volumeID string , unregisterDisk bool ) * Error
159
162
}
160
163
161
164
// CnsVolumeInfo hold information related to volume created by CNS.
@@ -215,6 +218,22 @@ type ExpandVolumeExtraParams struct {
215
218
IsPodVMOnStretchSupervisorFSSEnabled bool
216
219
}
217
220
221
+ // Error is a custom error type that wraps an error and adds a transient flag.
222
+ // This is used to indicate whether the error is transient or not, which can be
223
+ // useful for retry logic in the context of volume operations.
224
+ // More fields can be added to this struct in the future if needed.
225
+ type Error struct {
226
+ error
227
+ IsTransient bool
228
+ }
229
+
230
+ func newError (err error , isTransient bool ) * Error {
231
+ return & Error {
232
+ error : err ,
233
+ IsTransient : isTransient ,
234
+ }
235
+ }
236
+
218
237
var (
219
238
// managerInstance is a Manager singleton.
220
239
managerInstance * defaultManager
@@ -3615,3 +3634,103 @@ func (m *defaultManager) SetListViewNotReady(ctx context.Context) {
3615
3634
m .listViewIf .SetListViewNotReady (ctx )
3616
3635
}
3617
3636
}
3637
+
3638
+ // UnregisterVolume unregisters a volume from CNS.
3639
+ // If unregisterDisk is true, it will also unregister the disk from FCD.
3640
+ func (m * defaultManager ) UnregisterVolume (ctx context.Context , volumeID string , unregisterDisk bool ) * Error {
3641
+ ctx , cancelFunc := ensureOperationContextHasATimeout (ctx )
3642
+ defer cancelFunc ()
3643
+ start := time .Now ()
3644
+ e := m .unregisterVolume (ctx , volumeID , unregisterDisk )
3645
+ if e != nil {
3646
+ prometheus .CnsControlOpsHistVec .WithLabelValues (prometheus .PrometheusUnregisterVolumeOpType ,
3647
+ prometheus .PrometheusFailStatus ).Observe (time .Since (start ).Seconds ())
3648
+ return e
3649
+ }
3650
+
3651
+ prometheus .CnsControlOpsHistVec .WithLabelValues (prometheus .PrometheusUnregisterVolumeOpType ,
3652
+ prometheus .PrometheusPassStatus ).Observe (time .Since (start ).Seconds ())
3653
+ return nil
3654
+ }
3655
+
3656
+ func (m * defaultManager ) unregisterVolume (ctx context.Context , volumeID string , unregisterDisk bool ) * Error {
3657
+ log := logger .GetLogger (ctx )
3658
+
3659
+ if m .virtualCenter == nil {
3660
+ return newError (errors .New ("invalid manager instance" ), true )
3661
+ }
3662
+
3663
+ err := m .virtualCenter .ConnectCns (ctx )
3664
+ if err != nil {
3665
+ log .Errorf ("ConnectCns failed with err: %+v" , err )
3666
+ return newError (err , true )
3667
+ }
3668
+
3669
+ targetVolumeType := "FCD"
3670
+ if unregisterDisk {
3671
+ targetVolumeType = "LEGACY_DISK"
3672
+ }
3673
+ spec := []cnstypes.CnsUnregisterVolumeSpec {
3674
+ {
3675
+ VolumeId : cnstypes.CnsVolumeId {Id : volumeID },
3676
+ TargetVolumeType : targetVolumeType ,
3677
+ },
3678
+ }
3679
+ task , err := m .virtualCenter .CnsClient .UnregisterVolume (ctx , spec )
3680
+ if err != nil {
3681
+ log .Errorf ("CNS UnregisterVolume failed from the vCenter %q with err: %v" , m .virtualCenter .Config .Host , err )
3682
+ return handleUnregisterError (ctx , err )
3683
+ }
3684
+
3685
+ taskInfo , err := m .waitOnTask (ctx , task .Reference ())
3686
+ if err != nil {
3687
+ log .Errorf ("failed to get UnregisterVolume taskInfo from vCenter %q with err: %v" ,
3688
+ m .virtualCenter .Config .Host , err )
3689
+ // TODO: check if there could be non-transient errors here.
3690
+ return newError (err , true )
3691
+ }
3692
+
3693
+ if taskInfo == nil {
3694
+ log .Errorf ("failed to get UnregisterVolume taskInfo from vCenter %q" ,
3695
+ m .virtualCenter .Config .Host )
3696
+ return newError (errors .New ("taskInfo is empty for UnregisterVolume task" ), true )
3697
+ }
3698
+
3699
+ log .Infof ("UnregisterVolume: volumeID: %q, opId: %q" , volumeID , taskInfo .ActivationId )
3700
+ res , err := getTaskResultFromTaskInfo (ctx , taskInfo )
3701
+ if err != nil {
3702
+ log .Errorf ("failed to get UnregisterVolume task result with error: %v" , err )
3703
+ return newError (err , true )
3704
+ }
3705
+
3706
+ if res == nil {
3707
+ log .Errorf ("failed to get UnregisterVolume task result from vCenter %q. taskID: %q, opId: %q" ,
3708
+ m .virtualCenter .Config .Host , taskInfo .Task .Value , taskInfo .ActivationId )
3709
+ return newError (errors .New ("task result is empty for UnregisterVolume task" ), true )
3710
+ }
3711
+
3712
+ volOpRes := res .GetCnsVolumeOperationResult ()
3713
+ if volOpRes .Fault != nil {
3714
+ log .Errorf ("failed to unregister volume: %q, fault: %q, opID: %q" ,
3715
+ volumeID , ExtractFaultTypeFromVolumeResponseResult (ctx , volOpRes ), taskInfo .ActivationId )
3716
+ return newError (errors .New ("observed a fault in UnregisterVolume result" ), true )
3717
+ }
3718
+
3719
+ log .Infof ("UnregisterVolume: volume unregistered successfully. volumeID: %q, opId: %q" ,
3720
+ volumeID , taskInfo .ActivationId )
3721
+ return nil
3722
+ }
3723
+
3724
+ func handleUnregisterError (ctx context.Context , err error ) * Error {
3725
+ faultType := ExtractFaultTypeFromErr (ctx , err )
3726
+ e := Error {err , true }
3727
+ if faultType == csifault .VimFaultNotFound ||
3728
+ faultType == csifault .VimFaultInvalidState ||
3729
+ faultType == csifault .VimFaultInvalidDatastore ||
3730
+ faultType == csifault .VimFaultInvalidArgument {
3731
+ // If the fault type is NotFound, InvalidState, InvalidDatastore, or InvalidArgument,
3732
+ // retry won't help.
3733
+ e .IsTransient = false
3734
+ }
3735
+ return & e
3736
+ }
0 commit comments