diff --git a/helm/csi-unity/templates/controller.yaml b/helm/csi-unity/templates/controller.yaml index 5f09e0f..882e072 100644 --- a/helm/csi-unity/templates/controller.yaml +++ b/helm/csi-unity/templates/controller.yaml @@ -238,6 +238,8 @@ spec: - "--leader-election" - "--http-endpoint=:8080" - "--enable-node-watcher=true" + - "--monitor-interval={{ .Values.volumeHealthMonitorInterval | default "60s" }}" + - "--timeout=180s" env: - name: ADDRESS value: /var/run/csi/csi.sock diff --git a/helm/csi-unity/values.yaml b/helm/csi-unity/values.yaml index 97c7af6..e8f6cf1 100644 --- a/helm/csi-unity/values.yaml +++ b/helm/csi-unity/values.yaml @@ -145,3 +145,9 @@ maxUnityVolumesPerNode: 0 # Default value: "" # Examples : "tenant2" , "tenant3" tenantName: "" + +# healthMonitorInterval: Interval of monitoring volume health condition +# Allowed values: Number followed by unit (s,m,h) +# Examples: 60s, 5m, 1h +# Default value: 60s +volumeHealthMonitorInterval: 60s diff --git a/service/controller.go b/service/controller.go index 0f570bb..93f20ee 100644 --- a/service/controller.go +++ b/service/controller.go @@ -1788,33 +1788,106 @@ func (s *service) ControllerGetVolume(ctx context.Context, volumeAPI := gounity.NewVolume(unity) vol, err := volumeAPI.FindVolumeById(ctx, volID) if err != nil { - if err == gounity.VolumeNotFoundError { + if err != gounity.VolumeNotFoundError { + return nil, status.Error(codes.NotFound, utils.GetMessageWithRunID(rid, "Find volume failed with error: %v", err)) + } + abnormal = true + message = "Volume not found" + } + + if !abnormal { + content := vol.VolumeContent + if len(content.HostAccessResponse) > 0 { + for _, hostaccess := range content.HostAccessResponse { + hostcontent := hostaccess.HostContent + hosts = append(hosts, hostcontent.ID) + } + } + + // check if volume is in ok state + if content.Health.Value != 5 { abnormal = true - message = "volume not found" + message = "Volume is not in ok state" } - return nil, status.Error(codes.NotFound, utils.GetMessageWithRunID(rid, "Find volume failed with error: %v", err)) + abnormal = false + message = "Volume is in ok state" } + } else { + fileAPI := gounity.NewFilesystem(unity) + isSnapshot := false + filesystem, err := fileAPI.FindFilesystemById(ctx, volID) - content := vol.VolumeContent - if len(content.HostAccessResponse) > 0 { - for _, hostaccess := range content.HostAccessResponse { - hostcontent := hostaccess.HostContent - hosts = append(hosts, hostcontent.ID) + if err != nil { + var snapResp *types.Snapshot + snapshotAPI := gounity.NewSnapshot(unity) + snapResp, err = snapshotAPI.FindSnapshotById(ctx, volID) + if err != nil { + if err != gounity.SnapshotNotFoundError { + return nil, status.Error(codes.NotFound, utils.GetMessageWithRunID(rid, "Find filesystem: %s failed with error: %v", volID, err)) + } + abnormal = true + message = "Filesystem not found" + } + + isSnapshot = true + if !abnormal { + filesystem, err = s.getFilesystemByResourceID(ctx, snapResp.SnapshotContent.StorageResource.Id, arrayID) + if err != nil { + return nil, err + } } } - // check if volume is in ready state - if content.Health.Value != 5 { - abnormal = true - message = "Volume is not in ok state" + if !abnormal { + nfsShareID := "" + for _, nfsShare := range filesystem.FileContent.NFSShare { + if isSnapshot { + if nfsShare.Path == NFSShareLocalPath && nfsShare.ParentSnap.Id == volID { + nfsShareID = nfsShare.Id + } + } else { + if nfsShare.Path == NFSShareLocalPath && nfsShare.ParentSnap.Id == "" { + nfsShareID = nfsShare.Id + } + } + } + + if nfsShareID != "" { + nfsShareResp, err := fileAPI.FindNFSShareById(ctx, nfsShareID) + if err != nil { + return nil, status.Error(codes.NotFound, utils.GetMessageWithRunID(rid, "Find NFS Share: %s failed. Error: %v", nfsShareID, err)) + } + readOnlyHosts := nfsShareResp.NFSShareContent.ReadOnlyHosts + readWriteHosts := nfsShareResp.NFSShareContent.ReadWriteHosts + readOnlyRootHosts := nfsShareResp.NFSShareContent.ReadOnlyRootAccessHosts + readWriteRootHosts := nfsShareResp.NFSShareContent.RootAccessHosts + + for _, host := range readOnlyHosts { + hosts = append(hosts, host.ID) + } + for _, host := range readWriteHosts { + hosts = append(hosts, host.ID) + } + for _, host := range readOnlyRootHosts { + hosts = append(hosts, host.ID) + } + for _, host := range readWriteRootHosts { + hosts = append(hosts, host.ID) + } + } } - abnormal = false - message = "Volume is in ok state" - } else { - abnormal = false - message = "FileSystem is in ok state" + // check if filesystem is in ok state + if !abnormal { + if filesystem.FileContent.Health.Value != 5 { + abnormal = true + message = "Filesystem is not in ok state" + } + abnormal = false + message = "Filesystem is in ok state" + } } + resp := &csi.ControllerGetVolumeResponse{ Volume: &csi.Volume{ VolumeId: req.GetVolumeId(), diff --git a/service/node.go b/service/node.go index 21b986a..d8f11ba 100644 --- a/service/node.go +++ b/service/node.go @@ -935,7 +935,7 @@ func (s *service) NodeGetVolumeStats( resp := &csi.NodeGetVolumeStatsResponse{ VolumeCondition: &csi.VolumeCondition{ Abnormal: true, - Message: "filesystem not found", + Message: "Filesystem not found", }, } return resp, nil @@ -950,7 +950,7 @@ func (s *service) NodeGetVolumeStats( resp := &csi.NodeGetVolumeStatsResponse{ VolumeCondition: &csi.VolumeCondition{ Abnormal: true, - Message: "volume not found", + Message: "Volume not found", }, } return resp, nil @@ -964,7 +964,7 @@ func (s *service) NodeGetVolumeStats( resp := &csi.NodeGetVolumeStatsResponse{ VolumeCondition: &csi.VolumeCondition{ Abnormal: true, - Message: "volume path not mounted", + Message: "Volume path is not mounted", }, } return resp, nil @@ -976,7 +976,7 @@ func (s *service) NodeGetVolumeStats( resp := &csi.NodeGetVolumeStatsResponse{ VolumeCondition: &csi.VolumeCondition{ Abnormal: true, - Message: "volume path not accessible", + Message: "Volume path is not accessible", }, } return resp, nil diff --git a/test/unit-test/features/unit.feature b/test/unit-test/features/unit.feature index dd5d595..67a20d2 100644 --- a/test/unit-test/features/unit.feature +++ b/test/unit-test/features/unit.feature @@ -475,6 +475,22 @@ Feature: CSI interface When I call Controller Expand Volume "2" with volume "" Then the error message should contain "required" + Scenario: Controller get volume for FC protocol + Given a CSI service + And a basic block volume request name "gdtest-vol26" arrayId "Array1-Id" protocol "FC" + When I call CreateVolume + Then there are no errors + When I call Controller Get Volume "gdtest-vol26" + Then there are no errors + + Scenario: Controller get volume for NFS protocol + Given a CSI service + And a basic block volume request name "gdtest-vol26" arrayId "Array1-Id" protocol "NFS" + When I call CreateVolume + Then there are no errors + When I call Controller Get Volume "gdtest-vol26" + Then there are no errors + Scenario: Node stage, publish, unpublish and unstage volume Given a CSI service And a basic block volume request name "gdtest-vol30" arrayId "Array1-Id" protocol "FC" size "5" diff --git a/test/unit-test/unit_test.go b/test/unit-test/unit_test.go index e578c25..bce3057 100644 --- a/test/unit-test/unit_test.go +++ b/test/unit-test/unit_test.go @@ -36,6 +36,7 @@ type feature struct { validateVolumeCapabilitiesRequest *csi.ValidateVolumeCapabilitiesRequest controllerGetCapabilitiesRequest *csi.ControllerGetCapabilitiesRequest controllerExpandVolumeRequest *csi.ControllerExpandVolumeRequest + controllerGetVolumeRequest *csi.ControllerGetVolumeRequest nodePublishVolumeRequest *csi.NodePublishVolumeRequest nodeUnpublishVolumeRequest *csi.NodeUnpublishVolumeRequest nodeStageVolumeRequest *csi.NodeStageVolumeRequest @@ -798,6 +799,25 @@ func (f *feature) iCallControllerExpandVolumeWithVolume(new_size int, volID stri return nil } +//iCallControllerGetVolume - Test case for controller get volume with volume id as parameter +func (f *feature) iCallControllerGetVolumeWithVolume(volID string) error { + f.controllerGetVolumeRequest = nil + req := new(csi.ControllerGetVolumeRequest) + req.VolumeId = volID + f.controllerGetVolumeRequest = req + + ctx := context.Background() + client := csi.NewControllerClient(grpcClient) + _, err := client.ControllerGetVolume(ctx, req) + if err != nil { + fmt.Printf("Controller get volume failed: %s\n", err.Error()) + f.addError(err) + } else { + fmt.Printf("Controller get volume completed successfully\n") + } + return nil +} + //whenICallNodePublishVolume - Test case for node publish volume func (f *feature) whenICallNodePublishVolume(fsType, readonly string) error { f.nodePublishVolumeRequest = nil