diff --git a/.gitignore b/.gitignore index a79b9992..4f079119 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ csm-common.mk csi-powermax !csi-powermax/ semver.mk +vendor/ # files created by IDEs .vscode diff --git a/Dockerfile b/Dockerfile index 6cb2c945..e7f41dfd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# Copyright © 2020-2025 Dell Inc. or its subsidiaries. All Rights Reserved. +# Copyright © 2020-2026 Dell Inc. or its subsidiaries. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -10,24 +10,24 @@ # See the License for the specific language governing permissions and # limitations under the License. # -ARG GOPROXY ARG GOIMAGE ARG BASEIMAGE -ARG DIGEST +ARG VERSION="2.16.0" # Stage to build the driver FROM $GOIMAGE as builder -ARG GOPROXY +ARG VERSION RUN mkdir -p /go/src COPY ./ /go/src/ WORKDIR /go/src/ RUN CGO_ENABLED=0 \ - make build + make build IMAGE_VERSION=$VERSION # Stage to build the driver image FROM $BASEIMAGE AS final -COPY --from=builder /go/src/csi-powermax . -COPY "csi-powermax.sh" . +ARG VERSION +COPY --from=builder /go/src/csi-powermax /csi-powermax +COPY csi-powermax.sh /csi-powermax.sh ENTRYPOINT ["/csi-powermax.sh"] RUN chmod +x /csi-powermax.sh LABEL vendor="Dell Technologies" \ @@ -35,7 +35,7 @@ LABEL vendor="Dell Technologies" \ name="csi-powermax" \ summary="CSI Driver for Dell EMC PowerMax" \ description="CSI Driver for provisioning persistent storage from Dell EMC PowerMax" \ - release="1.15.0" \ - version="2.15.0" \ + release="1.16.0" \ + version=$VERSION \ license="Apache-2.0" COPY ./licenses /licenses diff --git a/Makefile b/Makefile index d4916b14..29c44744 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright © 2020-2024 Dell Inc. or its subsidiaries. All Rights Reserved. +# Copyright © 2020-2026 Dell Inc. or its subsidiaries. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -10,9 +10,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # -include overrides.mk +include images.mk -all: build unit-test +all: build + +# This will be overridden during image build. +IMAGE_VERSION ?= 0.0.0 +LDFLAGS = "-X main.ManifestSemver=$(IMAGE_VERSION)" # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -21,38 +25,14 @@ else GOBIN=$(shell go env GOBIN) endif -format: - @gofmt -w -s . - clean: - rm -f core/core_generated.go go-code-tester *.log *.out cover* + rm -f core/core_generated.go go-code-tester *.log *.out cover* semver.mk csm-common.mk + rm -rf csm-temp-repo vendor go clean make -C csireverseproxy clean -build: - go generate - CGO_ENABLED=0 go build - -# Generates the docker container (but does not push) -docker: - go generate - go run core/semver/semver.go -f mk >semver.mk - make -f docker.mk docker - # build the reverseproxy container as part of this target - ( cd csireverseproxy; make docker ) - - -# Generates the docker container without cache (but does not push) -docker-no-cache: - go generate - go run core/semver/semver.go -f mk >semver.mk - make -f docker.mk docker-no-cache - # build the reverseproxy container as part of this target - ( cd csireverseproxy; make docker-no-cache ) - -# Pushes container to the repository -push: docker - make -f docker.mk push +build: generate + GOOS=linux CGO_ENABLED=0 go build -mod=vendor -ldflags $(LDFLAGS) # Run unit tests unit-test: go-code-tester @@ -61,16 +41,11 @@ unit-test: go-code-tester # Run BDD tests. Need to be root to run as tests require some system access, need to fix bdd-test: - ( cd service; go clean -cache; CGO_ENABLED=0 go test -run TestGoDog -v -coverprofile=c.out ./... ) + (cd service; go clean -cache; CGO_ENABLED=0 go test -run TestGoDog -v -coverprofile=c.out ./...) # Linux only; populate env.sh with the hardware parameters integration-test: - ( cd test/integration; sh run.sh ) - -version: - go generate - go run core/semver/semver.go -f mk >semver.mk - make -f docker.mk version + (cd test/integration; sh run.sh) gosec: ifeq (, $(shell which gosec)) @@ -81,23 +56,11 @@ else endif @echo "Logs are stored at gosec.log, Outputfile at gosecresults.csv" -.PHONY: actions action-help -actions: ## Run all GitHub Action checks that run on a pull request creation - @echo "Running all GitHub Action checks for pull request events..." - @act -l | grep -v ^Stage | grep pull_request | grep -v image_security_scan | awk '{print $$2}' | while read WF; do \ - echo "Running workflow: $${WF}"; \ - act pull_request --no-cache-server --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --job "$${WF}"; \ - done - go-code-tester: - curl -o go-code-tester -L https://raw.githubusercontent.com/dell/common-github-actions/main/go-code-tester/entrypoint.sh \ - && chmod +x go-code-tester + git clone --depth 1 git@github.com:CSM/actions.git temp-repo + cp temp-repo/go-code-tester/entrypoint.sh ./go-code-tester + chmod +x go-code-tester + rm -rf temp-repo -action-help: ## Echo instructions to run one specific workflow locally - @echo "GitHub Workflows can be run locally with the following command:" - @echo "act pull_request --no-cache-server --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --job " - @echo "" - @echo "Where '' is a Job ID returned by the command:" - @echo "act -l" - @echo "" - @echo "NOTE: if act is not installed, it can be downloaded from https://github.com/nektos/act" +mocks: + go generate ./... diff --git a/README.md b/README.md index f4df9ee1..b7571b7b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ +# :lock: **Important Notice** +Starting with the release of **Container Storage Modules v1.16.0**, this repository will no longer be maintained as an open source project. Future development will continue under a closed source model. This change reflects our commitment to delivering even greater value to our customers by enabling faster innovation and more deeply integrated features with the Dell storage portfolio.
+For existing customers using Dell’s Container Storage Modules, you will continue to receive: +* **Ongoing Support & Community Engagement**
+ You will continue to receive high-quality support through Dell Support and our community channels. Your experience of engaging with the Dell community remains unchanged. +* **Streamlined Deployment & Updates**
+ Deployment and update processes will remain consistent, ensuring a smooth and familiar experience. +* **Access to Documentation & Resources**
+ All documentation and related materials will remain publicly accessible, providing transparency and technical guidance. +* **Continued Access to Current Open Source Version**
+ The current open-source version will remain available under its existing license for those who rely on it. + +Moving to a closed source model allows Dell’s development team to accelerate feature delivery and enhance integration across our Enterprise Kubernetes Storage solutions ultimately providing a more seamless and robust experience.
+We deeply appreciate the contributions of the open source community and remain committed to supporting our customers through this transition.
+ +For questions or access requests, please contact the maintainers via [Dell Support](https://www.dell.com/support/kbdoc/en-in/000188046/container-storage-interface-csi-drivers-and-container-storage-modules-csm-how-to-get-support). + # CSI Driver for Dell PowerMax [![Go Report Card](https://goreportcard.com/badge/github.com/dell/csi-powermax?style=flat-square)](https://goreportcard.com/report/github.com/dell/csi-powermax) @@ -44,4 +61,3 @@ For a complete list of dependencies, please visit [Prerequisites](https://dell.g ## Documentation For more detailed information on the driver, please refer to [Container Storage Modules documentation](https://dell.github.io/csm-docs/). - diff --git a/core/semver/semver.go b/core/semver/semver.go index 007dd303..ff085449 100644 --- a/core/semver/semver.go +++ b/core/semver/semver.go @@ -357,7 +357,7 @@ var OSExit = func(code int) { // GetExitError is a wrapper around exec.ExitError var GetExitError = func(err error) (e *exec.ExitError, ok bool) { e, ok = err.(*exec.ExitError) - return + return e, ok } // GetStatusError is a wrapper around syscall.WaitStatus diff --git a/csireverseproxy/Dockerfile b/csireverseproxy/Dockerfile index 1a1c7d41..139afc8c 100644 --- a/csireverseproxy/Dockerfile +++ b/csireverseproxy/Dockerfile @@ -1,4 +1,4 @@ -# Copyright © 2020-2025 Dell Inc. or its subsidiaries. All Rights Reserved. +# Copyright © 2020-2026 Dell Inc. or its subsidiaries. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ ############################ ARG GOIMAGE ARG BASEIMAGE -ARG GOPROXY +ARG VERSION="2.15.0" FROM $GOIMAGE as builder @@ -42,20 +42,21 @@ RUN mkdir -p /go/src/csireverseproxy COPY . /go/src/csireverseproxy WORKDIR /go/src/csireverseproxy -RUN CGO_ENABLED=0 go build +RUN CGO_ENABLED=0 go build -mod=vendor ############################ # STEP 2 build a small image ############################ FROM $BASEIMAGE as final +ARG VERSION LABEL vendor="Dell Technologies" \ maintainer="Dell Technologies" \ name="csipowermax-reverseproxy" \ summary="CSI PowerMax Reverse Proxy" \ description="CSI PowerMax Reverse Proxy which helps manage connections with Unisphere for PowerMax" \ - release="1.13.0" \ - version="2.12.0" \ + release="1.16.0" \ + version=$VERSION \ license="Apache-2.0" COPY licenses /licenses # Import from builder. diff --git a/csireverseproxy/Makefile b/csireverseproxy/Makefile index 22cc5f36..c24f5464 100644 --- a/csireverseproxy/Makefile +++ b/csireverseproxy/Makefile @@ -1,42 +1,22 @@ -# Copyright © 2020-2025 Dell Inc. or its subsidiaries. All Rights Reserved. +# Copyright © 2020-2026 Dell Inc. or its subsidiaries. All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. # -include overrides.mk + +include images.mk run: @./run.sh -format: - @gofmt -w -s . - clean: rm -f cover.out coverage.txt + rm -rf vendor csm-common.mk csm-temp-repo go clean -build: - CGO_ENABLED=0 go build - -docker: - go run ../core/semver/semver.go -f mk >semver.mk - make -f docker.mk docker - -docker-no-cache: - make -f docker.mk docker-no-cache - -docker-push: docker - make -f docker.mk push +build: vendor + CGO_ENABLED=0 go build -mod=vendor unit-test: go test -v -coverprofile c1.out ./... - -download-csm-common: - curl -O -L https://raw.githubusercontent.com/dell/csm/main/config/csm-common.mk diff --git a/csireverseproxy/docker.mk b/csireverseproxy/docker.mk deleted file mode 100644 index 0a51b7f8..00000000 --- a/csireverseproxy/docker.mk +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# docker makefile, included from Makefile, will build/push images with docker or podman -# - -# Includes the following generated file to get semantic version information -include semver.mk - -ifdef NOTES - RELNOTE="-$(NOTES)" -else - RELNOTE= -endif - -ifeq ($(PROXY_IMAGETAG),) -PROXY_IMAGETAG="v$(MAJOR).$(MINOR).$(PATCH)$(RELNOTE)" -endif - - -docker: download-csm-common - $(eval include csm-common.mk) - @echo "Building: $(REGISTRY)/$(PROXY_IMAGENAME):$(PROXY_IMAGETAG)" - $(BUILDER) build --pull $(NOCACHE) -t "$(REGISTRY)/$(PROXY_IMAGENAME):$(PROXY_IMAGETAG)" --target $(BUILDSTAGE) --build-arg GOPROXY --build-arg BASEIMAGE=$(CSM_BASEIMAGE) --build-arg GOIMAGE=$(DEFAULT_GOIMAGE) . - -docker-no-cache: - @echo "Building with --no-cache ..." - @make docker NOCACHE=--no-cache - -push: - @echo "Pushing: $(REGISTRY)/$(PROXY_IMAGENAME):$(PROXY_IMAGETAG)" - $(BUILDER) push "$(REGISTRY)/$(PROXY_IMAGENAME):$(PROXY_IMAGETAG)" - -download-csm-common: - curl -O -L https://raw.githubusercontent.com/dell/csm/main/config/csm-common.mk \ No newline at end of file diff --git a/csireverseproxy/go.mod b/csireverseproxy/go.mod index 8cf9de81..ace63b55 100644 --- a/csireverseproxy/go.mod +++ b/csireverseproxy/go.mod @@ -1,73 +1,83 @@ module github.com/dell/csi-powermax/csireverseproxy/v2 -go 1.25 +go 1.25.0 require ( - github.com/dell/gopowermax/v2 v2.11.0 - github.com/fsnotify/fsnotify v1.8.0 + github.com/dell/csmlog v1.0.0 + github.com/dell/gopowermax/v2 v2.12.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/gorilla/mux v1.8.1 - github.com/kubernetes-csi/csi-lib-utils v0.20.0 + github.com/kubernetes-csi/csi-lib-utils v0.23.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/viper v1.19.0 - github.com/stretchr/testify v1.11.0 - go.uber.org/mock v0.5.0 + github.com/spf13/viper v1.21.0 + github.com/stretchr/testify v1.11.1 + go.uber.org/mock v0.6.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.32.1 - k8s.io/apimachinery v0.32.1 - k8s.io/client-go v0.32.1 + k8s.io/api v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/client-go v0.34.2 ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.12.1 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.22.3 // indirect + github.com/go-openapi/jsonreference v0.21.3 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/magiconair/properties v1.8.9 // indirect - github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/onsi/ginkgo/v2 v2.25.3 // indirect + github.com/onsi/gomega v1.39.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/sagikazarmark/locafero v0.7.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.9.0 // indirect - google.golang.org/protobuf v1.36.4 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect - k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/csireverseproxy/go.sum b/csireverseproxy/go.sum index ae0cb771..570ba447 100644 --- a/csireverseproxy/go.sum +++ b/csireverseproxy/go.sum @@ -1,49 +1,74 @@ +github.com/dell/csmlog v1.0.0 h1:EzW+nMJBD0QTNP88OoaAJUOMVilS9cWkO248BTcJt/4= +github.com/dell/csmlog v1.0.0/go.mod h1:7rBzSv9xF5t233+J+9vkStjFsmyYO3L/B9tDTy3+9ZU= +github.com/dell/gopowermax/v2 v2.12.0 h1:6ARWgc+KGY3ns2misQt2yXTyiP+y1E6X/jZWABosAEw= +github.com/dell/gopowermax/v2 v2.12.0/go.mod h1:f/wnjHWZ6H4nJE/w2VTu547yBvZxMQjlz3oc2twb2Fk= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dell/gopowermax/v2 v2.11.0 h1:wxuTxZVgPYYIotwvuvQYHb2MKXAZWcSiRQnG3YuXlbk= -github.com/dell/gopowermax/v2 v2.11.0/go.mod h1:vnH9aCT+/TxPROPvP4mkbcAOnW8rSJC6r4nWjuyiR0Q= -github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= -github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8= +github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo= +github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -52,82 +77,81 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kubernetes-csi/csi-lib-utils v0.20.0 h1:JTvHRJugn+cByMnIU4nCnqPqOOUhuPzhlLqRvenwjDA= -github.com/kubernetes-csi/csi-lib-utils v0.20.0/go.mod h1:3b/HFVURW11oxV/gUAKyhhkvFpxXO/zRdvh1wdEfCZY= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/kubernetes-csi/csi-lib-utils v0.23.0 h1:070SC4ubEvJpQak0ibxgv7l5dUoDVdqKyktam6zkm4s= +github.com/kubernetes-csi/csi-lib-utils v0.23.0/go.mod h1:H5+JRXAvb7lpC4nrddI7sfQfaXA1O8Tek3uNrTIx1/g= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw= +github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= +github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q= +github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -135,57 +159,59 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= -k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= -k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= -k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= -k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= -sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/csireverseproxy/helper.mk b/csireverseproxy/helper.mk new file mode 100644 index 00000000..22dec8d7 --- /dev/null +++ b/csireverseproxy/helper.mk @@ -0,0 +1,13 @@ +# Copyright © 2026 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. + +download-csm-common: + git clone --depth 1 git@github.com:CSM/csm.git temp-repo + cp temp-repo/config/csm-common.mk . + rm -rf temp-repo + +vendor: + GOPRIVATE=github.com go mod vendor diff --git a/csireverseproxy/images.mk b/csireverseproxy/images.mk new file mode 100644 index 00000000..21cefeac --- /dev/null +++ b/csireverseproxy/images.mk @@ -0,0 +1,21 @@ +# Copyright © 2026 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. + +include overrides.mk +include helper.mk + +images: download-csm-common vendor + $(eval include csm-common.mk) + @echo "Building: $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" + $(BUILDER) build --pull $(NOCACHE) -t "$(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" --build-arg BASEIMAGE=$(CSM_BASEIMAGE) --build-arg GOIMAGE=$(DEFAULT_GOIMAGE) . + +images-no-cache: + @echo "Building with --no-cache ..." + @make images NOCACHE=--no-cache + +push: + @echo "Pushing: $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" + $(BUILDER) push "$(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" diff --git a/csireverseproxy/main.go b/csireverseproxy/main.go index 8afe1288..53e3e0ad 100644 --- a/csireverseproxy/main.go +++ b/csireverseproxy/main.go @@ -31,7 +31,8 @@ import ( "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/proxy" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/utils" - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" + "github.com/sirupsen/logrus" "github.com/kubernetes-csi/csi-lib-utils/leaderelection" @@ -41,6 +42,8 @@ import ( corev1 "k8s.io/api/core/v1" ) +var log = csmlog.GetLogger() + // RevProxy - interface which is implemented by the different proxy implementations type RevProxy interface { ServeReverseProxy(res http.ResponseWriter, req *http.Request) @@ -130,18 +133,18 @@ func (s *Server) Config() *config.ProxyConfig { func (s *Server) Setup(k8sUtils k8sutils.UtilsInterface) error { // Read the config from secret if secret provided if getEnv(common.EnvReverseProxyUseSecret, "false") == "true" { - log.Printf("Reading config using secret") + log.Info("Reading config using secret") vs := viper.New() proxySecret, err := config.ReadConfigFromSecret(vs) if err != nil { - log.Printf("Error while reading config from secret: %v\n", err) + log.Errorf("Error while reading config from secret: %v", err) return err } proxyConfig, err := config.NewProxyConfigFromSecret(proxySecret, k8sUtils) if err != nil { - log.Printf("Error while creating proxy config from secret: %v\n", err) + log.Errorf("Error while creating proxy config from secret: %v", err) return err } s.CertFile = filepath.Join(s.Opts.TLSCertDir, s.Opts.CertFile) @@ -150,11 +153,11 @@ func (s *Server) Setup(k8sUtils k8sutils.UtilsInterface) error { s.Port = proxyConfig.Port proxy, err := proxy.NewProxy(*proxyConfig) if err != nil { - log.Printf("Error while creating proxy instance from secret: %v\n", err) + log.Errorf("Error while creating proxy instance from secret: %v", err) return err } - log.Infof("Setting up watcher for mounted secret") + log.Info("Setting up watcher for mounted secret") s.SetupConfigWatcher(k8sUtils, vs, s.configChangeSecret) // params config map @@ -162,7 +165,7 @@ func (s *Server) Setup(k8sUtils k8sutils.UtilsInterface) error { paramsFilePath := getEnv(common.EnvPowermaxConfigPath, "") paramsConfig, err := config.ReadParamsConfigMapFromPath(paramsFilePath, vcp) if err != nil { - log.Printf("Error while reading from params config map: %v\n", err) + log.Errorf("Error while reading from params config map: %v", err) return err } if paramsConfig.Port != "" { @@ -171,7 +174,7 @@ func (s *Server) Setup(k8sUtils k8sutils.UtilsInterface) error { proxyConfig.Port = paramsConfig.Port } - log.Infof("Setting up watcher for mounted params config map") + log.Info("Setting up watcher for mounted params config map") s.SetupConfigWatcher(k8sUtils, vcp, s.configChangeParamsConfigMap) s.Proxy = proxy @@ -180,7 +183,7 @@ func (s *Server) Setup(k8sUtils k8sutils.UtilsInterface) error { } else { // Read the config from config map - log.Printf("Reading config using config map") + log.Info("Reading config using config map") vcm := viper.New() proxyConfigMap, err := config.ReadConfig(s.Opts.ConfigFileName, s.Opts.ConfigDir, vcm) if err != nil { @@ -196,11 +199,11 @@ func (s *Server) Setup(k8sUtils k8sutils.UtilsInterface) error { s.Port = proxyConfig.Port proxy, err := proxy.NewProxy(*proxyConfig) if err != nil { - log.Printf("Error while creating proxy instance from config map: %v\n", err) + log.Errorf("Error while creating proxy instance from config map: %v", err) return err } - log.Infof("Setting up watcher for mounted reverse proxy config map") + log.Info("Setting up watcher for mounted reverse proxy config map") s.SetupConfigWatcher(k8sUtils, vcm, s.configChangeConfigMap) @@ -263,36 +266,36 @@ func (s *Server) SignalHandler(k8sUtils k8sutils.UtilsInterface) { func updateRevProxyLogParams(format, logLevel string) { logFormatFromConfig := strings.ToLower(format) - var formatter log.Formatter + var formatter logrus.Formatter // Use text logger as default - formatter = &log.TextFormatter{ + formatter = &logrus.TextFormatter{ DisableColors: true, FullTimestamp: true, } if strings.EqualFold(logFormatFromConfig, "json") { - formatter = &log.JSONFormatter{ + formatter = &logrus.JSONFormatter{ TimestampFormat: time.RFC3339Nano, } } else if !strings.EqualFold(logFormatFromConfig, "text") && (logFormatFromConfig != "") { - log.Printf("Unsupported logFormat: %s supplied. Defaulting to text", logFormatFromConfig) + log.Infof("Unsupported logFormat: %s supplied. Defaulting to text", logFormatFromConfig) } - level := log.DebugLevel // Use debug as default + level := csmlog.DebugLevel // Use debug as default if logLevel != "" { logLevel = strings.ToLower(logLevel) - l, err := log.ParseLevel(logLevel) + l, err := csmlog.ParseLevel(logLevel) if err != nil { - log.WithError(err).Errorf("logLevel %s value not recognized, error: %s, Setting to default: %s", + log.Errorf("logLevel %s value not recognized, error: %s, Setting to default: %s", logLevel, err.Error(), level) } else { level = l } } else { - log.Print("Couldn't read logLevel from config file. Using debug level as default") + log.Info("Couldn't read logLevel from config file. Using debug level as default") } setLogFormatAndLevel(formatter, level) } -func setLogFormatAndLevel(logFormat log.Formatter, level log.Level) { +func setLogFormatAndLevel(logFormat logrus.Formatter, level csmlog.Level) { log.SetFormatter(logFormat) log.Infof("Setting log level to %v", level) log.SetLevel(level) diff --git a/csireverseproxy/main_test.go b/csireverseproxy/main_test.go index 4bec4e33..9f5d8ee7 100644 --- a/csireverseproxy/main_test.go +++ b/csireverseproxy/main_test.go @@ -41,10 +41,10 @@ import ( "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/k8sutils" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/servermock" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/utils" + "github.com/dell/csmlog" "github.com/kubernetes-csi/csi-lib-utils/leaderelection" "github.com/sirupsen/logrus" - log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -152,7 +152,7 @@ func startTestServer(config string) error { } server, err = startServer(k8sUtils, serverOpts) if err == nil { - log.Printf("started revproxy server successfully on port %s", serverOpts.Port) + log.Infof("started revproxy server successfully on port %s", serverOpts.Port) } return err @@ -456,7 +456,7 @@ func TearDownSetup() { log.Info("Removing the certs, temp files") err := utils.RemoveTempFiles() if err != nil { - log.Fatalln(err.Error()) + log.Fatal(err.Error()) os.Exit(1) } log.Info("Proxy and mock servers stopped successfully") @@ -1278,16 +1278,16 @@ func TestRun(t *testing.T) { func TestUpdateRevProxyLogParams(t *testing.T) { tests := []struct { - name string - format string - logLevel string - expectLog log.Level + name string + format string + logLevel string + expectLevel csmlog.Level }{ - {"Default values", "", "", log.DebugLevel}, - {"Valid JSON format", "json", "info", log.InfoLevel}, - {"Valid Text format", "text", "warn", log.WarnLevel}, - {"Invalid format defaults to text", "xml", "error", log.ErrorLevel}, - {"Invalid log level defaults to debug", "json", "invalid", log.DebugLevel}, + {"Default values", "", "", csmlog.DebugLevel}, + {"Valid JSON format", "json", "info", csmlog.InfoLevel}, + {"Valid Text format", "text", "warn", csmlog.WarnLevel}, + {"Invalid format defaults to text", "xml", "error", csmlog.ErrorLevel}, + {"Invalid log level defaults to debug", "json", "invalid", csmlog.DebugLevel}, } for _, tt := range tests { @@ -1299,7 +1299,7 @@ func TestUpdateRevProxyLogParams(t *testing.T) { updateRevProxyLogParams(tt.format, tt.logLevel) // Validate log level - assert.Equal(t, tt.expectLog, logrus.GetLevel()) + assert.Equal(t, tt.expectLevel, csmlog.GetLevel()) }) } } diff --git a/csireverseproxy/overrides.mk b/csireverseproxy/overrides.mk index 8516ea9a..81013344 100644 --- a/csireverseproxy/overrides.mk +++ b/csireverseproxy/overrides.mk @@ -1,51 +1,12 @@ -# Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. +# Copyright © 2026 Dell Inc. or its subsidiaries. All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. -# overrides file -# this file, included from the Makefile, will overlay default values with environment variables -# - -# DEFAULT values -# ubi9/ubi-micro:9.2-13 -DEFAULT_GOIMAGE=$(shell sed -En 's/^go (.*)$$/\1/p' go.mod) -DEFAULT_REGISTRY="sample_registry" -DEFAULT_IMAGENAME="csipowermax-reverseproxy" -DEFAULT_BUILDSTAGE="final" -DEFAULT_IMAGETAG="" - -# set the GOIMAGE if needed -ifeq ($(GOIMAGE),) -export GOIMAGE="$(DEFAULT_GOIMAGE)" -endif - -# set the REGISTRY if needed -ifeq ($(REGISTRY),) -export REGISTRY="$(DEFAULT_REGISTRY)" -endif - -# set the PROXY_IMAGENAME if needed -ifeq ($(PROXY_IMAGENAME),) -export PROXY_IMAGENAME="$(DEFAULT_IMAGENAME)" -endif - -#set the PROXY_IMAGETAG if needed -ifneq ($(DEFAULT_IMAGETAG), "") -export PROXY_IMAGETAG="$(DEFAULT_IMAGETAG)" -endif - -# set the BUILDSTAGE if needed -ifeq ($(BUILDSTAGE),) -export BUILDSTAGE="$(DEFAULT_BUILDSTAGE)" -endif +IMAGE_REGISTRY="sample_registry" +IMAGE_NAME="csipowermax-reverseproxy" +IMAGE_TAG?=$(shell date +%Y%m%d%H%M%S) # figure out if podman or docker should be used (use podman if found) ifneq (, $(shell which podman 2>/dev/null)) @@ -59,17 +20,12 @@ overrides-help: @echo @echo "The following environment variables can be set to control the build" @echo - @echo "GOIMAGE - The version of Go to build with, default is: $(DEFAULT_GOIMAGE)" - @echo " Current setting is: $(GOIMAGE)" - @echo "REGISTRY - The registry to push images to, default is: $(DEFAULT_REGISTRY)" - @echo " Current setting is: $(REGISTRY)" - @echo "PROXY_IMAGENAME - The image name to be built, defaut is: $(DEFAULT_IMAGENAME)" - @echo " Current setting is: $(PROXY_IMAGENAME)" - @echo "PROXY_IMAGETAG - The image tag to be built, default is an empty string which will determine the tag by examining annotated tags in the repo." - @echo " Current setting is: $(PROXY_IMAGETAG)" - @echo "BUILDSTAGE - The Dockerfile build stage to execute, default is: $(DEFAULT_BUILDSTAGE)" - @echo " Stages can be found by looking at the Dockerfile" - @echo " Current setting is: $(BUILDSTAGE)" + @echo "IMAGE_REGISTRY - The registry to push images to." + @echo " Current setting is: $(IMAGE_REGISTRY)" + @echo "IMAGE_NAME - The image name to be built." + @echo " Current setting is: $(IMAGE_NAME)" + @echo "IMAGE_TAG - The image tag to be built, default is the current date." + @echo " Current setting is: $(IMAGE_TAG)" @echo diff --git a/csireverseproxy/pkg/cache/cache.go b/csireverseproxy/pkg/cache/cache.go index 6240c8c9..e9d23557 100644 --- a/csireverseproxy/pkg/cache/cache.go +++ b/csireverseproxy/pkg/cache/cache.go @@ -18,7 +18,7 @@ import ( "sync" "time" - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" ) // Cache is the interface for a timed key-value store @@ -28,7 +28,10 @@ type Cache interface { Remove(string) } -var _ Cache = new(cache) +var ( + _ Cache = new(cache) + log = csmlog.GetLogger() +) // New creates instance of timed key-value store. func New(name string, ttl time.Duration) Cache { diff --git a/csireverseproxy/pkg/common/common.go b/csireverseproxy/pkg/common/common.go index cc0cb398..c756951a 100644 --- a/csireverseproxy/pkg/common/common.go +++ b/csireverseproxy/pkg/common/common.go @@ -22,7 +22,7 @@ import ( "sync/atomic" "time" - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" ) const ( @@ -32,8 +32,12 @@ const ( MaximumFailureDurationForFailover = 1 * time.Minute // MinimumSuccessCount - minimum number of successes to reset the proxy health MinimumSuccessCount = 5 + // MaximumConsecutiveFailureCountForFailover - max consecutive failure count irrespective of time to do failover. + MaximumConsecutiveFailureCountForFailover = 10 ) +var log = csmlog.GetLogger() + // Credentials represent a pair of username and password type Credentials struct { UserName string `json:"username"` @@ -45,30 +49,33 @@ type Credentials struct { type ProxyHealth interface { ReportFailure() bool ReportSuccess() - SetThreshold(int, int, time.Duration) + SetThreshold(int, int, int, time.Duration) HasDeteriorated() bool } // NewProxyHealth - creates and returns a new proxyHealth instance func NewProxyHealth() ProxyHealth { ph := &proxyHealth{ - minimumFailureCount: MinimumFailureCountForFailover, - maximumFailureDuration: MaximumFailureDurationForFailover, - minimumSuccessCount: MinimumSuccessCount, + minimumFailureCount: MinimumFailureCountForFailover, + maximumFailureDuration: MaximumFailureDurationForFailover, + minimumSuccessCount: MinimumSuccessCount, + maximumConsFailureCount: MaximumConsecutiveFailureCountForFailover, } ph.hasDeteriorated.Store(false) return ph } type proxyHealth struct { - failureCount int - successCount int - firstFailure time.Time - minimumFailureCount int - minimumSuccessCount int - maximumFailureDuration time.Duration - healthMutex sync.Mutex - hasDeteriorated atomic.Value + failureCount int + successCount int + consecutiveFailureCount int + firstFailure time.Time + minimumFailureCount int + minimumSuccessCount int + maximumConsFailureCount int + maximumFailureDuration time.Duration + healthMutex sync.Mutex + hasDeteriorated atomic.Value } func (health *proxyHealth) reset() { @@ -80,9 +87,15 @@ func (health *proxyHealth) reset() { // ReportSuccess resets the proxy health if the successCount // reaches a certain threshold func (health *proxyHealth) ReportSuccess() { + health.healthMutex.Lock() + defer health.healthMutex.Unlock() + // reset the consecutive counter for success + if health.consecutiveFailureCount > 0 { + health.consecutiveFailureCount = 0 + } + + // if the proxy is already in a deteriorated state, check for success count and reset based on minimumSuccessCount if health.hasDeteriorated.Load().(bool) { - health.healthMutex.Lock() - defer health.healthMutex.Unlock() if health.successCount < health.minimumSuccessCount { health.successCount++ } else { @@ -92,7 +105,7 @@ func (health *proxyHealth) ReportSuccess() { } // ReportFailure - updates the health of the proxy in case of failure -// or clears the failure count if error threshold is not met after maximum +// or clears the failure count if the error threshold is not met after maximum // failure duration func (health *proxyHealth) ReportFailure() bool { health.healthMutex.Lock() @@ -101,15 +114,26 @@ func (health *proxyHealth) ReportFailure() bool { health.hasDeteriorated.Store(true) health.firstFailure = time.Now() health.failureCount = 1 + health.consecutiveFailureCount++ return false } + health.consecutiveFailureCount++ + // if the continuous failure count is greater than the maximum failure count, return true + if health.consecutiveFailureCount > health.maximumConsFailureCount { + log.Debugf("consecutive failure reached!") + health.consecutiveFailureCount = 0 + health.reset() + return true + } timeElapsed := time.Since(health.firstFailure) + // if the time elapsed is greater than the maximum failure duration, check for failure count if timeElapsed < health.maximumFailureDuration { health.failureCount++ return false } failureCount := health.failureCount health.reset() + // if the failure count is less than the minimum failure counts, return false as a threshold not met if failureCount < health.minimumFailureCount { return false } @@ -117,10 +141,11 @@ func (health *proxyHealth) ReportFailure() bool { } // SetThreshold - sets the threshold values for proxy health -func (health *proxyHealth) SetThreshold(failureCount, successCount int, duration time.Duration) { +func (health *proxyHealth) SetThreshold(failureCount, consecutiveFC, successCount int, duration time.Duration) { health.maximumFailureDuration = duration health.minimumFailureCount = failureCount health.minimumSuccessCount = successCount + health.maximumConsFailureCount = consecutiveFC } // HasDeteriorated returns true if the proxyHealth object has recorded even a single failure diff --git a/csireverseproxy/pkg/common/common_test.go b/csireverseproxy/pkg/common/common_test.go index 59b926be..65a0b6e2 100644 --- a/csireverseproxy/pkg/common/common_test.go +++ b/csireverseproxy/pkg/common/common_test.go @@ -53,10 +53,11 @@ func reportSuccess(proxyHealth ProxyHealth, count int) { func TestProxyHealth(t *testing.T) { failureCount := 5 successCount := 2 + consFailureCount := 20 maxDuration := time.Second proxyHealth := NewProxyHealth() - proxyHealth.SetThreshold(failureCount, successCount, maxDuration) + proxyHealth.SetThreshold(failureCount, consFailureCount, successCount, maxDuration) shouldFailover := reportFailure(proxyHealth, failureCount, maxDuration) if !shouldFailover { @@ -72,13 +73,62 @@ func TestProxyHealth(t *testing.T) { } } +func TestProxyFailoverSlow(t *testing.T) { + failureCount := 10 + failAPIFreq := 1 * time.Second // frequency of failing API calls + + proxyHealth := NewProxyHealth() + proxyHealth.SetThreshold(10, 10, 1, 1*time.Minute) + // Report failures concurrently with a shorter sleep duration + var wg sync.WaitGroup + for i := 0; i < failureCount; i++ { + time.Sleep(failAPIFreq) // Shorter sleep duration + wg.Add(1) + go func() { + proxyHealth.ReportFailure() + fmt.Printf("failure #%d\n", i) + wg.Done() + }() + } + wg.Wait() + // Check if failover occurred + if !proxyHealth.ReportFailure() { + t.Errorf("Health update not working properly") + } +} + +func TestProxyNoFailover(t *testing.T) { + failureCount := 12 // failover will happen at 10th failure + failAPIFreq := 1 * time.Second // frequency of failing API calls + + proxyHealth := NewProxyHealth() + proxyHealth.SetThreshold(10, 10, 1, 1*time.Minute) + // Report failures concurrently with a shorter sleep duration + var wg sync.WaitGroup + for i := 0; i < failureCount; i++ { + time.Sleep(failAPIFreq) // Shorter sleep duration + wg.Add(1) + go func() { + proxyHealth.ReportFailure() + fmt.Printf("failure #%d\n", i) + wg.Done() + }() + } + wg.Wait() + // Check if failover occurred + if proxyHealth.ReportFailure() { + t.Errorf("Health update not working properly") + } +} + func TestHealthReset(t *testing.T) { failureCount := 5 successCount := 2 + consFailureCount := 10 maxDuration := time.Second proxyHealth := NewProxyHealth() - proxyHealth.SetThreshold(failureCount, successCount, maxDuration) + proxyHealth.SetThreshold(failureCount, consFailureCount, successCount, maxDuration) shouldFailover := reportFailure(proxyHealth, 3, time.Nanosecond) if shouldFailover { t.Error("Health update not working properly") @@ -155,3 +205,29 @@ func TestRoundTrip(t *testing.T) { }) } } + +// TestProxyHealthConcurrency verifies that concurrent ReportSuccess and ReportFailure calls +// do not cause any data races or panics. +func TestProxyHealthConcurrency(t *testing.T) { + h := NewProxyHealth() + var wg sync.WaitGroup + + // Run 100 concurrent ReportSuccess and ReportFailure calls + for i := 0; i < 100; i++ { + wg.Add(2) + go func() { + defer wg.Done() + h.ReportSuccess() + }() + go func() { + defer wg.Done() + h.ReportFailure() + }() + } + + wg.Wait() + time.Sleep(50 * time.Millisecond) // allow any goroutines to finish + + // Optional: verify HasDeteriorated does not panic + _ = h.HasDeteriorated() +} diff --git a/csireverseproxy/pkg/common/envoy.go b/csireverseproxy/pkg/common/envoy.go index b493a359..056966f7 100644 --- a/csireverseproxy/pkg/common/envoy.go +++ b/csireverseproxy/pkg/common/envoy.go @@ -18,8 +18,6 @@ import ( "net/http" "sync/atomic" "time" - - log "github.com/sirupsen/logrus" ) // Envoy is an interface for failover/failback enabled proxy clients @@ -36,7 +34,7 @@ type Envoy interface { GetActiveHTTPClient() *http.Client RemoveBackupProxy() RemoveBackupHTTPClient() - ConfigureHealthParams(int, int, time.Duration) + ConfigureHealthParams(int, int, int, time.Duration) HasHealthDeteriorated() bool } @@ -173,8 +171,8 @@ func (e *envoy) RemoveBackupHTTPClient() { } // ConfigureHealthParams set the threshold params for enovy health -func (e *envoy) ConfigureHealthParams(failureCount, successCount int, failureDuration time.Duration) { - e.healthMonitor.SetThreshold(failureCount, successCount, failureDuration) +func (e *envoy) ConfigureHealthParams(failureCount, consecutiveFC, successCount int, failureDuration time.Duration) { + e.healthMonitor.SetThreshold(failureCount, consecutiveFC, successCount, failureDuration) } // HasHealthDeteriorated checks if evnoy has encountered any error while using primary proxy diff --git a/csireverseproxy/pkg/common/envoy_test.go b/csireverseproxy/pkg/common/envoy_test.go index f12b1232..55b9115c 100644 --- a/csireverseproxy/pkg/common/envoy_test.go +++ b/csireverseproxy/pkg/common/envoy_test.go @@ -148,7 +148,7 @@ func TestRemoveBackupHTTPClient(t *testing.T) { func TestConfigureHealthParams(t *testing.T) { e := &envoy{healthMonitor: NewProxyHealth()} - e.ConfigureHealthParams(3, 2, time.Second) + e.ConfigureHealthParams(3, 10, 2, time.Second) // Assuming SetThreshold properly sets parameters, no assertion needed } diff --git a/csireverseproxy/pkg/config/config.go b/csireverseproxy/pkg/config/config.go index 65238eeb..27ff1e69 100644 --- a/csireverseproxy/pkg/config/config.go +++ b/csireverseproxy/pkg/config/config.go @@ -27,13 +27,15 @@ import ( "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/k8sutils" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/utils" + "github.com/dell/csmlog" "github.com/mitchellh/mapstructure" - log "github.com/sirupsen/logrus" "github.com/spf13/viper" corev1 "k8s.io/api/core/v1" ) +var log = csmlog.GetLogger() + // ConfigManager is an interface used for testing, satisfied by viper.Viper. // //go:generate mockgen -source=config.go -destination=mocks/config-manager.go @@ -210,26 +212,26 @@ func (pc *ProxyConfig) DeepCopy() *ProxyConfig { // Log - logs the Proxy Config func (pc *ProxyConfig) Log() { - log.Println("---------------------") - log.Printf("port ::: %+s\n", pc.Port) - log.Println("---------------------") - log.Println("managedArrays") + log.Infof("---------------------") + log.Infof("port ::: %+s", pc.Port) + log.Infof("---------------------") + log.Infof("managedArrays") for key, val := range pc.managedArrays { - log.Printf("%s ::: %+v\n", key, val) + log.Infof("%s ::: %+v", key, val) } - log.Println("---------------------") - log.Println("---------------------") - log.Println("managementServers") + log.Infof("---------------------") + log.Infof("---------------------") + log.Infof("managementServers") for key, val := range pc.managementServers { - log.Printf("%v ::: %+v\n", key, val) + log.Infof("%v ::: %+v", key, val) } - log.Println("---------------------") - log.Println("---------------------") - log.Println("proxyCredentials") + log.Infof("---------------------") + log.Infof("---------------------") + log.Infof("proxyCredentials") for key, val := range pc.proxyCredentials { - log.Printf("%s ::: %+v\n", key, val) + log.Infof("%s ::: %+v", key, val) } - log.Println("---------------------") + log.Infof("---------------------") } func (pc *ProxyConfig) updateProxyCredentials(creds common.Credentials, storageArrayIdentifier string) { @@ -915,7 +917,7 @@ func ReadConfigFromSecret(vs *viper.Viper) (*ProxySecret, error) { secretFilePath := getEnv(common.EnvSecretFilePath, common.DefaultSecretPath) secretFileName := filepath.Base(secretFilePath) secretFileDir := filepath.Dir(secretFilePath) - log.Printf("Reading secret: %s from path: %s \n", secretFileName, secretFileDir) + log.Infof("Reading secret: %s from path: %s ", secretFileName, secretFileDir) vs.SetConfigName(secretFileName) vs.SetConfigType("yaml") vs.AddConfigPath(secretFileDir) @@ -933,7 +935,7 @@ func ReadConfigFromSecret(vs *viper.Viper) (*ProxySecret, error) { // ReadParamsConfigMapFromPath - read config map for params func ReadParamsConfigMapFromPath(configFilePath string, vcp ConfigManager) (*ParamsConfigMap, error) { - log.Printf("Reading params config map: %s from path: %s", filepath.Base(configFilePath), filepath.Dir(configFilePath)) + log.Infof("Reading params config map: %s from path: %s", filepath.Base(configFilePath), filepath.Dir(configFilePath)) vcp.SetConfigName(filepath.Base(configFilePath)) vcp.SetConfigType("yaml") diff --git a/csireverseproxy/pkg/config/config_test.go b/csireverseproxy/pkg/config/config_test.go index 196fb2b6..c741f0e4 100644 --- a/csireverseproxy/pkg/config/config_test.go +++ b/csireverseproxy/pkg/config/config_test.go @@ -27,7 +27,6 @@ import ( mock_config "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/config/mocks" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/k8smock" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/utils" - log "github.com/sirupsen/logrus" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" diff --git a/csireverseproxy/pkg/k8smock/k8smock_test.go b/csireverseproxy/pkg/k8smock/k8smock_test.go index 14049e6b..4b01b65f 100644 --- a/csireverseproxy/pkg/k8smock/k8smock_test.go +++ b/csireverseproxy/pkg/k8smock/k8smock_test.go @@ -25,11 +25,13 @@ import ( "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/k8sutils" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/utils" - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +var log = csmlog.GetLogger() + func TestMain(m *testing.M) { status := 0 if st := m.Run(); st > status { diff --git a/csireverseproxy/pkg/proxy/proxy.go b/csireverseproxy/pkg/proxy/proxy.go index c8d78e42..70eb2806 100644 --- a/csireverseproxy/pkg/proxy/proxy.go +++ b/csireverseproxy/pkg/proxy/proxy.go @@ -34,7 +34,7 @@ import ( "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/config" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/utils" - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" types "github.com/dell/gopowermax/v2/types/v100" "github.com/gorilla/mux" @@ -42,6 +42,8 @@ import ( const clientSymID = "proxyClientSymID" +var log = csmlog.GetLogger() + // Proxy - represents a Proxy type Proxy struct { config config.ProxyConfig @@ -112,7 +114,7 @@ func newTLSConfig(mgmtServer config.ManagementServer) *tls.Config { if !mgmtServer.SkipCertificateValidation { caCert, err := os.ReadFile(mgmtServer.CertFile) if err != nil { - log.Fatal(err) + log.Fatalf("%v", err) } caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) @@ -181,7 +183,7 @@ func (revProxy *Proxy) getAuthorisedArrays(res http.ResponseWriter, req *http.Re utils.WriteHTTPError(res, "No managed arrays under this user", utils.StatusUnAuthorized) return nil, fmt.Errorf("no managed arrays under this user") } - log.Printf("Authorized arrays - %s\n", symIDs) + log.Infof("Authorized arrays - %s", symIDs) return symIDs, nil } @@ -214,6 +216,48 @@ func (revProxy *Proxy) setIteratorID(resp *http.Response, URL url.URL, symID str return volumeIterator, nil } +func (revProxy *Proxy) setVolumeListID(resp *http.Response, URL url.URL, symID string) (*types.Volumev1, error) { + log.Debugf("Decoding Volumev1 response for Symmetrix ID: %s, URL: %v", symID, URL) + volumev1 := &types.Volumev1{} + decoder := json.NewDecoder(resp.Body) + if err := decoder.Decode(volumev1); err != nil { + log.Errorf("Failed to decode Volumev1 response: %v", err) + return nil, err + } + log.Debugf("Successfully decoded Volumev1 response for Symmetrix ID: %s, URL: %v", symID, URL) + + log.Debugf("Setting iterator cache for %d volumes", len(volumev1.Volumes)) + for _, vol := range volumev1.Volumes { + log.Debugf("Setting iterator cache for volume ID: %s, Symmetrix ID: %s, URL: %v", vol.ID, symID, URL) + revProxy.iteratorCache.Set(vol.ID, common.SymmURL{ + SymmetrixID: symID, + URL: URL, + }) + log.Debugf("Added Volume (%s) to iterator cache", vol.ID) + } + log.Debugf("Successfully set iterator cache for Symmetrix ID: %s, URL: %v", symID, URL) + return volumev1, nil +} + +func (revProxy *Proxy) setPortGroups(resp *http.Response, URL url.URL, symID string) (*types.PortGroupListResult, error) { + log.Debugf("Decoding PortGroupListResult: %s, URL: %v", symID, URL) + + portGroups := &types.PortGroupListResult{} + decoder := json.NewDecoder(resp.Body) + if err := decoder.Decode(portGroups); err != nil { + log.Errorf("Failed to decode port groups response: %v", err) + return nil, err + } + for _, pg := range portGroups.Results { + log.Debugf("Port Group ID: %s", pg.ID) + for _, port := range pg.Ports { + log.Debugf(" Port ID: %s, Type: %s, Director ID: %s", port.PortID, port.Type, port.Director.ID) + } + } + log.Debugf("Successfully decoded port groups: %s", symID) + return portGroups, nil +} + func (revProxy *Proxy) loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { reqID := revProxy.getRequestID() @@ -240,15 +284,18 @@ func (revProxy *Proxy) getResponseIfAuthorised(res http.ResponseWriter, req *htt if req.URL.RawQuery != "" { path = path + "?" + req.URL.RawQuery } + + requestID := req.Header.Get("RequestID") req, err = http.NewRequest(req.Method, path, req.Body) if err != nil { http.Error(res, err.Error(), 500) return nil, err } + + req.Header.Set("RequestID", requestID) revProxy.modifyHTTPRequest(res, req, proxy.URL) lockID := proxy.URL.String() - requestID := req.Header.Get("RequestID") - restCall := fmt.Sprintf("%s %s\n", req.Method, req.URL) + restCall := fmt.Sprintf("%s %s", req.Method, req.URL) lockType := utils.GetRequestType(req) defer utils.Elapsed(requestID, restCall)() lock := utils.Lock{ @@ -377,6 +424,9 @@ func (revProxy *Proxy) updateConfig(proxyConfig config.ProxyConfig) { func (revProxy *Proxy) GetRouter() http.Handler { router := mux.NewRouter() router.Use(revProxy.symIDMiddleware) + router.Path(utils.PrefixV1 + "/systems/{symid}/volumes").HandlerFunc(revProxy.GetVolumes) + router.Path(utils.PrefixV1 + "/systems/{symid}/port-groups").HandlerFunc(revProxy.GetPortGroups) + router.Path(utils.PrefixV1 + "/systems/{symid}/storage-groups").HandlerFunc(revProxy.ServeReverseProxy) router.Path(utils.Prefix + "/{version}/sloprovisioning/symmetrix/{symid}/volume").HandlerFunc(revProxy.ServeVolume) router.PathPrefix(utils.Prefix + "/{version}/sloprovisioning/symmetrix/{symid}").HandlerFunc(revProxy.ServeReverseProxy) router.PathPrefix(utils.Prefix + "/{version}/system/symmetrix/{symid}").HandlerFunc(revProxy.ServeReverseProxy) @@ -406,6 +456,26 @@ func (revProxy *Proxy) GetRouter() http.Handler { // migration router.PathPrefix(utils.Prefix + "/{version}/migration/symmetrix/{symid}").HandlerFunc(revProxy.ServeReverseProxy) + log.Info("started print path") + err := router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { + pathTemplate, err := route.GetPathTemplate() + if err != nil { + pathTemplate = "" + } + + methods, err := route.GetMethods() + if err != nil { + methods = []string{"ANY"} + } + + log.Infof("Registered Route: %s Methods: %v", pathTemplate, methods) + + return nil + }) + if err != nil { + log.Errorf("Error walking routes: %v", err) + } + return revProxy.loggingMiddleware(router) } @@ -618,7 +688,7 @@ func (revProxy *Proxy) ServeIterator(res http.ResponseWriter, req *http.Request) revProxy.modifyRequest(res, req, u4p.URL) lockID := u4p.URL.String() requestID := req.Header.Get("RequestID") - restCall := fmt.Sprintf("%s %s\n", req.Method, req.URL) + restCall := fmt.Sprintf("%s %s", req.Method, req.URL) lockType := utils.GetRequestType(req) defer utils.Elapsed(requestID, restCall)() lock := utils.Lock{ @@ -740,3 +810,93 @@ func (revProxy *Proxy) ServeVolume(res http.ResponseWriter, req *http.Request) { utils.WriteHTTPResponse(res, volumeIterator) } } + +func (revProxy *Proxy) GetVolumes(res http.ResponseWriter, req *http.Request) { + log.Debugf("Entering GetVolumes function") + symID, _ := revProxy.getSymID(req) + log.Debugf("Got symID: %s", symID) + err := revProxy.isAuthorized(res, req, symID) + if err != nil { + log.Errorf("Failed to authorize: %s", err.Error()) + return + } + log.Debugf("Authorized successfully") + resp, err := revProxy.getResponseIfAuthorised(res, req, symID) + if err != nil { + log.Errorf("Failed to get response: %s", err.Error()) + return + } + + log.Debugf("Got response from getResponseIfAuthorised") + defer resp.Body.Close() + err = utils.IsValidResponse(resp) + if err != nil { + log.Errorf("Invalid response: %s", err.Error()) + utils.WriteHTTPError(res, err.Error(), resp.StatusCode) + log.Errorf("Get Volume step fails for: (%s) symID with error (%s)", symID, err.Error()) + } else { + log.Debugf("Response is valid") + proxy, err := revProxy.getProxyBySymmID(symID) + if err != nil { + log.Errorf("Failed to get proxy: %s", err.Error()) + utils.WriteHTTPError(res, err.Error(), utils.StatusNotFound) + log.Errorf("Get Proxy for: (%s) symID with error (%s)", symID, err.Error()) + return + } + log.Debugf("Got proxy successfully") + volumeList, err := revProxy.setVolumeListID(resp, proxy.URL, symID) + if err != nil { + log.Errorf("Failed to set volume list ID: %s", err.Error()) + utils.WriteHTTPError(res, err.Error(), utils.StatusInternalError) + log.Errorf("Setting iterator failed for: (%s) symID with error (%s)", symID, err.Error()) + } + log.Debugf("Set volume list ID successfully") + utils.WriteHTTPResponse(res, volumeList) + } + log.Debugf("Exiting GetVolumes function") +} + +func (revProxy *Proxy) GetPortGroups(res http.ResponseWriter, req *http.Request) { + log.Debugf("Entering GetPortGroups function") + symID, _ := revProxy.getSymID(req) + log.Debugf("Got symID: %s", symID) + err := revProxy.isAuthorized(res, req, symID) + if err != nil { + log.Errorf("Failed to authorize: %s", err.Error()) + return + } + log.Debugf("Authorized successfully") + resp, err := revProxy.getResponseIfAuthorised(res, req, symID) + if err != nil { + log.Errorf("Failed to get response: %s", err.Error()) + return + } + + log.Debugf("Got response from getResponseIfAuthorised") + defer resp.Body.Close() + err = utils.IsValidResponse(resp) + if err != nil { + log.Errorf("Invalid response: %s", err.Error()) + utils.WriteHTTPError(res, err.Error(), resp.StatusCode) + log.Errorf("Get Volume step fails for: (%s) symID with error (%s)", symID, err.Error()) + } else { + log.Debugf("Response is valid") + proxy, err := revProxy.getProxyBySymmID(symID) + if err != nil { + log.Errorf("Failed to get proxy: %s", err.Error()) + utils.WriteHTTPError(res, err.Error(), utils.StatusNotFound) + log.Errorf("Get Proxy for: (%s) symID with error (%s)", symID, err.Error()) + return + } + log.Debugf("Got proxy successfully") + portgroups, err := revProxy.setPortGroups(resp, proxy.URL, symID) + if err != nil { + log.Errorf("Failed to set port groups: %s", err.Error()) + utils.WriteHTTPError(res, err.Error(), utils.StatusInternalError) + log.Errorf("Setting iterator failed for: (%s) symID with error (%s)", symID, err.Error()) + } + log.Debugf("Set port groups successfully") + utils.WriteHTTPResponse(res, portgroups) + } + log.Debugf("Exiting GetPortGroups function") +} diff --git a/csireverseproxy/pkg/proxy/proxy_test.go b/csireverseproxy/pkg/proxy/proxy_test.go index ae961b72..dcba310f 100644 --- a/csireverseproxy/pkg/proxy/proxy_test.go +++ b/csireverseproxy/pkg/proxy/proxy_test.go @@ -481,6 +481,196 @@ func TestGetRouter_ServeVolume(t *testing.T) { } } +func TestGetRouter_GetVolumes(t *testing.T) { + testCases := []struct { + name string + proxy func(*httptest.Server) *Proxy + req []func() *http.Request + server *httptest.Server + }{ + { + name: "Success: GetVolumes - with symid header", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/volumes", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + req.Header.Set("symid", arrayID) + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(`{"id": "00000000-1111-2abc-def3-44gh55ij66kl_0"}`)) + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + })), + }, + { + name: "Fail: GetVolumes - unauthorized", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/volumes", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(`{"id": "00000000-1111-2abc-def3-44gh55ij66kl_0"}`)) + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + })), + }, + { + name: "Fail: GetVolumes - Invalid SystemID", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "0000000000001" + url := fmt.Sprintf("%s/systems/%s/volumes", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // TODO: return error such that http.client.Do(req) recievs it + http.Error(w, "internal error", http.StatusInternalServerError) + })), + }, + { + name: "Fail: GetVolumes - Bad Response", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/volumes", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) + })), + }, + { + name: "Fail: GetVolumes - Empty Response", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/volumes", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(``)) + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + })), + }, + } + + utils.InitializeLock() + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + proxy := tc.proxy(tc.server) + if proxy == nil { + return + } + + // Setup router + router := proxy.GetRouter() + for _, reqFunc := range tc.req { + req := reqFunc() + router.ServeHTTP(httptest.NewRecorder(), req) + } + + t.Logf("Router: %v", router) + }) + } +} + func TestGetRouter_ServeReverseProxy(t *testing.T) { testCases := []struct { name string @@ -1932,3 +2122,193 @@ func mockProxyConfigMap(server *httptest.Server) *config.ProxyConfigMap { }, } } + +func TestGetRouter_GetPortGroups(t *testing.T) { + testCases := []struct { + name string + proxy func(*httptest.Server) *Proxy + req []func() *http.Request + server *httptest.Server + }{ + { + name: "Success: TestGetRouter_GetPortGroups - with symid header", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/port-groups", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + req.Header.Set("symid", arrayID) + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(`{"id": "00000000-1111-2abc-def3-44gh55ij66kl_0"}`)) + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + })), + }, + { + name: "Fail: GetPortGroups - unauthorized", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/port-groups", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(`{"id": "00000000-1111-2abc-def3-44gh55ij66kl_0"}`)) + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + })), + }, + { + name: "Fail: GetPortGroups - Invalid SystemID", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "0000000000001" + url := fmt.Sprintf("%s/systems/%s/port-groups", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // TODO: return error such that http.client.Do(req) recievs it + http.Error(w, "internal error", http.StatusInternalServerError) + })), + }, + { + name: "Fail: GetPortGroups - Bad Response", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/port-groups", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) + })), + }, + { + name: "Fail: GetPortGroups - Empty Response", + proxy: func(server *httptest.Server) *Proxy { + // Create a new Proxy + proxy, err := createValidProxyConfig(t, server) + if err != nil { + t.Errorf("Failed to create proxy: %v", err) + return nil + } + + return proxy + }, + req: []func() *http.Request{ + func() *http.Request { + arrayID := "000000000001" + url := fmt.Sprintf("%s/systems/%s/port-groups", utils.PrefixV1, arrayID) + req, _ := http.NewRequest("GET", url, nil) + + vars := map[string]string{ + "symid": arrayID, + } + req = mux.SetURLVars(req, vars) + req.SetBasicAuth("test-username", "test-password") + return req + }, + }, + server: fakeServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(``)) + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + })), + }, + } + + utils.InitializeLock() + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + proxy := tc.proxy(tc.server) + if proxy == nil { + return + } + + // Setup router + router := proxy.GetRouter() + for _, reqFunc := range tc.req { + req := reqFunc() + router.ServeHTTP(httptest.NewRecorder(), req) + } + + t.Logf("Router: %v", router) + }) + } +} diff --git a/csireverseproxy/pkg/utils/locks.go b/csireverseproxy/pkg/utils/locks.go index 782583bc..19b598af 100644 --- a/csireverseproxy/pkg/utils/locks.go +++ b/csireverseproxy/pkg/utils/locks.go @@ -19,8 +19,6 @@ import ( "sync" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/common" - - log "github.com/sirupsen/logrus" ) const ( diff --git a/csireverseproxy/pkg/utils/utils.go b/csireverseproxy/pkg/utils/utils.go index f49bebcf..2a254965 100644 --- a/csireverseproxy/pkg/utils/utils.go +++ b/csireverseproxy/pkg/utils/utils.go @@ -27,8 +27,7 @@ import ( "time" "github.com/dell/csi-powermax/csireverseproxy/v2/pkg/common" - - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" ) // Constants for util package @@ -40,10 +39,13 @@ const ( StatusProxyBusy = 504 StatusNotFound = 404 Prefix = "/univmax/restapi" + PrefixV1 = "/univmax/rest/v1" PrivatePrefix = "/univmax/restapi/private" InternalPrefix = Prefix + "/internal" ) +var log = csmlog.GetLogger() + // WriteHTTPError - given a statuscode and error message, writes a HTTP error using the // ResponseWriter func WriteHTTPError(w http.ResponseWriter, errorMsg string, statusCode int) { diff --git a/docker.mk b/docker.mk deleted file mode 100644 index d5a86cd7..00000000 --- a/docker.mk +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# docker makefile, included from Makefile, will build/push images with docker or podman -# - -# Includes the following generated file to get semantic version information -include semver.mk - -ifdef NOTES - RELNOTE="-$(NOTES)" -else - RELNOTE= -endif - -ifeq ($(IMAGETAG),) -IMAGETAG="v$(MAJOR).$(MINOR).$(PATCH)$(RELNOTE)" -endif - - -docker: download-csm-common - $(eval include csm-common.mk) - @echo "Building: $(REGISTRY)/$(IMAGENAME):$(IMAGETAG)" - $(BUILDER) build --pull $(NOCACHE) -t "$(REGISTRY)/$(IMAGENAME):$(IMAGETAG)" --target $(BUILDSTAGE) --build-arg GOPROXY --build-arg BASEIMAGE=$(CSM_BASEIMAGE) --build-arg GOIMAGE=$(DEFAULT_GOIMAGE) . - -docker-no-cache: - @echo "Building with --no-cache ..." - @make docker NOCACHE=--no-cache - -push: - @echo "Pushing: $(REGISTRY)/$(IMAGENAME):$(IMAGETAG)" - $(BUILDER) push "$(REGISTRY)/$(IMAGENAME):$(IMAGETAG)" - -download-csm-common: - curl -O -L https://raw.githubusercontent.com/dell/csm/main/config/csm-common.mk \ No newline at end of file diff --git a/go.mod b/go.mod index ef878dc3..1fe14ea9 100644 --- a/go.mod +++ b/go.mod @@ -1,38 +1,38 @@ module github.com/dell/csi-powermax/v2 -go 1.25 +go 1.25.0 require ( + github.com/dell/csmlog v1.0.0 + github.com/dell/dell-csi-extensions/common v1.10.0 + github.com/dell/dell-csi-extensions/migration v1.10.0 + github.com/dell/dell-csi-extensions/podmon v1.10.0 + github.com/dell/dell-csi-extensions/replication v1.13.0 + github.com/dell/gobrick v1.16.0 + github.com/dell/gocsi v1.16.0 + github.com/dell/gofsutil v1.21.0 + github.com/dell/goiscsi v1.14.0 + github.com/dell/gonvme v1.13.0 + github.com/dell/gopowermax/v2 v2.12.0 github.com/akutz/goof v0.1.2 github.com/container-storage-interface/spec v1.6.0 github.com/coreos/go-systemd/v22 v22.6.0 - github.com/cucumber/godog v0.15.0 + github.com/cucumber/godog v0.15.1 github.com/cucumber/messages-go/v10 v10.0.3 - github.com/dell/dell-csi-extensions/common v1.9.0 - github.com/dell/dell-csi-extensions/migration v1.9.0 - github.com/dell/dell-csi-extensions/podmon v1.9.0 - github.com/dell/dell-csi-extensions/replication v1.12.0 - github.com/dell/gobrick v1.15.0 - github.com/dell/gocsi v1.15.0 - github.com/dell/gofsutil v1.20.0 - github.com/dell/goiscsi v1.13.0 - github.com/dell/gonvme v1.12.0 - github.com/dell/gopowermax/v2 v2.11.0 github.com/fsnotify/fsnotify v1.9.0 github.com/golang/mock v1.6.0 github.com/gorilla/mux v1.8.1 github.com/kubernetes-csi/csi-lib-utils v0.11.0 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/viper v1.20.1 - github.com/stretchr/testify v1.11.0 + github.com/spf13/viper v1.21.0 + github.com/stretchr/testify v1.11.1 github.com/vmware/govmomi v0.52.0 - go.uber.org/mock v0.6.0 - golang.org/x/net v0.43.0 - google.golang.org/grpc v1.75.0 - google.golang.org/protobuf v1.36.6 - k8s.io/api v0.34.0 - k8s.io/apimachinery v0.34.0 - k8s.io/client-go v0.34.0 + golang.org/x/net v0.48.0 + google.golang.org/grpc v1.78.0 + google.golang.org/protobuf v1.36.10 + k8s.io/api v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/client-go v0.34.2 ) require ( @@ -41,57 +41,64 @@ require ( github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect github.com/cucumber/messages/go/v21 v21.0.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/jsonpointer v0.22.3 // indirect + github.com/go-openapi/jsonreference v0.21.3 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/godbus/dbus/v5 v5.2.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-memdb v1.3.4 // indirect + github.com/hashicorp/go-memdb v1.3.5 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/jinzhu/copier v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/sagikazarmark/locafero v0.7.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/pflag v1.0.9 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/etcd/api/v3 v3.6.1 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.1 // indirect - go.etcd.io/etcd/client/v3 v3.6.1 // indirect + go.etcd.io/etcd/api/v3 v3.6.6 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.6 // indirect + go.etcd.io/etcd/client/v3 v3.6.6 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect @@ -99,6 +106,6 @@ require ( k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 322a7ee8..7efb9110 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,28 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/dell/csmlog v1.0.0 h1:EzW+nMJBD0QTNP88OoaAJUOMVilS9cWkO248BTcJt/4= +github.com/dell/csmlog v1.0.0/go.mod h1:7rBzSv9xF5t233+J+9vkStjFsmyYO3L/B9tDTy3+9ZU= +github.com/dell/dell-csi-extensions/common v1.10.0 h1:WIFPWVEBUyzOTCOPAlcgQsiRRGAufyKJYbIATbNZXIY= +github.com/dell/dell-csi-extensions/common v1.10.0/go.mod h1:zRHzmPX5SQQnqQ1LEIxG4hYqLBeQOSiD8TkEhU0eWTY= +github.com/dell/dell-csi-extensions/migration v1.10.0 h1:TwMCI91zZNuDUqr3TcL1BCWL1cNkQMwkupyRbVbYJAo= +github.com/dell/dell-csi-extensions/migration v1.10.0/go.mod h1:BN7Mlxt3CrpqrxxliH3BadAtmAyTzdBKDid7IDyfoi4= +github.com/dell/dell-csi-extensions/podmon v1.10.0 h1:YeM9OmgJHE+n6aNaeEC96EuVev5x3pddggcM7Ws7pkk= +github.com/dell/dell-csi-extensions/podmon v1.10.0/go.mod h1:+g7fdyw1Zx74NBJQgi1BCtsywqk37MJd9JN86IPJJu0= +github.com/dell/dell-csi-extensions/replication v1.13.0 h1:DSpoZ3vX65a3KDxUv0OinLkY2qUAQtRX3E1c1e3fnvA= +github.com/dell/dell-csi-extensions/replication v1.13.0/go.mod h1:aJBwd55amqbY3kk8SG7NjwH7nxBscceDwc1rKesUG1g= +github.com/dell/gobrick v1.16.0 h1:z/a9qXnT3hx3D4I+SJUMnIgJtcCx0j3gzmPPDUWtoYs= +github.com/dell/gobrick v1.16.0/go.mod h1:9uoH8EsNi9yAsUZj2gZFgB5kqdlyvArqx0tYC7Qg9IM= +github.com/dell/gocsi v1.16.0 h1:avhQPD11rYzT6/dPxpZfFsJV+T/T0x1GJqqbco45W8c= +github.com/dell/gocsi v1.16.0/go.mod h1:Fz5dQv/kWf5Y1EXZEzxLBQSsnW2HE/WY95R0WCDQLO4= +github.com/dell/gofsutil v1.21.0 h1:SeusAYjiO/1ogvg/TapvCyHcrM9z+OvdaMU5i9Ijn3M= +github.com/dell/gofsutil v1.21.0/go.mod h1:qBGEz1wMOtqTODuJfiBZhUHT0JjexBblu2oa+sEclNs= +github.com/dell/goiscsi v1.14.0 h1:kNDqOlpJ3cLSJh7Hfyn/Kz/FMCKHzV0s/xx4EqnelFw= +github.com/dell/goiscsi v1.14.0/go.mod h1:SCSC8dJCqTosU7SspaoLv6ICTKNEz08rt/I8nZ3+ptc= +github.com/dell/gonvme v1.13.0 h1:j8A1BzYA48gelih3xWd/J6LQ71CbC8Lbdyv0jG8uUNU= +github.com/dell/gonvme v1.13.0/go.mod h1:L5K7V4JZTf12m3k2wdwKwP+/eA6pr8DvlCsJU1QTGOQ= +github.com/dell/gopowermax/v2 v2.12.0 h1:6ARWgc+KGY3ns2misQt2yXTyiP+y1E6X/jZWABosAEw= +github.com/dell/gopowermax/v2 v2.12.0/go.mod h1:f/wnjHWZ6H4nJE/w2VTu547yBvZxMQjlz3oc2twb2Fk= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -91,8 +113,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= -github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo= -github.com/cucumber/godog v0.15.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/godog v0.15.1 h1:rb/6oHDdvVZKS66hrhpjFQFHjthFSrQBCOI1LwshNTI= +github.com/cucumber/godog v0.15.1/go.mod h1:qju+SQDewOljHuq9NSM66s0xEhogx0q30flfxL4WUk8= github.com/cucumber/messages-go/v10 v10.0.3 h1:m/9SD/K/A15WP7i1aemIv7cwvUw+viS51Ui5HBw1cdE= github.com/cucumber/messages-go/v10 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY= github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= @@ -102,26 +124,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dell/dell-csi-extensions/common v1.9.0 h1:H1NXBYlJZ+XTCe4tSXo94Lvg8HD2wgt6ywql3kVrG34= -github.com/dell/dell-csi-extensions/common v1.9.0/go.mod h1:DA9lX2BX3fdshR40IaXfokDrIKo9a32QShcTlAqhf+c= -github.com/dell/dell-csi-extensions/migration v1.9.0 h1:naNAdZDWG485EAZ3hAk0zmsHURGxkk0dlUwZyT4Ny3s= -github.com/dell/dell-csi-extensions/migration v1.9.0/go.mod h1:yLs7K7/xVtsYQKSxeIS897TkrJrXJq/ClTJc+sdGYrY= -github.com/dell/dell-csi-extensions/podmon v1.9.0 h1:AYE3n6o6jB3Sh0uce65JPmir3FPxvqSW/21/bGqRhvY= -github.com/dell/dell-csi-extensions/podmon v1.9.0/go.mod h1:jz846RAruY/m25uBbZVYcr8vp7wmKakbjOuUBwpY0Ls= -github.com/dell/dell-csi-extensions/replication v1.12.0 h1:jOdaZsoGHWX9SsqgH+2v9cIJSAVLF1SnKKHtdiF5Ywc= -github.com/dell/dell-csi-extensions/replication v1.12.0/go.mod h1:nyPBfbMOpboVI/cYLOFJhv0LADGSvHwDcF4AxZau/go= -github.com/dell/gobrick v1.15.0 h1:0BAjAjDxnpX2b3aIuemhhWsnaOS09P1JeFA8vibDlmI= -github.com/dell/gobrick v1.15.0/go.mod h1:5GhPEB6AE5dWmKZLSMl5QT8BZBSkcolI7GBej1wo8XY= -github.com/dell/gocsi v1.15.0 h1:SXBtiNTb3iTHms4WRoewwdJaItOY8XaaxBjkTYy8o5Q= -github.com/dell/gocsi v1.15.0/go.mod h1:u8+NcCB2rWr79Dx63GWUo3TsAJj/RSlRoimTrp6BZiM= -github.com/dell/gofsutil v1.20.0 h1:jkQrOb4sSxEUcPTAbyLBABMBf+7vBC6g+yzxTGb0Ozw= -github.com/dell/gofsutil v1.20.0/go.mod h1:kKFZSYY0tF5lx/U6UhSAqLxKnNESd0hT4gJ4PlYXSB8= -github.com/dell/goiscsi v1.13.0 h1:4+uB+uJQmJ91yN7wy38sLsr5S/lqL3/tVboLOh0sg38= -github.com/dell/goiscsi v1.13.0/go.mod h1:1IPCAavfm6T9BzKS0QYfBlJz7X+AfYPYjH4G84TvJP4= -github.com/dell/gonvme v1.12.0 h1:KLOr+v+1kn/sz26CFTAkFrR1Ti4aZ37i1Mlxp1hBXYs= -github.com/dell/gonvme v1.12.0/go.mod h1:ETLwyr+OG3DYfzdlMKCv5PjeDfj+JIxV2xrbHBTg2lk= -github.com/dell/gopowermax/v2 v2.11.0 h1:wxuTxZVgPYYIotwvuvQYHb2MKXAZWcSiRQnG3YuXlbk= -github.com/dell/gopowermax/v2 v2.11.0/go.mod h1:vnH9aCT+/TxPROPvP4mkbcAOnW8rSJC6r4nWjuyiR0Q= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= @@ -129,8 +131,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -167,21 +169,49 @@ github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8= +github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8= +github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -229,8 +259,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -275,8 +305,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -285,8 +315,9 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= +github.com/hashicorp/go-memdb v1.3.5 h1:b3taDMxCBCBVgyRrS1AZVHO14ubMYZB++QpNhBg+Nyo= +github.com/hashicorp/go-memdb v1.3.5/go.mod h1:8IVKKBkVe+fxFgdFOYxzQQNjz+sWCyHCdIC/+5+Vy1Y= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -317,8 +348,6 @@ github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -353,8 +382,6 @@ github.com/kubernetes-csi/csi-lib-utils v0.11.0/go.mod h1:BmGZZB16L18+9+Lgg9YWwB github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -400,16 +427,15 @@ github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= -github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= +github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q= +github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -446,13 +472,13 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -465,34 +491,33 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -502,8 +527,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= @@ -522,34 +547,34 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= -go.etcd.io/etcd/api/v3 v3.6.1 h1:yJ9WlDih9HT457QPuHt/TH/XtsdN2tubyxyQHSHPsEo= -go.etcd.io/etcd/api/v3 v3.6.1/go.mod h1:lnfuqoGsXMlZdTJlact3IB56o3bWp1DIlXPIGKRArto= -go.etcd.io/etcd/client/pkg/v3 v3.6.1 h1:CxDVv8ggphmamrXM4Of8aCC8QHzDM4tGcVr9p2BSoGk= -go.etcd.io/etcd/client/pkg/v3 v3.6.1/go.mod h1:aTkCp+6ixcVTZmrJGa7/Mc5nMNs59PEgBbq+HCmWyMc= -go.etcd.io/etcd/client/v3 v3.6.1 h1:KelkcizJGsskUXlsxjVrSmINvMMga0VWwFF0tSPGEP0= -go.etcd.io/etcd/client/v3 v3.6.1/go.mod h1:fCbPUdjWNLfx1A6ATo9syUmFVxqHH9bCnPLBZmnLmMY= -go.etcd.io/etcd/pkg/v3 v3.6.1 h1:Qpshk3/SLra217k7FxcFGaH2niFAxFf1Dug57f0IUiw= -go.etcd.io/etcd/pkg/v3 v3.6.1/go.mod h1:nS0ahQoZZ9qXjQAtYGDt80IEHKl9YOF7mv6J0lQmBoQ= -go.etcd.io/etcd/server/v3 v3.6.1 h1:Y/mh94EeImzXyTBIMVgR0v5H+ANtRFDY4g1s5sxOZGE= -go.etcd.io/etcd/server/v3 v3.6.1/go.mod h1:nCqJGTP9c2WlZluJB59j3bqxZEI/GYBfQxno0MguVjE= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.etcd.io/etcd/api/v3 v3.6.6 h1:mcaMp3+7JawWv69p6QShYWS8cIWUOl32bFLb6qf8pOQ= +go.etcd.io/etcd/api/v3 v3.6.6/go.mod h1:f/om26iXl2wSkcTA1zGQv8reJRSLVdoEBsi4JdfMrx4= +go.etcd.io/etcd/client/pkg/v3 v3.6.6 h1:uoqgzSOv2H9KlIF5O1Lsd8sW+eMLuV6wzE3q5GJGQNs= +go.etcd.io/etcd/client/pkg/v3 v3.6.6/go.mod h1:YngfUVmvsvOJ2rRgStIyHsKtOt9SZI2aBJrZiWJhCbI= +go.etcd.io/etcd/client/v3 v3.6.6 h1:G5z1wMf5B9SNexoxOHUGBaULurOZPIgGPsW6CN492ec= +go.etcd.io/etcd/client/v3 v3.6.6/go.mod h1:36Qv6baQ07znPR3+n7t+Rk5VHEzVYPvFfGmfF4wBHV8= +go.etcd.io/etcd/pkg/v3 v3.6.6 h1:wylOivS/UxXTZ0Le5fOdxCjatW5ql9dcWEggQQHSorw= +go.etcd.io/etcd/pkg/v3 v3.6.6/go.mod h1:9TKZL7WUEVHXYM3srP3ESZfIms34s1G72eNtWA9YKg4= +go.etcd.io/etcd/server/v3 v3.6.6 h1:YSRWGJPzU+lIREwUQI4MfyLZrkUyzjJOVpMxJvZePaY= +go.etcd.io/etcd/server/v3 v3.6.6/go.mod h1:A1OQ1x3PaiENDLywMjCiMwV1pwJSpb0h9Z5ORP2dv6I= go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= @@ -557,19 +582,19 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqx go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= @@ -577,18 +602,16 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -600,8 +623,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -665,15 +688,15 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -683,8 +706,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -729,13 +752,13 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -743,14 +766,14 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -790,8 +813,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -835,10 +858,10 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -851,8 +874,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -865,8 +888,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -875,8 +898,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -909,14 +932,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.22.0/go.mod h1:0AoXXqst47OI/L0oGKq9DG61dvGRPXs7X4/B7KyjBCU= -k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE= -k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/apimachinery v0.22.0/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= -k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/client-go v0.22.0/go.mod h1:GUjIuXR5PiEv/RVK5OODUsm6eZk7wtSWZSaSJbpFdGg= -k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo= -k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/component-base v0.22.0/go.mod h1:SXj6Z+V6P6GsBhHZVbWCw9hFjUdUYnJerlhhPnYCBCg= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= @@ -938,8 +961,8 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/helper.mk b/helper.mk new file mode 100644 index 00000000..bac46381 --- /dev/null +++ b/helper.mk @@ -0,0 +1,18 @@ +# Copyright © 2026 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. + +generate: + go generate + go run core/semver/semver.go -f mk > semver.mk + +download-csm-common: + git clone --depth 1 git@github.com:CSM/csm.git temp-repo + cp temp-repo/config/csm-common.mk . + rm -rf temp-repo + +vendor: + GOPRIVATE=github.com go mod vendor + (cd csireverseproxy; make vendor) diff --git a/images.mk b/images.mk new file mode 100644 index 00000000..a9d1d1cf --- /dev/null +++ b/images.mk @@ -0,0 +1,32 @@ +# Copyright © 2026 Dell Inc. or its subsidiaries. All Rights Reserved. +# +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. + +include overrides.mk +include helper.mk + +images: download-csm-common vendor generate + $(eval include csm-common.mk) + @echo "Base Images is set to: $(BASEIMAGE)" + @echo "Building: $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" + $(BUILDER) build --pull $(NOCACHE) -t "$(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" --build-arg BASEIMAGE=$(CSM_BASEIMAGE) --build-arg GOIMAGE=$(DEFAULT_GOIMAGE) . + + # build the reverseproxy container as part of this target +ifndef ($(NOCACHE)) + make -C csireverseproxy images +else + make -C csireverseproxy images-no-cache +endif + +images-no-cache: + @echo "Building with --no-cache ..." + @make images NOCACHE=--no-cache + +push: + @echo "Pushing: $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" + $(BUILDER) push "$(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)" + + # push the reverseproxy container + make -C csireverseproxy push diff --git a/k8sutils/k8sutils.go b/k8sutils/k8sutils.go index 78cb751b..b6d298c7 100644 --- a/k8sutils/k8sutils.go +++ b/k8sutils/k8sutils.go @@ -24,13 +24,15 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kubernetes-csi/csi-lib-utils/leaderelection" - log "github.com/sirupsen/logrus" + "github.com/dell/csmlog" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) +var log = csmlog.GetLogger() + // UtilsInterface - interface which provides helper methods related to k8s type UtilsInterface interface { GetNodeLabels(string) (map[string]string, error) diff --git a/k8sutils/k8sutils_test.go b/k8sutils/k8sutils_test.go index f79858ed..ec725f36 100644 --- a/k8sutils/k8sutils_test.go +++ b/k8sutils/k8sutils_test.go @@ -63,7 +63,7 @@ func Test_CreateKubeClientSet(t *testing.T) { }, { name: "not in a cluster", - kubeConfig: "", + kubeConfig: "/tmp/kubeconfig1", before: func(_ string) error { return nil }, after: func() {}, wantErr: true, @@ -323,7 +323,7 @@ func Test_Init(t *testing.T) { args: args{kubeConfig: ""}, before: func(_ test) error { return nil }, after: func() {}, - wantErr: true, + wantErr: false, }, { name: "returns existing k8s clientset", @@ -361,12 +361,15 @@ func Test_Init(t *testing.T) { t.Errorf("Init() error = %v, wantErr %v", err, tt.wantErr) return } + if !tt.wantErr { assert.NotNil(t, got) // if a wanted return value is specified, check it against what we got if tt.want != nil { assert.Equal(t, tt.want, got) } + } else { + assert.Error(t, err) } }) } diff --git a/main.go b/main.go index 5e283317..05bf57b4 100644 --- a/main.go +++ b/main.go @@ -1,5 +1,5 @@ /* - Copyright © 2021-2025 Dell Inc. or its subsidiaries. All Rights Reserved. + Copyright © 2021-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -35,11 +35,18 @@ var flags struct { kubeconfig *string } +var ManifestSemver string + // main is ignored when this package is built as a go plug-in func main() { setEnvsFunc() initFlagsFunc() + if ManifestSemver != "" { + service.ManifestSemver = ManifestSemver + service.Manifest["semver"] = ManifestSemver + } + err := driverRun() if err != nil { os.Exit(1) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..97f6a05f --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,7 @@ +site_name: 'csi-powermax' +site_description: 'csi-powermax Documentation.' +docs_dir: docs +plugins: + - techdocs-core +theme: + name: material diff --git a/overrides.mk b/overrides.mk index d554be2c..fff48607 100644 --- a/overrides.mk +++ b/overrides.mk @@ -1,51 +1,12 @@ -# Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. +# Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Dell Technologies, Dell and other trademarks are trademarks of Dell Inc. +# or its subsidiaries. Other trademarks may be trademarks of their respective +# owners. -# overrides file -# this file, included from the Makefile, will overlay default values with environment variables -# - -# DEFAULT values -# ubi9/ubi-micro:9.2-13 -DEFAULT_GOIMAGE=$(shell sed -En 's/^go (.*)$$/\1/p' go.mod) -DEFAULT_REGISTRY="sample_registry" -DEFAULT_IMAGENAME="csi-powermax" -DEFAULT_BUILDSTAGE="final" -DEFAULT_IMAGETAG="" - -# set the GOIMAGE if needed -ifeq ($(GOIMAGE),) -export GOIMAGE="$(DEFAULT_GOIMAGE)" -endif - -# set the REGISTRY if needed -ifeq ($(REGISTRY),) -export REGISTRY="$(DEFAULT_REGISTRY)" -endif - -# set the IMAGENAME if needed -ifeq ($(IMAGENAME),) -export IMAGENAME="$(DEFAULT_IMAGENAME)" -endif - -#set the IMAGETAG if needed -ifneq ($(DEFAULT_IMAGETAG), "") -export IMAGETAG="$(DEFAULT_IMAGETAG)" -endif - -# set the BUILDSTAGE if needed -ifeq ($(BUILDSTAGE),) -export BUILDSTAGE="$(DEFAULT_BUILDSTAGE)" -endif +IMAGE_REGISTRY?="sample_registry" +IMAGE_NAME="csi-powermax" +IMAGE_TAG?=$(shell date +%Y%m%d%H%M%S) # figure out if podman or docker should be used (use podman if found) ifneq (, $(shell which podman 2>/dev/null)) @@ -59,17 +20,10 @@ overrides-help: @echo @echo "The following environment variables can be set to control the build" @echo - @echo "GOIMAGE - The version of Go to build with, default is: $(DEFAULT_GOIMAGE)" - @echo " Current setting is: $(GOIMAGE)" - @echo "REGISTRY - The registry to push images to, default is: $(DEFAULT_REGISTRY)" - @echo " Current setting is: $(REGISTRY)" - @echo "IMAGENAME - The image name to be built, defaut is: $(DEFAULT_IMAGENAME)" - @echo " Current setting is: $(IMAGENAME)" - @echo "IMAGETAG - The image tag to be built, default is an empty string which will determine the tag by examining annotated tags in the repo." - @echo " Current setting is: $(IMAGETAG)" - @echo "BUILDSTAGE - The Dockerfile build stage to execute, default is: $(DEFAULT_BUILDSTAGE)" - @echo " Stages can be found by looking at the Dockerfile" - @echo " Current setting is: $(BUILDSTAGE)" + @echo "IMAGE_REGISTRY - The registry to push images to." + @echo " Current setting is: $(IMAGE_REGISTRY)" + @echo "IMAGE_NAME - The image name to be built." + @echo " Current setting is: $(IMAGE_NAME)" + @echo "IMAGE_TAG - The image tag to be built, default is the current date." + @echo " Current setting is: $(IMAGE_TAG)" @echo - - diff --git a/pkg/file/file.go b/pkg/file/file.go index 7f7cf052..296e2628 100644 --- a/pkg/file/file.go +++ b/pkg/file/file.go @@ -22,11 +22,12 @@ import ( "strings" "time" - "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/dell/csmlog" + "github.com/dell/gofsutil" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" + "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -53,8 +54,11 @@ const ( NFSExportIDParam = "NFSExportID" ) +var log = csmlog.GetLogger() + // CreateFileSystem creates a file system func CreateFileSystem(ctx context.Context, reqID string, accessibility *csi.TopologyRequirement, params map[string]string, symID, storagePoolID, serviceLevel, nasServerName, fileSystemIdentifier, allowRoot string, sizeInMiB int64, pmaxClient pmax.Pmax) (*csi.CreateVolumeResponse, error) { + log := log.WithContext(ctx) // Get NAS Server ID from NASServer Name nasServerList, err := pmaxClient.GetNASServerList(ctx, symID, types.QueryParams{QueryName: nasServerName}) if err != nil { @@ -156,6 +160,7 @@ func DeleteFileSystem(ctx context.Context, symID, fileSystemID string, pmaxClien // CreateNFSExport creates a NFS export for the given file system func CreateNFSExport(ctx context.Context, reqID, symID, fsID string, am *csi.VolumeCapability_AccessMode, volumeContext map[string]string, pmaxClient pmax.Pmax) (*csi.ControllerPublishVolumeResponse, error) { + log := log.WithContext(ctx) nasServerID := volumeContext[NASServerIDParam] nasServerName := volumeContext[NASServerNameParam] allowRoot := volumeContext[AllowRootParam] @@ -241,6 +246,7 @@ func checkIfNFSExportExist(ctx context.Context, symID, fsID string, nfsName stri // DeleteNFSExport deletes a NFS Export for the given file system func DeleteNFSExport(ctx context.Context, reqID, symID, fsID string, pmaxClient pmax.Pmax) (*csi.ControllerUnpublishVolumeResponse, error) { + log := log.WithContext(ctx) // get the fileSystem fs, err := pmaxClient.GetFileSystemByID(ctx, symID, fsID) if err != nil { @@ -281,6 +287,7 @@ func DeleteNFSExport(ctx context.Context, reqID, symID, fsID string, pmaxClient func StageFileSystem(ctx context.Context, reqID, symID, fsID string, privTgt string, publishContext map[string]string, _ pmax.Pmax) ( *csi.NodeStageVolumeResponse, error, ) { + log := log.WithContext(ctx) nasServerName := publishContext[NASServerNameParam] nasServerID := publishContext[NASServerIDParam] nfsExportPath := publishContext[NFSExportPathParam] @@ -326,6 +333,7 @@ func StageFileSystem(ctx context.Context, reqID, symID, fsID string, privTgt str // PublishFileSystem bind the file system mount on the node func PublishFileSystem(ctx context.Context, req *csi.NodePublishVolumeRequest, reqID, symID, fsID string, _ pmax.Pmax) (*csi.NodePublishVolumeResponse, error) { + log := log.WithContext(ctx) // get params for publish publishContext := req.GetPublishContext() targetPath := req.GetTargetPath() @@ -382,6 +390,7 @@ func PublishFileSystem(ctx context.Context, req *csi.NodePublishVolumeRequest, r // ExpandFileSystem expands the given file system on the array func ExpandFileSystem(ctx context.Context, reqID, symID, fsID string, requestedSize int64, pmaxClient pmax.Pmax) (*csi.ControllerExpandVolumeResponse, error) { + log := log.WithContext(ctx) // log all parameters used in ExpandVolume call fields := map[string]interface{}{ "RequestID": reqID, @@ -432,7 +441,7 @@ func isReadyToPublishNFS(stagingPath string) (bool, error) { return found, err } if !found { - log.Warning("staged device not found") + log.Warn("staged device not found") return found, nil } return found, nil diff --git a/pkg/file/file_test.go b/pkg/file/file_test.go index 965ab8a1..47fc1ef7 100644 --- a/pkg/file/file_test.go +++ b/pkg/file/file_test.go @@ -22,11 +22,11 @@ import ( "testing" "time" - "github.com/container-storage-interface/spec/lib/go/csi" "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" "github.com/dell/gofsutil" types "github.com/dell/gopowermax/v2/types/v100" - "go.uber.org/mock/gomock" + "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/golang/mock/gomock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/pkg/migration/migration.go b/pkg/migration/migration.go index a45da8d5..ecb8a8fe 100644 --- a/pkg/migration/migration.go +++ b/pkg/migration/migration.go @@ -23,9 +23,10 @@ import ( "fmt" "strings" + "github.com/dell/csmlog" + pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -42,6 +43,8 @@ var localSGVolumeList map[string]*types.VolumeIterator // CacheReset is flag for cache var CacheReset bool +var log = csmlog.GetLogger() + const ( // CsiNoSrpSGPrefix to be used as filter CsiNoSrpSGPrefix = "csi-no-srp-sg-" @@ -75,6 +78,7 @@ func ListContains(a []string, x string) bool { } func getOrCreateSGMigration(ctx context.Context, symID, remoteSymID, storageGroupID string, pmaxClient pmax.Pmax) (*types.MigrationSession, error) { + log := log.WithContext(ctx) migrationSG, err := pmaxClient.GetStorageGroupMigrationByID(ctx, symID, storageGroupID) if err != nil { if strings.Contains(err.Error(), "is not in a migration") || migrationSG == nil { @@ -97,6 +101,7 @@ func getOrCreateSGMigration(ctx context.Context, symID, remoteSymID, storageGrou // 2. Create migration session for no-srp SG // 3. Create default SRP storage group on remote array, and maintain a list of volumes to be added. var StorageGroupMigration = func(ctx context.Context, symID, remoteSymID, clusterPrefix string, pmaxClient pmax.Pmax) (bool, error) { + log := log.WithContext(ctx) // Before running no-srp-sg migrate call, remove the volumes from srp SG // for all the SG for this cluster on local sym ID localSgList, err := pmaxClient.GetStorageGroupIDList(ctx, symID, CsiVolumePrefix+clusterPrefix, true) @@ -197,6 +202,7 @@ var StorageGroupMigration = func(ctx context.Context, symID, remoteSymID, cluste // StorageGroupCommit does a "commit" on all the migration session SG // Returns true if not sessions found, all migration completed var StorageGroupCommit = func(ctx context.Context, symID, action string, pmaxClient pmax.Pmax) (bool, error) { + log := log.WithContext(ctx) // for all the SG in saved local SG mgSGList, err := pmaxClient.GetStorageGroupMigration(ctx, symID) if err != nil { @@ -225,6 +231,7 @@ var StorageGroupCommit = func(ctx context.Context, symID, action string, pmaxCli // AddVolumesToRemoteSG adds remote volumes to default SRP SG on remote array var AddVolumesToRemoteSG = func(ctx context.Context, remoteSymID string, pmaxClient pmax.Pmax) (bool, error) { + log := log.WithContext(ctx) // add all the volumes in SGtoRemoteVols log.Debugf("SGToRemoteVols: %v", SGToRemoteVols) if len(SGToRemoteVols) == 0 { diff --git a/pkg/migration/migration_test.go b/pkg/migration/migration_test.go index 1801769f..d1cf8197 100644 --- a/pkg/migration/migration_test.go +++ b/pkg/migration/migration_test.go @@ -22,8 +22,8 @@ import ( "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" types "github.com/dell/gopowermax/v2/types/v100" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/pkg/symmetrix/metro.go b/pkg/symmetrix/metro.go index 4cab60e1..7b034d86 100644 --- a/pkg/symmetrix/metro.go +++ b/pkg/symmetrix/metro.go @@ -21,10 +21,13 @@ import ( "sync/atomic" "time" + "github.com/dell/csmlog" + pmax "github.com/dell/gopowermax/v2" - log "github.com/sirupsen/logrus" ) +var log = csmlog.GetLogger() + var metroClients sync.Map const ( diff --git a/pkg/symmetrix/mocks/pmaxclient.go b/pkg/symmetrix/mocks/pmaxclient.go index 9623b428..8a8344af 100644 --- a/pkg/symmetrix/mocks/pmaxclient.go +++ b/pkg/symmetrix/mocks/pmaxclient.go @@ -1,10 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/dell/csi-powermax/v2/pkg/symmetrix (interfaces: PmaxClient) -// -// Generated by this command: -// -// mockgen -destination=pkg/symmetrix/mocks/pmaxclient.go -package=mocks github.com/dell/csi-powermax/v2/pkg/symmetrix PmaxClient -// // Package mocks is a generated GoMock package. package mocks @@ -16,14 +11,13 @@ import ( pmax "github.com/dell/gopowermax/v2" v100 "github.com/dell/gopowermax/v2/types/v100" - gomock "go.uber.org/mock/gomock" + gomock "github.com/golang/mock/gomock" ) // MockPmaxClient is a mock of PmaxClient interface. type MockPmaxClient struct { ctrl *gomock.Controller recorder *MockPmaxClientMockRecorder - isgomock struct{} } // MockPmaxClientMockRecorder is the mock recorder for MockPmaxClient. @@ -44,10 +38,10 @@ func (m *MockPmaxClient) EXPECT() *MockPmaxClientMockRecorder { } // AddVolumesToProtectedStorageGroup mocks base method. -func (m *MockPmaxClient) AddVolumesToProtectedStorageGroup(ctx context.Context, symID, storageGroupID, remoteSymID, remoteStorageGroupID string, force bool, volumeIDs ...string) error { +func (m *MockPmaxClient) AddVolumesToProtectedStorageGroup(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 bool, arg6 ...string) error { m.ctrl.T.Helper() - varargs := []any{ctx, symID, storageGroupID, remoteSymID, remoteStorageGroupID, force} - for _, a := range volumeIDs { + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4, arg5} + for _, a := range arg6 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddVolumesToProtectedStorageGroup", varargs...) @@ -56,17 +50,17 @@ func (m *MockPmaxClient) AddVolumesToProtectedStorageGroup(ctx context.Context, } // AddVolumesToProtectedStorageGroup indicates an expected call of AddVolumesToProtectedStorageGroup. -func (mr *MockPmaxClientMockRecorder) AddVolumesToProtectedStorageGroup(ctx, symID, storageGroupID, remoteSymID, remoteStorageGroupID, force any, volumeIDs ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) AddVolumesToProtectedStorageGroup(arg0, arg1, arg2, arg3, arg4, arg5 interface{}, arg6 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, storageGroupID, remoteSymID, remoteStorageGroupID, force}, volumeIDs...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4, arg5}, arg6...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVolumesToProtectedStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).AddVolumesToProtectedStorageGroup), varargs...) } // AddVolumesToStorageGroup mocks base method. -func (m *MockPmaxClient) AddVolumesToStorageGroup(ctx context.Context, symID, storageGroupID string, force bool, volumeIDs ...string) error { +func (m *MockPmaxClient) AddVolumesToStorageGroup(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 ...string) error { m.ctrl.T.Helper() - varargs := []any{ctx, symID, storageGroupID, force} - for _, a := range volumeIDs { + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddVolumesToStorageGroup", varargs...) @@ -75,17 +69,17 @@ func (m *MockPmaxClient) AddVolumesToStorageGroup(ctx context.Context, symID, st } // AddVolumesToStorageGroup indicates an expected call of AddVolumesToStorageGroup. -func (mr *MockPmaxClientMockRecorder) AddVolumesToStorageGroup(ctx, symID, storageGroupID, force any, volumeIDs ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) AddVolumesToStorageGroup(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, storageGroupID, force}, volumeIDs...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVolumesToStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).AddVolumesToStorageGroup), varargs...) } // AddVolumesToStorageGroupS mocks base method. -func (m *MockPmaxClient) AddVolumesToStorageGroupS(ctx context.Context, symID, storageGroupID string, force bool, volumeIDs ...string) error { +func (m *MockPmaxClient) AddVolumesToStorageGroupS(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 ...string) error { m.ctrl.T.Helper() - varargs := []any{ctx, symID, storageGroupID, force} - for _, a := range volumeIDs { + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddVolumesToStorageGroupS", varargs...) @@ -94,240 +88,254 @@ func (m *MockPmaxClient) AddVolumesToStorageGroupS(ctx context.Context, symID, s } // AddVolumesToStorageGroupS indicates an expected call of AddVolumesToStorageGroupS. -func (mr *MockPmaxClientMockRecorder) AddVolumesToStorageGroupS(ctx, symID, storageGroupID, force any, volumeIDs ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) AddVolumesToStorageGroupS(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, storageGroupID, force}, volumeIDs...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVolumesToStorageGroupS", reflect.TypeOf((*MockPmaxClient)(nil).AddVolumesToStorageGroupS), varargs...) } // Authenticate mocks base method. -func (m *MockPmaxClient) Authenticate(ctx context.Context, configConnect *pmax.ConfigConnect) error { +func (m *MockPmaxClient) Authenticate(arg0 context.Context, arg1 *pmax.ConfigConnect) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Authenticate", ctx, configConnect) + ret := m.ctrl.Call(m, "Authenticate", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // Authenticate indicates an expected call of Authenticate. -func (mr *MockPmaxClientMockRecorder) Authenticate(ctx, configConnect any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) Authenticate(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticate", reflect.TypeOf((*MockPmaxClient)(nil).Authenticate), ctx, configConnect) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticate", reflect.TypeOf((*MockPmaxClient)(nil).Authenticate), arg0, arg1) +} + +// CloneVolumeFromVolume mocks base method. +func (m *MockPmaxClient) CloneVolumeFromVolume(arg0 context.Context, arg1 string, arg2 v100.ReplicationRequest) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloneVolumeFromVolume", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// CloneVolumeFromVolume indicates an expected call of CloneVolumeFromVolume. +func (mr *MockPmaxClientMockRecorder) CloneVolumeFromVolume(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloneVolumeFromVolume", reflect.TypeOf((*MockPmaxClient)(nil).CloneVolumeFromVolume), arg0, arg1, arg2) } // CreateFileSystem mocks base method. -func (m *MockPmaxClient) CreateFileSystem(ctx context.Context, symID, name, nasServer, serviceLevel string, sizeInMiB int64) (*v100.FileSystem, error) { +func (m *MockPmaxClient) CreateFileSystem(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 int64) (*v100.FileSystem, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateFileSystem", ctx, symID, name, nasServer, serviceLevel, sizeInMiB) + ret := m.ctrl.Call(m, "CreateFileSystem", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.FileSystem) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateFileSystem indicates an expected call of CreateFileSystem. -func (mr *MockPmaxClientMockRecorder) CreateFileSystem(ctx, symID, name, nasServer, serviceLevel, sizeInMiB any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateFileSystem(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFileSystem", reflect.TypeOf((*MockPmaxClient)(nil).CreateFileSystem), ctx, symID, name, nasServer, serviceLevel, sizeInMiB) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFileSystem", reflect.TypeOf((*MockPmaxClient)(nil).CreateFileSystem), arg0, arg1, arg2, arg3, arg4, arg5) } // CreateHost mocks base method. -func (m *MockPmaxClient) CreateHost(ctx context.Context, symID, hostID string, initiatorIDs []string, hostFlags *v100.HostFlags) (*v100.Host, error) { +func (m *MockPmaxClient) CreateHost(arg0 context.Context, arg1, arg2 string, arg3 []string, arg4 *v100.HostFlags) (*v100.Host, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateHost", ctx, symID, hostID, initiatorIDs, hostFlags) + ret := m.ctrl.Call(m, "CreateHost", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.Host) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateHost indicates an expected call of CreateHost. -func (mr *MockPmaxClientMockRecorder) CreateHost(ctx, symID, hostID, initiatorIDs, hostFlags any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateHost(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHost", reflect.TypeOf((*MockPmaxClient)(nil).CreateHost), ctx, symID, hostID, initiatorIDs, hostFlags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHost", reflect.TypeOf((*MockPmaxClient)(nil).CreateHost), arg0, arg1, arg2, arg3, arg4) } // CreateHostGroup mocks base method. -func (m *MockPmaxClient) CreateHostGroup(ctx context.Context, symID, hostGroupID string, hostIDs []string, hostFlags *v100.HostFlags) (*v100.HostGroup, error) { +func (m *MockPmaxClient) CreateHostGroup(arg0 context.Context, arg1, arg2 string, arg3 []string, arg4 *v100.HostFlags) (*v100.HostGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateHostGroup", ctx, symID, hostGroupID, hostIDs, hostFlags) + ret := m.ctrl.Call(m, "CreateHostGroup", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.HostGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateHostGroup indicates an expected call of CreateHostGroup. -func (mr *MockPmaxClientMockRecorder) CreateHostGroup(ctx, symID, hostGroupID, hostIDs, hostFlags any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateHostGroup(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHostGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreateHostGroup), ctx, symID, hostGroupID, hostIDs, hostFlags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHostGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreateHostGroup), arg0, arg1, arg2, arg3, arg4) } // CreateMaskingView mocks base method. -func (m *MockPmaxClient) CreateMaskingView(ctx context.Context, symID, maskingViewID, storageGroupID, hostOrhostGroupID string, isHost bool, portGroupID string) (*v100.MaskingView, error) { +func (m *MockPmaxClient) CreateMaskingView(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 bool, arg6 string) (*v100.MaskingView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateMaskingView", ctx, symID, maskingViewID, storageGroupID, hostOrhostGroupID, isHost, portGroupID) + ret := m.ctrl.Call(m, "CreateMaskingView", arg0, arg1, arg2, arg3, arg4, arg5, arg6) ret0, _ := ret[0].(*v100.MaskingView) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateMaskingView indicates an expected call of CreateMaskingView. -func (mr *MockPmaxClientMockRecorder) CreateMaskingView(ctx, symID, maskingViewID, storageGroupID, hostOrhostGroupID, isHost, portGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateMaskingView(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMaskingView", reflect.TypeOf((*MockPmaxClient)(nil).CreateMaskingView), ctx, symID, maskingViewID, storageGroupID, hostOrhostGroupID, isHost, portGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMaskingView", reflect.TypeOf((*MockPmaxClient)(nil).CreateMaskingView), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } // CreateMigrationEnvironment mocks base method. -func (m *MockPmaxClient) CreateMigrationEnvironment(ctx context.Context, sourceSymID, remoteSymID string) (*v100.MigrationEnv, error) { +func (m *MockPmaxClient) CreateMigrationEnvironment(arg0 context.Context, arg1, arg2 string) (*v100.MigrationEnv, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateMigrationEnvironment", ctx, sourceSymID, remoteSymID) + ret := m.ctrl.Call(m, "CreateMigrationEnvironment", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.MigrationEnv) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateMigrationEnvironment indicates an expected call of CreateMigrationEnvironment. -func (mr *MockPmaxClientMockRecorder) CreateMigrationEnvironment(ctx, sourceSymID, remoteSymID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateMigrationEnvironment(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMigrationEnvironment", reflect.TypeOf((*MockPmaxClient)(nil).CreateMigrationEnvironment), ctx, sourceSymID, remoteSymID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMigrationEnvironment", reflect.TypeOf((*MockPmaxClient)(nil).CreateMigrationEnvironment), arg0, arg1, arg2) } // CreateNFSExport mocks base method. -func (m *MockPmaxClient) CreateNFSExport(ctx context.Context, symID string, createNFSExportPayload v100.CreateNFSExport) (*v100.NFSExport, error) { +func (m *MockPmaxClient) CreateNFSExport(arg0 context.Context, arg1 string, arg2 v100.CreateNFSExport) (*v100.NFSExport, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateNFSExport", ctx, symID, createNFSExportPayload) + ret := m.ctrl.Call(m, "CreateNFSExport", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.NFSExport) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateNFSExport indicates an expected call of CreateNFSExport. -func (mr *MockPmaxClientMockRecorder) CreateNFSExport(ctx, symID, createNFSExportPayload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateNFSExport(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNFSExport", reflect.TypeOf((*MockPmaxClient)(nil).CreateNFSExport), ctx, symID, createNFSExportPayload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNFSExport", reflect.TypeOf((*MockPmaxClient)(nil).CreateNFSExport), arg0, arg1, arg2) } // CreatePortGroup mocks base method. -func (m *MockPmaxClient) CreatePortGroup(ctx context.Context, symID, portGroupID string, dirPorts []v100.PortKey, protocol string) (*v100.PortGroup, error) { +func (m *MockPmaxClient) CreatePortGroup(arg0 context.Context, arg1, arg2 string, arg3 []v100.PortKey, arg4 string) (*v100.PortGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreatePortGroup", ctx, symID, portGroupID, dirPorts, protocol) + ret := m.ctrl.Call(m, "CreatePortGroup", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.PortGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // CreatePortGroup indicates an expected call of CreatePortGroup. -func (mr *MockPmaxClientMockRecorder) CreatePortGroup(ctx, symID, portGroupID, dirPorts, protocol any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreatePortGroup(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreatePortGroup), ctx, symID, portGroupID, dirPorts, protocol) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreatePortGroup), arg0, arg1, arg2, arg3, arg4) } // CreateRDFPair mocks base method. -func (m *MockPmaxClient) CreateRDFPair(ctx context.Context, symID, rdfGroupNo, deviceID, rdfMode, rdfType string, establish, exemptConsistency bool) (*v100.RDFDevicePairList, error) { +func (m *MockPmaxClient) CreateRDFPair(arg0 context.Context, arg1, arg2, arg3, arg4, arg5 string, arg6, arg7 bool) (*v100.RDFDevicePairList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateRDFPair", ctx, symID, rdfGroupNo, deviceID, rdfMode, rdfType, establish, exemptConsistency) + ret := m.ctrl.Call(m, "CreateRDFPair", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) ret0, _ := ret[0].(*v100.RDFDevicePairList) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateRDFPair indicates an expected call of CreateRDFPair. -func (mr *MockPmaxClientMockRecorder) CreateRDFPair(ctx, symID, rdfGroupNo, deviceID, rdfMode, rdfType, establish, exemptConsistency any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateRDFPair(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRDFPair", reflect.TypeOf((*MockPmaxClient)(nil).CreateRDFPair), ctx, symID, rdfGroupNo, deviceID, rdfMode, rdfType, establish, exemptConsistency) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRDFPair", reflect.TypeOf((*MockPmaxClient)(nil).CreateRDFPair), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) } // CreateSGMigration mocks base method. -func (m *MockPmaxClient) CreateSGMigration(ctx context.Context, localSymID, remoteSymID, storageGroup string) (*v100.MigrationSession, error) { +func (m *MockPmaxClient) CreateSGMigration(arg0 context.Context, arg1, arg2, arg3 string) (*v100.MigrationSession, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSGMigration", ctx, localSymID, remoteSymID, storageGroup) + ret := m.ctrl.Call(m, "CreateSGMigration", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.MigrationSession) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSGMigration indicates an expected call of CreateSGMigration. -func (mr *MockPmaxClientMockRecorder) CreateSGMigration(ctx, localSymID, remoteSymID, storageGroup any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateSGMigration(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSGMigration", reflect.TypeOf((*MockPmaxClient)(nil).CreateSGMigration), ctx, localSymID, remoteSymID, storageGroup) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSGMigration", reflect.TypeOf((*MockPmaxClient)(nil).CreateSGMigration), arg0, arg1, arg2, arg3) } // CreateSGReplica mocks base method. -func (m *MockPmaxClient) CreateSGReplica(ctx context.Context, symID, remoteSymID, rdfMode, rdfGroupNo, sourceSG, remoteSGName, remoteServiceLevel string, bias bool) (*v100.SGRDFInfo, error) { +func (m *MockPmaxClient) CreateSGReplica(arg0 context.Context, arg1, arg2, arg3, arg4, arg5, arg6, arg7 string, arg8 bool) (*v100.SGRDFInfo, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSGReplica", ctx, symID, remoteSymID, rdfMode, rdfGroupNo, sourceSG, remoteSGName, remoteServiceLevel, bias) + ret := m.ctrl.Call(m, "CreateSGReplica", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) ret0, _ := ret[0].(*v100.SGRDFInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSGReplica indicates an expected call of CreateSGReplica. -func (mr *MockPmaxClientMockRecorder) CreateSGReplica(ctx, symID, remoteSymID, rdfMode, rdfGroupNo, sourceSG, remoteSGName, remoteServiceLevel, bias any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateSGReplica(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSGReplica", reflect.TypeOf((*MockPmaxClient)(nil).CreateSGReplica), ctx, symID, remoteSymID, rdfMode, rdfGroupNo, sourceSG, remoteSGName, remoteServiceLevel, bias) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSGReplica", reflect.TypeOf((*MockPmaxClient)(nil).CreateSGReplica), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } // CreateSnapshot mocks base method. -func (m *MockPmaxClient) CreateSnapshot(ctx context.Context, symID, SnapID string, sourceVolumeList []v100.VolumeList, ttl int64) error { +func (m *MockPmaxClient) CreateSnapshot(arg0 context.Context, arg1, arg2 string, arg3 []v100.VolumeList, arg4 int64) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSnapshot", ctx, symID, SnapID, sourceVolumeList, ttl) + ret := m.ctrl.Call(m, "CreateSnapshot", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // CreateSnapshot indicates an expected call of CreateSnapshot. -func (mr *MockPmaxClientMockRecorder) CreateSnapshot(ctx, symID, SnapID, sourceVolumeList, ttl any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateSnapshot(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).CreateSnapshot), ctx, symID, SnapID, sourceVolumeList, ttl) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).CreateSnapshot), arg0, arg1, arg2, arg3, arg4) } // CreateSnapshotPolicy mocks base method. -func (m *MockPmaxClient) CreateSnapshotPolicy(ctx context.Context, symID, snapshotPolicyID, interval string, offsetMins int32, complianceCountWarn, complianceCountCritical int64, optionalPayload map[string]any) (*v100.SnapshotPolicy, error) { +func (m *MockPmaxClient) CreateSnapshotPolicy(arg0 context.Context, arg1, arg2, arg3 string, arg4 int32, arg5, arg6 int64, arg7 map[string]interface{}) (*v100.SnapshotPolicy, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSnapshotPolicy", ctx, symID, snapshotPolicyID, interval, offsetMins, complianceCountWarn, complianceCountCritical, optionalPayload) + ret := m.ctrl.Call(m, "CreateSnapshotPolicy", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) ret0, _ := ret[0].(*v100.SnapshotPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSnapshotPolicy indicates an expected call of CreateSnapshotPolicy. -func (mr *MockPmaxClientMockRecorder) CreateSnapshotPolicy(ctx, symID, snapshotPolicyID, interval, offsetMins, complianceCountWarn, complianceCountCritical, optionalPayload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateSnapshotPolicy(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).CreateSnapshotPolicy), ctx, symID, snapshotPolicyID, interval, offsetMins, complianceCountWarn, complianceCountCritical, optionalPayload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).CreateSnapshotPolicy), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) } // CreateStorageGroup mocks base method. -func (m *MockPmaxClient) CreateStorageGroup(ctx context.Context, symID, storageGroupID, srpID, serviceLevel string, thickVolumes bool, optionalPayload map[string]any) (*v100.StorageGroup, error) { +func (m *MockPmaxClient) CreateStorageGroup(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 bool, arg6 map[string]interface{}) (*v100.StorageGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateStorageGroup", ctx, symID, storageGroupID, srpID, serviceLevel, thickVolumes, optionalPayload) + ret := m.ctrl.Call(m, "CreateStorageGroup", arg0, arg1, arg2, arg3, arg4, arg5, arg6) ret0, _ := ret[0].(*v100.StorageGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateStorageGroup indicates an expected call of CreateStorageGroup. -func (mr *MockPmaxClientMockRecorder) CreateStorageGroup(ctx, symID, storageGroupID, srpID, serviceLevel, thickVolumes, optionalPayload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateStorageGroup(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreateStorageGroup), ctx, symID, storageGroupID, srpID, serviceLevel, thickVolumes, optionalPayload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreateStorageGroup), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } // CreateStorageGroupSnapshot mocks base method. -func (m *MockPmaxClient) CreateStorageGroupSnapshot(ctx context.Context, symID, storageGroupID string, payload *v100.CreateStorageGroupSnapshot) (*v100.StorageGroupSnap, error) { +func (m *MockPmaxClient) CreateStorageGroupSnapshot(arg0 context.Context, arg1, arg2 string, arg3 *v100.CreateStorageGroupSnapshot) (*v100.StorageGroupSnap, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateStorageGroupSnapshot", ctx, symID, storageGroupID, payload) + ret := m.ctrl.Call(m, "CreateStorageGroupSnapshot", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.StorageGroupSnap) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateStorageGroupSnapshot indicates an expected call of CreateStorageGroupSnapshot. -func (mr *MockPmaxClientMockRecorder) CreateStorageGroupSnapshot(ctx, symID, storageGroupID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateStorageGroupSnapshot(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStorageGroupSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).CreateStorageGroupSnapshot), ctx, symID, storageGroupID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStorageGroupSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).CreateStorageGroupSnapshot), arg0, arg1, arg2, arg3) } // CreateVolumeInProtectedStorageGroupS mocks base method. -func (m *MockPmaxClient) CreateVolumeInProtectedStorageGroupS(ctx context.Context, symID, remoteSymID, storageGroupID, remoteStorageGroupID, volumeName string, volumeSize any, volOpts map[string]any, opts ...http.Header) (*v100.Volume, error) { +func (m *MockPmaxClient) CreateVolumeInProtectedStorageGroupS(arg0 context.Context, arg1, arg2, arg3, arg4, arg5 string, arg6 interface{}, arg7 map[string]interface{}, arg8 ...http.Header) (*v100.Volume, error) { m.ctrl.T.Helper() - varargs := []any{ctx, symID, remoteSymID, storageGroupID, remoteStorageGroupID, volumeName, volumeSize, volOpts} - for _, a := range opts { + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7} + for _, a := range arg8 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CreateVolumeInProtectedStorageGroupS", varargs...) @@ -337,32 +345,32 @@ func (m *MockPmaxClient) CreateVolumeInProtectedStorageGroupS(ctx context.Contex } // CreateVolumeInProtectedStorageGroupS indicates an expected call of CreateVolumeInProtectedStorageGroupS. -func (mr *MockPmaxClientMockRecorder) CreateVolumeInProtectedStorageGroupS(ctx, symID, remoteSymID, storageGroupID, remoteStorageGroupID, volumeName, volumeSize, volOpts any, opts ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateVolumeInProtectedStorageGroupS(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}, arg8 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, remoteSymID, storageGroupID, remoteStorageGroupID, volumeName, volumeSize, volOpts}, opts...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7}, arg8...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolumeInProtectedStorageGroupS", reflect.TypeOf((*MockPmaxClient)(nil).CreateVolumeInProtectedStorageGroupS), varargs...) } // CreateVolumeInStorageGroup mocks base method. -func (m *MockPmaxClient) CreateVolumeInStorageGroup(ctx context.Context, symID, storageGroupID, volumeName string, volumeSize any, volOpts map[string]any) (*v100.Volume, error) { +func (m *MockPmaxClient) CreateVolumeInStorageGroup(arg0 context.Context, arg1, arg2, arg3 string, arg4 interface{}, arg5 map[string]interface{}) (*v100.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateVolumeInStorageGroup", ctx, symID, storageGroupID, volumeName, volumeSize, volOpts) + ret := m.ctrl.Call(m, "CreateVolumeInStorageGroup", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateVolumeInStorageGroup indicates an expected call of CreateVolumeInStorageGroup. -func (mr *MockPmaxClientMockRecorder) CreateVolumeInStorageGroup(ctx, symID, storageGroupID, volumeName, volumeSize, volOpts any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateVolumeInStorageGroup(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolumeInStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreateVolumeInStorageGroup), ctx, symID, storageGroupID, volumeName, volumeSize, volOpts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolumeInStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).CreateVolumeInStorageGroup), arg0, arg1, arg2, arg3, arg4, arg5) } // CreateVolumeInStorageGroupS mocks base method. -func (m *MockPmaxClient) CreateVolumeInStorageGroupS(ctx context.Context, symID, storageGroupID, volumeName string, volumeSize any, volOpts map[string]any, opts ...http.Header) (*v100.Volume, error) { +func (m *MockPmaxClient) CreateVolumeInStorageGroupS(arg0 context.Context, arg1, arg2, arg3 string, arg4 interface{}, arg5 map[string]interface{}, arg6 ...http.Header) (*v100.Volume, error) { m.ctrl.T.Helper() - varargs := []any{ctx, symID, storageGroupID, volumeName, volumeSize, volOpts} - for _, a := range opts { + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4, arg5} + for _, a := range arg6 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CreateVolumeInStorageGroupS", varargs...) @@ -372,255 +380,255 @@ func (m *MockPmaxClient) CreateVolumeInStorageGroupS(ctx context.Context, symID, } // CreateVolumeInStorageGroupS indicates an expected call of CreateVolumeInStorageGroupS. -func (mr *MockPmaxClientMockRecorder) CreateVolumeInStorageGroupS(ctx, symID, storageGroupID, volumeName, volumeSize, volOpts any, opts ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) CreateVolumeInStorageGroupS(arg0, arg1, arg2, arg3, arg4, arg5 interface{}, arg6 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, storageGroupID, volumeName, volumeSize, volOpts}, opts...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4, arg5}, arg6...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolumeInStorageGroupS", reflect.TypeOf((*MockPmaxClient)(nil).CreateVolumeInStorageGroupS), varargs...) } // DeleteFileSystem mocks base method. -func (m *MockPmaxClient) DeleteFileSystem(ctx context.Context, symID, fsID string) error { +func (m *MockPmaxClient) DeleteFileSystem(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteFileSystem", ctx, symID, fsID) + ret := m.ctrl.Call(m, "DeleteFileSystem", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteFileSystem indicates an expected call of DeleteFileSystem. -func (mr *MockPmaxClientMockRecorder) DeleteFileSystem(ctx, symID, fsID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteFileSystem(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFileSystem", reflect.TypeOf((*MockPmaxClient)(nil).DeleteFileSystem), ctx, symID, fsID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFileSystem", reflect.TypeOf((*MockPmaxClient)(nil).DeleteFileSystem), arg0, arg1, arg2) } // DeleteHost mocks base method. -func (m *MockPmaxClient) DeleteHost(ctx context.Context, symID, hostID string) error { +func (m *MockPmaxClient) DeleteHost(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteHost", ctx, symID, hostID) + ret := m.ctrl.Call(m, "DeleteHost", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteHost indicates an expected call of DeleteHost. -func (mr *MockPmaxClientMockRecorder) DeleteHost(ctx, symID, hostID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteHost(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHost", reflect.TypeOf((*MockPmaxClient)(nil).DeleteHost), ctx, symID, hostID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHost", reflect.TypeOf((*MockPmaxClient)(nil).DeleteHost), arg0, arg1, arg2) } // DeleteHostGroup mocks base method. -func (m *MockPmaxClient) DeleteHostGroup(ctx context.Context, symID, hostGroupID string) error { +func (m *MockPmaxClient) DeleteHostGroup(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteHostGroup", ctx, symID, hostGroupID) + ret := m.ctrl.Call(m, "DeleteHostGroup", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteHostGroup indicates an expected call of DeleteHostGroup. -func (mr *MockPmaxClientMockRecorder) DeleteHostGroup(ctx, symID, hostGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteHostGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHostGroup", reflect.TypeOf((*MockPmaxClient)(nil).DeleteHostGroup), ctx, symID, hostGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHostGroup", reflect.TypeOf((*MockPmaxClient)(nil).DeleteHostGroup), arg0, arg1, arg2) } // DeleteMaskingView mocks base method. -func (m *MockPmaxClient) DeleteMaskingView(ctx context.Context, symID, maskingViewID string) error { +func (m *MockPmaxClient) DeleteMaskingView(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteMaskingView", ctx, symID, maskingViewID) + ret := m.ctrl.Call(m, "DeleteMaskingView", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteMaskingView indicates an expected call of DeleteMaskingView. -func (mr *MockPmaxClientMockRecorder) DeleteMaskingView(ctx, symID, maskingViewID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteMaskingView(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMaskingView", reflect.TypeOf((*MockPmaxClient)(nil).DeleteMaskingView), ctx, symID, maskingViewID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMaskingView", reflect.TypeOf((*MockPmaxClient)(nil).DeleteMaskingView), arg0, arg1, arg2) } // DeleteMigrationEnvironment mocks base method. -func (m *MockPmaxClient) DeleteMigrationEnvironment(ctx context.Context, localSymID, remoteSymID string) error { +func (m *MockPmaxClient) DeleteMigrationEnvironment(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteMigrationEnvironment", ctx, localSymID, remoteSymID) + ret := m.ctrl.Call(m, "DeleteMigrationEnvironment", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteMigrationEnvironment indicates an expected call of DeleteMigrationEnvironment. -func (mr *MockPmaxClientMockRecorder) DeleteMigrationEnvironment(ctx, localSymID, remoteSymID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteMigrationEnvironment(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMigrationEnvironment", reflect.TypeOf((*MockPmaxClient)(nil).DeleteMigrationEnvironment), ctx, localSymID, remoteSymID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMigrationEnvironment", reflect.TypeOf((*MockPmaxClient)(nil).DeleteMigrationEnvironment), arg0, arg1, arg2) } // DeleteNASServer mocks base method. -func (m *MockPmaxClient) DeleteNASServer(ctx context.Context, symID, nasID string) error { +func (m *MockPmaxClient) DeleteNASServer(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteNASServer", ctx, symID, nasID) + ret := m.ctrl.Call(m, "DeleteNASServer", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteNASServer indicates an expected call of DeleteNASServer. -func (mr *MockPmaxClientMockRecorder) DeleteNASServer(ctx, symID, nasID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteNASServer(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNASServer", reflect.TypeOf((*MockPmaxClient)(nil).DeleteNASServer), ctx, symID, nasID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNASServer", reflect.TypeOf((*MockPmaxClient)(nil).DeleteNASServer), arg0, arg1, arg2) } // DeleteNFSExport mocks base method. -func (m *MockPmaxClient) DeleteNFSExport(ctx context.Context, symID, nfsExportID string) error { +func (m *MockPmaxClient) DeleteNFSExport(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteNFSExport", ctx, symID, nfsExportID) + ret := m.ctrl.Call(m, "DeleteNFSExport", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteNFSExport indicates an expected call of DeleteNFSExport. -func (mr *MockPmaxClientMockRecorder) DeleteNFSExport(ctx, symID, nfsExportID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteNFSExport(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNFSExport", reflect.TypeOf((*MockPmaxClient)(nil).DeleteNFSExport), ctx, symID, nfsExportID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNFSExport", reflect.TypeOf((*MockPmaxClient)(nil).DeleteNFSExport), arg0, arg1, arg2) } // DeletePortGroup mocks base method. -func (m *MockPmaxClient) DeletePortGroup(ctx context.Context, symID, portGroupID string) error { +func (m *MockPmaxClient) DeletePortGroup(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeletePortGroup", ctx, symID, portGroupID) + ret := m.ctrl.Call(m, "DeletePortGroup", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeletePortGroup indicates an expected call of DeletePortGroup. -func (mr *MockPmaxClientMockRecorder) DeletePortGroup(ctx, symID, portGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeletePortGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).DeletePortGroup), ctx, symID, portGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).DeletePortGroup), arg0, arg1, arg2) } // DeleteSnapshot mocks base method. -func (m *MockPmaxClient) DeleteSnapshot(ctx context.Context, symID, SnapID string, sourceVolumes []v100.VolumeList, generation int64) error { +func (m *MockPmaxClient) DeleteSnapshot(arg0 context.Context, arg1, arg2 string, arg3 []v100.VolumeList, arg4 int64) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSnapshot", ctx, symID, SnapID, sourceVolumes, generation) + ret := m.ctrl.Call(m, "DeleteSnapshot", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // DeleteSnapshot indicates an expected call of DeleteSnapshot. -func (mr *MockPmaxClientMockRecorder) DeleteSnapshot(ctx, symID, SnapID, sourceVolumes, generation any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteSnapshot(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).DeleteSnapshot), ctx, symID, SnapID, sourceVolumes, generation) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).DeleteSnapshot), arg0, arg1, arg2, arg3, arg4) } // DeleteSnapshotPolicy mocks base method. -func (m *MockPmaxClient) DeleteSnapshotPolicy(ctx context.Context, symID, snapshotPolicyID string) error { +func (m *MockPmaxClient) DeleteSnapshotPolicy(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSnapshotPolicy", ctx, symID, snapshotPolicyID) + ret := m.ctrl.Call(m, "DeleteSnapshotPolicy", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteSnapshotPolicy indicates an expected call of DeleteSnapshotPolicy. -func (mr *MockPmaxClientMockRecorder) DeleteSnapshotPolicy(ctx, symID, snapshotPolicyID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteSnapshotPolicy(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).DeleteSnapshotPolicy), ctx, symID, snapshotPolicyID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).DeleteSnapshotPolicy), arg0, arg1, arg2) } // DeleteSnapshotS mocks base method. -func (m *MockPmaxClient) DeleteSnapshotS(ctx context.Context, symID, SnapID string, sourceVolumes []v100.VolumeList, generation int64) error { +func (m *MockPmaxClient) DeleteSnapshotS(arg0 context.Context, arg1, arg2 string, arg3 []v100.VolumeList, arg4 int64) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSnapshotS", ctx, symID, SnapID, sourceVolumes, generation) + ret := m.ctrl.Call(m, "DeleteSnapshotS", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // DeleteSnapshotS indicates an expected call of DeleteSnapshotS. -func (mr *MockPmaxClientMockRecorder) DeleteSnapshotS(ctx, symID, SnapID, sourceVolumes, generation any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteSnapshotS(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshotS", reflect.TypeOf((*MockPmaxClient)(nil).DeleteSnapshotS), ctx, symID, SnapID, sourceVolumes, generation) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshotS", reflect.TypeOf((*MockPmaxClient)(nil).DeleteSnapshotS), arg0, arg1, arg2, arg3, arg4) } // DeleteStorageGroup mocks base method. -func (m *MockPmaxClient) DeleteStorageGroup(ctx context.Context, symID, storageGroupID string) error { +func (m *MockPmaxClient) DeleteStorageGroup(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteStorageGroup", ctx, symID, storageGroupID) + ret := m.ctrl.Call(m, "DeleteStorageGroup", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteStorageGroup indicates an expected call of DeleteStorageGroup. -func (mr *MockPmaxClientMockRecorder) DeleteStorageGroup(ctx, symID, storageGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteStorageGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).DeleteStorageGroup), ctx, symID, storageGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).DeleteStorageGroup), arg0, arg1, arg2) } // DeleteStorageGroupSnapshot mocks base method. -func (m *MockPmaxClient) DeleteStorageGroupSnapshot(ctx context.Context, symID, storageGroupID, snapshotID, snapID string) error { +func (m *MockPmaxClient) DeleteStorageGroupSnapshot(arg0 context.Context, arg1, arg2, arg3, arg4 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteStorageGroupSnapshot", ctx, symID, storageGroupID, snapshotID, snapID) + ret := m.ctrl.Call(m, "DeleteStorageGroupSnapshot", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // DeleteStorageGroupSnapshot indicates an expected call of DeleteStorageGroupSnapshot. -func (mr *MockPmaxClientMockRecorder) DeleteStorageGroupSnapshot(ctx, symID, storageGroupID, snapshotID, snapID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteStorageGroupSnapshot(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStorageGroupSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).DeleteStorageGroupSnapshot), ctx, symID, storageGroupID, snapshotID, snapID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStorageGroupSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).DeleteStorageGroupSnapshot), arg0, arg1, arg2, arg3, arg4) } // DeleteVolume mocks base method. -func (m *MockPmaxClient) DeleteVolume(ctx context.Context, symID, volumeID string) error { +func (m *MockPmaxClient) DeleteVolume(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteVolume", ctx, symID, volumeID) + ret := m.ctrl.Call(m, "DeleteVolume", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // DeleteVolume indicates an expected call of DeleteVolume. -func (mr *MockPmaxClientMockRecorder) DeleteVolume(ctx, symID, volumeID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteVolume(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolume", reflect.TypeOf((*MockPmaxClient)(nil).DeleteVolume), ctx, symID, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolume", reflect.TypeOf((*MockPmaxClient)(nil).DeleteVolume), arg0, arg1, arg2) } // DeleteVolumeIDsIterator mocks base method. -func (m *MockPmaxClient) DeleteVolumeIDsIterator(ctx context.Context, iter *v100.VolumeIterator) error { +func (m *MockPmaxClient) DeleteVolumeIDsIterator(arg0 context.Context, arg1 *v100.VolumeIterator) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteVolumeIDsIterator", ctx, iter) + ret := m.ctrl.Call(m, "DeleteVolumeIDsIterator", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeleteVolumeIDsIterator indicates an expected call of DeleteVolumeIDsIterator. -func (mr *MockPmaxClientMockRecorder) DeleteVolumeIDsIterator(ctx, iter any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) DeleteVolumeIDsIterator(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolumeIDsIterator", reflect.TypeOf((*MockPmaxClient)(nil).DeleteVolumeIDsIterator), ctx, iter) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolumeIDsIterator", reflect.TypeOf((*MockPmaxClient)(nil).DeleteVolumeIDsIterator), arg0, arg1) } // ExecuteCreateRDFGroup mocks base method. -func (m *MockPmaxClient) ExecuteCreateRDFGroup(ctx context.Context, symID string, CreateRDFPayload *v100.RDFGroupCreate) error { +func (m *MockPmaxClient) ExecuteCreateRDFGroup(arg0 context.Context, arg1 string, arg2 *v100.RDFGroupCreate) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExecuteCreateRDFGroup", ctx, symID, CreateRDFPayload) + ret := m.ctrl.Call(m, "ExecuteCreateRDFGroup", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // ExecuteCreateRDFGroup indicates an expected call of ExecuteCreateRDFGroup. -func (mr *MockPmaxClientMockRecorder) ExecuteCreateRDFGroup(ctx, symID, CreateRDFPayload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ExecuteCreateRDFGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteCreateRDFGroup", reflect.TypeOf((*MockPmaxClient)(nil).ExecuteCreateRDFGroup), ctx, symID, CreateRDFPayload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteCreateRDFGroup", reflect.TypeOf((*MockPmaxClient)(nil).ExecuteCreateRDFGroup), arg0, arg1, arg2) } // ExecuteReplicationActionOnSG mocks base method. -func (m *MockPmaxClient) ExecuteReplicationActionOnSG(ctx context.Context, symID, action, storageGroup, rdfGroup string, force, exemptConsistency, bias bool) error { +func (m *MockPmaxClient) ExecuteReplicationActionOnSG(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5, arg6, arg7 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExecuteReplicationActionOnSG", ctx, symID, action, storageGroup, rdfGroup, force, exemptConsistency, bias) + ret := m.ctrl.Call(m, "ExecuteReplicationActionOnSG", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) ret0, _ := ret[0].(error) return ret0 } // ExecuteReplicationActionOnSG indicates an expected call of ExecuteReplicationActionOnSG. -func (mr *MockPmaxClientMockRecorder) ExecuteReplicationActionOnSG(ctx, symID, action, storageGroup, rdfGroup, force, exemptConsistency, bias any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ExecuteReplicationActionOnSG(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteReplicationActionOnSG", reflect.TypeOf((*MockPmaxClient)(nil).ExecuteReplicationActionOnSG), ctx, symID, action, storageGroup, rdfGroup, force, exemptConsistency, bias) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteReplicationActionOnSG", reflect.TypeOf((*MockPmaxClient)(nil).ExecuteReplicationActionOnSG), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) } // ExpandVolume mocks base method. -func (m *MockPmaxClient) ExpandVolume(ctx context.Context, symID, volumeID string, rdfGNo int, volumeSize any, capUnits ...string) (*v100.Volume, error) { +func (m *MockPmaxClient) ExpandVolume(arg0 context.Context, arg1, arg2 string, arg3 int, arg4 interface{}, arg5 ...string) (*v100.Volume, error) { m.ctrl.T.Helper() - varargs := []any{ctx, symID, volumeID, rdfGNo, volumeSize} - for _, a := range capUnits { + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4} + for _, a := range arg5 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ExpandVolume", varargs...) @@ -630,9 +638,9 @@ func (m *MockPmaxClient) ExpandVolume(ctx context.Context, symID, volumeID strin } // ExpandVolume indicates an expected call of ExpandVolume. -func (mr *MockPmaxClientMockRecorder) ExpandVolume(ctx, symID, volumeID, rdfGNo, volumeSize any, capUnits ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ExpandVolume(arg0, arg1, arg2, arg3, arg4 interface{}, arg5 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, volumeID, rdfGNo, volumeSize}, capUnits...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4}, arg5...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandVolume", reflect.TypeOf((*MockPmaxClient)(nil).ExpandVolume), varargs...) } @@ -651,127 +659,127 @@ func (mr *MockPmaxClientMockRecorder) GetAllowedArrays() *gomock.Call { } // GetArrayPerfKeys mocks base method. -func (m *MockPmaxClient) GetArrayPerfKeys(ctx context.Context) (*v100.ArrayKeysResult, error) { +func (m *MockPmaxClient) GetArrayPerfKeys(arg0 context.Context) (*v100.ArrayKeysResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetArrayPerfKeys", ctx) + ret := m.ctrl.Call(m, "GetArrayPerfKeys", arg0) ret0, _ := ret[0].(*v100.ArrayKeysResult) ret1, _ := ret[1].(error) return ret0, ret1 } // GetArrayPerfKeys indicates an expected call of GetArrayPerfKeys. -func (mr *MockPmaxClientMockRecorder) GetArrayPerfKeys(ctx any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetArrayPerfKeys(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArrayPerfKeys", reflect.TypeOf((*MockPmaxClient)(nil).GetArrayPerfKeys), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetArrayPerfKeys", reflect.TypeOf((*MockPmaxClient)(nil).GetArrayPerfKeys), arg0) } // GetCreateVolInSGPayload mocks base method. -func (m *MockPmaxClient) GetCreateVolInSGPayload(volumeSize any, capUnit, volumeName string, isSync, enableMobility bool, remoteSymID, storageGroupID string, opts ...http.Header) any { +func (m *MockPmaxClient) GetCreateVolInSGPayload(arg0 interface{}, arg1, arg2 string, arg3, arg4 bool, arg5, arg6 string, arg7 ...http.Header) interface{} { m.ctrl.T.Helper() - varargs := []any{volumeSize, capUnit, volumeName, isSync, enableMobility, remoteSymID, storageGroupID} - for _, a := range opts { + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4, arg5, arg6} + for _, a := range arg7 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetCreateVolInSGPayload", varargs...) - ret0, _ := ret[0].(any) + ret0, _ := ret[0].(interface{}) return ret0 } // GetCreateVolInSGPayload indicates an expected call of GetCreateVolInSGPayload. -func (mr *MockPmaxClientMockRecorder) GetCreateVolInSGPayload(volumeSize, capUnit, volumeName, isSync, enableMobility, remoteSymID, storageGroupID any, opts ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetCreateVolInSGPayload(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}, arg7 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{volumeSize, capUnit, volumeName, isSync, enableMobility, remoteSymID, storageGroupID}, opts...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4, arg5, arg6}, arg7...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCreateVolInSGPayload", reflect.TypeOf((*MockPmaxClient)(nil).GetCreateVolInSGPayload), varargs...) } // GetDirectorIDList mocks base method. -func (m *MockPmaxClient) GetDirectorIDList(ctx context.Context, symID string) (*v100.DirectorIDList, error) { +func (m *MockPmaxClient) GetDirectorIDList(arg0 context.Context, arg1 string) (*v100.DirectorIDList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDirectorIDList", ctx, symID) + ret := m.ctrl.Call(m, "GetDirectorIDList", arg0, arg1) ret0, _ := ret[0].(*v100.DirectorIDList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDirectorIDList indicates an expected call of GetDirectorIDList. -func (mr *MockPmaxClientMockRecorder) GetDirectorIDList(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetDirectorIDList(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDirectorIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetDirectorIDList), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDirectorIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetDirectorIDList), arg0, arg1) } // GetFileInterfaceByID mocks base method. -func (m *MockPmaxClient) GetFileInterfaceByID(ctx context.Context, symID, interfaceID string) (*v100.FileInterface, error) { +func (m *MockPmaxClient) GetFileInterfaceByID(arg0 context.Context, arg1, arg2 string) (*v100.FileInterface, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFileInterfaceByID", ctx, symID, interfaceID) + ret := m.ctrl.Call(m, "GetFileInterfaceByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.FileInterface) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFileInterfaceByID indicates an expected call of GetFileInterfaceByID. -func (mr *MockPmaxClientMockRecorder) GetFileInterfaceByID(ctx, symID, interfaceID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetFileInterfaceByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileInterfaceByID", reflect.TypeOf((*MockPmaxClient)(nil).GetFileInterfaceByID), ctx, symID, interfaceID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileInterfaceByID", reflect.TypeOf((*MockPmaxClient)(nil).GetFileInterfaceByID), arg0, arg1, arg2) } // GetFileSystemByID mocks base method. -func (m *MockPmaxClient) GetFileSystemByID(ctx context.Context, symID, fsID string) (*v100.FileSystem, error) { +func (m *MockPmaxClient) GetFileSystemByID(arg0 context.Context, arg1, arg2 string) (*v100.FileSystem, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFileSystemByID", ctx, symID, fsID) + ret := m.ctrl.Call(m, "GetFileSystemByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.FileSystem) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFileSystemByID indicates an expected call of GetFileSystemByID. -func (mr *MockPmaxClientMockRecorder) GetFileSystemByID(ctx, symID, fsID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetFileSystemByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileSystemByID", reflect.TypeOf((*MockPmaxClient)(nil).GetFileSystemByID), ctx, symID, fsID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileSystemByID", reflect.TypeOf((*MockPmaxClient)(nil).GetFileSystemByID), arg0, arg1, arg2) } // GetFileSystemList mocks base method. -func (m *MockPmaxClient) GetFileSystemList(ctx context.Context, symID string, query v100.QueryParams) (*v100.FileSystemIterator, error) { +func (m *MockPmaxClient) GetFileSystemList(arg0 context.Context, arg1 string, arg2 v100.QueryParams) (*v100.FileSystemIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFileSystemList", ctx, symID, query) + ret := m.ctrl.Call(m, "GetFileSystemList", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.FileSystemIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFileSystemList indicates an expected call of GetFileSystemList. -func (mr *MockPmaxClientMockRecorder) GetFileSystemList(ctx, symID, query any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetFileSystemList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileSystemList", reflect.TypeOf((*MockPmaxClient)(nil).GetFileSystemList), ctx, symID, query) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileSystemList", reflect.TypeOf((*MockPmaxClient)(nil).GetFileSystemList), arg0, arg1, arg2) } // GetFileSystemMetricsByID mocks base method. -func (m *MockPmaxClient) GetFileSystemMetricsByID(ctx context.Context, symID, fsID string, metricsQuery []string, firstAvailableTime, lastAvailableTime int64) (*v100.FileSystemMetricsIterator, error) { +func (m *MockPmaxClient) GetFileSystemMetricsByID(arg0 context.Context, arg1, arg2 string, arg3 []string, arg4, arg5 int64) (*v100.FileSystemMetricsIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFileSystemMetricsByID", ctx, symID, fsID, metricsQuery, firstAvailableTime, lastAvailableTime) + ret := m.ctrl.Call(m, "GetFileSystemMetricsByID", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.FileSystemMetricsIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFileSystemMetricsByID indicates an expected call of GetFileSystemMetricsByID. -func (mr *MockPmaxClientMockRecorder) GetFileSystemMetricsByID(ctx, symID, fsID, metricsQuery, firstAvailableTime, lastAvailableTime any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetFileSystemMetricsByID(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileSystemMetricsByID", reflect.TypeOf((*MockPmaxClient)(nil).GetFileSystemMetricsByID), ctx, symID, fsID, metricsQuery, firstAvailableTime, lastAvailableTime) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileSystemMetricsByID", reflect.TypeOf((*MockPmaxClient)(nil).GetFileSystemMetricsByID), arg0, arg1, arg2, arg3, arg4, arg5) } // GetFreeLocalAndRemoteRDFg mocks base method. -func (m *MockPmaxClient) GetFreeLocalAndRemoteRDFg(ctx context.Context, localSymmID, remoteSymmID string) (*v100.NextFreeRDFGroup, error) { +func (m *MockPmaxClient) GetFreeLocalAndRemoteRDFg(arg0 context.Context, arg1, arg2 string) (*v100.NextFreeRDFGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetFreeLocalAndRemoteRDFg", ctx, localSymmID, remoteSymmID) + ret := m.ctrl.Call(m, "GetFreeLocalAndRemoteRDFg", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.NextFreeRDFGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // GetFreeLocalAndRemoteRDFg indicates an expected call of GetFreeLocalAndRemoteRDFg. -func (mr *MockPmaxClientMockRecorder) GetFreeLocalAndRemoteRDFg(ctx, localSymmID, remoteSymmID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetFreeLocalAndRemoteRDFg(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFreeLocalAndRemoteRDFg", reflect.TypeOf((*MockPmaxClient)(nil).GetFreeLocalAndRemoteRDFg), ctx, localSymmID, remoteSymmID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFreeLocalAndRemoteRDFg", reflect.TypeOf((*MockPmaxClient)(nil).GetFreeLocalAndRemoteRDFg), arg0, arg1, arg2) } // GetHTTPClient mocks base method. @@ -789,1190 +797,1295 @@ func (mr *MockPmaxClientMockRecorder) GetHTTPClient() *gomock.Call { } // GetHostByID mocks base method. -func (m *MockPmaxClient) GetHostByID(ctx context.Context, symID, hostID string) (*v100.Host, error) { +func (m *MockPmaxClient) GetHostByID(arg0 context.Context, arg1, arg2 string) (*v100.Host, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetHostByID", ctx, symID, hostID) + ret := m.ctrl.Call(m, "GetHostByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.Host) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHostByID indicates an expected call of GetHostByID. -func (mr *MockPmaxClientMockRecorder) GetHostByID(ctx, symID, hostID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetHostByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostByID", reflect.TypeOf((*MockPmaxClient)(nil).GetHostByID), ctx, symID, hostID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostByID", reflect.TypeOf((*MockPmaxClient)(nil).GetHostByID), arg0, arg1, arg2) } // GetHostGroupByID mocks base method. -func (m *MockPmaxClient) GetHostGroupByID(ctx context.Context, symID, hostGroupID string) (*v100.HostGroup, error) { +func (m *MockPmaxClient) GetHostGroupByID(arg0 context.Context, arg1, arg2 string) (*v100.HostGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetHostGroupByID", ctx, symID, hostGroupID) + ret := m.ctrl.Call(m, "GetHostGroupByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.HostGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHostGroupByID indicates an expected call of GetHostGroupByID. -func (mr *MockPmaxClientMockRecorder) GetHostGroupByID(ctx, symID, hostGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetHostGroupByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostGroupByID", reflect.TypeOf((*MockPmaxClient)(nil).GetHostGroupByID), ctx, symID, hostGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostGroupByID", reflect.TypeOf((*MockPmaxClient)(nil).GetHostGroupByID), arg0, arg1, arg2) } // GetHostGroupList mocks base method. -func (m *MockPmaxClient) GetHostGroupList(ctx context.Context, symID string) (*v100.HostGroupList, error) { +func (m *MockPmaxClient) GetHostGroupList(arg0 context.Context, arg1 string) (*v100.HostGroupList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetHostGroupList", ctx, symID) + ret := m.ctrl.Call(m, "GetHostGroupList", arg0, arg1) ret0, _ := ret[0].(*v100.HostGroupList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHostGroupList indicates an expected call of GetHostGroupList. -func (mr *MockPmaxClientMockRecorder) GetHostGroupList(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetHostGroupList(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostGroupList", reflect.TypeOf((*MockPmaxClient)(nil).GetHostGroupList), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostGroupList", reflect.TypeOf((*MockPmaxClient)(nil).GetHostGroupList), arg0, arg1) } // GetHostList mocks base method. -func (m *MockPmaxClient) GetHostList(ctx context.Context, symID string) (*v100.HostList, error) { +func (m *MockPmaxClient) GetHostList(arg0 context.Context, arg1 string) (*v100.HostList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetHostList", ctx, symID) + ret := m.ctrl.Call(m, "GetHostList", arg0, arg1) ret0, _ := ret[0].(*v100.HostList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHostList indicates an expected call of GetHostList. -func (mr *MockPmaxClientMockRecorder) GetHostList(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetHostList(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostList", reflect.TypeOf((*MockPmaxClient)(nil).GetHostList), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHostList", reflect.TypeOf((*MockPmaxClient)(nil).GetHostList), arg0, arg1) } // GetISCSITargets mocks base method. -func (m *MockPmaxClient) GetISCSITargets(ctx context.Context, symID string) ([]pmax.ISCSITarget, error) { +func (m *MockPmaxClient) GetISCSITargets(arg0 context.Context, arg1 string) ([]pmax.ISCSITarget, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetISCSITargets", ctx, symID) + ret := m.ctrl.Call(m, "GetISCSITargets", arg0, arg1) ret0, _ := ret[0].([]pmax.ISCSITarget) ret1, _ := ret[1].(error) return ret0, ret1 } // GetISCSITargets indicates an expected call of GetISCSITargets. -func (mr *MockPmaxClientMockRecorder) GetISCSITargets(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetISCSITargets(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetISCSITargets", reflect.TypeOf((*MockPmaxClient)(nil).GetISCSITargets), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetISCSITargets", reflect.TypeOf((*MockPmaxClient)(nil).GetISCSITargets), arg0, arg1) } // GetInitiatorByID mocks base method. -func (m *MockPmaxClient) GetInitiatorByID(ctx context.Context, symID, initID string) (*v100.Initiator, error) { +func (m *MockPmaxClient) GetInitiatorByID(arg0 context.Context, arg1, arg2 string) (*v100.Initiator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetInitiatorByID", ctx, symID, initID) + ret := m.ctrl.Call(m, "GetInitiatorByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.Initiator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInitiatorByID indicates an expected call of GetInitiatorByID. -func (mr *MockPmaxClientMockRecorder) GetInitiatorByID(ctx, symID, initID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetInitiatorByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitiatorByID", reflect.TypeOf((*MockPmaxClient)(nil).GetInitiatorByID), ctx, symID, initID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitiatorByID", reflect.TypeOf((*MockPmaxClient)(nil).GetInitiatorByID), arg0, arg1, arg2) } // GetInitiatorList mocks base method. -func (m *MockPmaxClient) GetInitiatorList(ctx context.Context, symID, initiatorHBA string, isISCSI, inHost bool) (*v100.InitiatorList, error) { +func (m *MockPmaxClient) GetInitiatorList(arg0 context.Context, arg1, arg2 string, arg3, arg4 bool) (*v100.InitiatorList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetInitiatorList", ctx, symID, initiatorHBA, isISCSI, inHost) + ret := m.ctrl.Call(m, "GetInitiatorList", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.InitiatorList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInitiatorList indicates an expected call of GetInitiatorList. -func (mr *MockPmaxClientMockRecorder) GetInitiatorList(ctx, symID, initiatorHBA, isISCSI, inHost any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetInitiatorList(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitiatorList", reflect.TypeOf((*MockPmaxClient)(nil).GetInitiatorList), ctx, symID, initiatorHBA, isISCSI, inHost) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitiatorList", reflect.TypeOf((*MockPmaxClient)(nil).GetInitiatorList), arg0, arg1, arg2, arg3, arg4) } // GetJobByID mocks base method. -func (m *MockPmaxClient) GetJobByID(ctx context.Context, symID, jobID string) (*v100.Job, error) { +func (m *MockPmaxClient) GetJobByID(arg0 context.Context, arg1, arg2 string) (*v100.Job, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetJobByID", ctx, symID, jobID) + ret := m.ctrl.Call(m, "GetJobByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.Job) ret1, _ := ret[1].(error) return ret0, ret1 } // GetJobByID indicates an expected call of GetJobByID. -func (mr *MockPmaxClientMockRecorder) GetJobByID(ctx, symID, jobID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetJobByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobByID", reflect.TypeOf((*MockPmaxClient)(nil).GetJobByID), ctx, symID, jobID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobByID", reflect.TypeOf((*MockPmaxClient)(nil).GetJobByID), arg0, arg1, arg2) } // GetJobIDList mocks base method. -func (m *MockPmaxClient) GetJobIDList(ctx context.Context, symID, statusQuery string) ([]string, error) { +func (m *MockPmaxClient) GetJobIDList(arg0 context.Context, arg1, arg2 string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetJobIDList", ctx, symID, statusQuery) + ret := m.ctrl.Call(m, "GetJobIDList", arg0, arg1, arg2) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetJobIDList indicates an expected call of GetJobIDList. -func (mr *MockPmaxClientMockRecorder) GetJobIDList(ctx, symID, statusQuery any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetJobIDList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetJobIDList), ctx, symID, statusQuery) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetJobIDList), arg0, arg1, arg2) } // GetListOfTargetAddresses mocks base method. -func (m *MockPmaxClient) GetListOfTargetAddresses(ctx context.Context, symID string) ([]string, error) { +func (m *MockPmaxClient) GetListOfTargetAddresses(arg0 context.Context, arg1 string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListOfTargetAddresses", ctx, symID) + ret := m.ctrl.Call(m, "GetListOfTargetAddresses", arg0, arg1) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListOfTargetAddresses indicates an expected call of GetListOfTargetAddresses. -func (mr *MockPmaxClientMockRecorder) GetListOfTargetAddresses(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetListOfTargetAddresses(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListOfTargetAddresses", reflect.TypeOf((*MockPmaxClient)(nil).GetListOfTargetAddresses), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListOfTargetAddresses", reflect.TypeOf((*MockPmaxClient)(nil).GetListOfTargetAddresses), arg0, arg1) } // GetLocalOnlineRDFDirs mocks base method. -func (m *MockPmaxClient) GetLocalOnlineRDFDirs(ctx context.Context, localSymID string) (*v100.RDFDirList, error) { +func (m *MockPmaxClient) GetLocalOnlineRDFDirs(arg0 context.Context, arg1 string) (*v100.RDFDirList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLocalOnlineRDFDirs", ctx, localSymID) + ret := m.ctrl.Call(m, "GetLocalOnlineRDFDirs", arg0, arg1) ret0, _ := ret[0].(*v100.RDFDirList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLocalOnlineRDFDirs indicates an expected call of GetLocalOnlineRDFDirs. -func (mr *MockPmaxClientMockRecorder) GetLocalOnlineRDFDirs(ctx, localSymID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetLocalOnlineRDFDirs(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalOnlineRDFDirs", reflect.TypeOf((*MockPmaxClient)(nil).GetLocalOnlineRDFDirs), ctx, localSymID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalOnlineRDFDirs", reflect.TypeOf((*MockPmaxClient)(nil).GetLocalOnlineRDFDirs), arg0, arg1) } // GetLocalOnlineRDFPorts mocks base method. -func (m *MockPmaxClient) GetLocalOnlineRDFPorts(ctx context.Context, rdfDir, localSymID string) (*v100.RDFPortList, error) { +func (m *MockPmaxClient) GetLocalOnlineRDFPorts(arg0 context.Context, arg1, arg2 string) (*v100.RDFPortList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLocalOnlineRDFPorts", ctx, rdfDir, localSymID) + ret := m.ctrl.Call(m, "GetLocalOnlineRDFPorts", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.RDFPortList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLocalOnlineRDFPorts indicates an expected call of GetLocalOnlineRDFPorts. -func (mr *MockPmaxClientMockRecorder) GetLocalOnlineRDFPorts(ctx, rdfDir, localSymID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetLocalOnlineRDFPorts(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalOnlineRDFPorts", reflect.TypeOf((*MockPmaxClient)(nil).GetLocalOnlineRDFPorts), ctx, rdfDir, localSymID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalOnlineRDFPorts", reflect.TypeOf((*MockPmaxClient)(nil).GetLocalOnlineRDFPorts), arg0, arg1, arg2) } // GetLocalRDFPortDetails mocks base method. -func (m *MockPmaxClient) GetLocalRDFPortDetails(ctx context.Context, localSymID, rdfDir string, rdfPort int) (*v100.RDFPortDetails, error) { +func (m *MockPmaxClient) GetLocalRDFPortDetails(arg0 context.Context, arg1, arg2 string, arg3 int) (*v100.RDFPortDetails, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLocalRDFPortDetails", ctx, localSymID, rdfDir, rdfPort) + ret := m.ctrl.Call(m, "GetLocalRDFPortDetails", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.RDFPortDetails) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLocalRDFPortDetails indicates an expected call of GetLocalRDFPortDetails. -func (mr *MockPmaxClientMockRecorder) GetLocalRDFPortDetails(ctx, localSymID, rdfDir, rdfPort any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetLocalRDFPortDetails(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalRDFPortDetails", reflect.TypeOf((*MockPmaxClient)(nil).GetLocalRDFPortDetails), ctx, localSymID, rdfDir, rdfPort) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocalRDFPortDetails", reflect.TypeOf((*MockPmaxClient)(nil).GetLocalRDFPortDetails), arg0, arg1, arg2, arg3) } // GetMaskingViewByID mocks base method. -func (m *MockPmaxClient) GetMaskingViewByID(ctx context.Context, symID, maskingViewID string) (*v100.MaskingView, error) { +func (m *MockPmaxClient) GetMaskingViewByID(arg0 context.Context, arg1, arg2 string) (*v100.MaskingView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMaskingViewByID", ctx, symID, maskingViewID) + ret := m.ctrl.Call(m, "GetMaskingViewByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.MaskingView) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMaskingViewByID indicates an expected call of GetMaskingViewByID. -func (mr *MockPmaxClientMockRecorder) GetMaskingViewByID(ctx, symID, maskingViewID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetMaskingViewByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaskingViewByID", reflect.TypeOf((*MockPmaxClient)(nil).GetMaskingViewByID), ctx, symID, maskingViewID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaskingViewByID", reflect.TypeOf((*MockPmaxClient)(nil).GetMaskingViewByID), arg0, arg1, arg2) } // GetMaskingViewConnections mocks base method. -func (m *MockPmaxClient) GetMaskingViewConnections(ctx context.Context, symID, maskingViewID, volumeID string) ([]*v100.MaskingViewConnection, error) { +func (m *MockPmaxClient) GetMaskingViewConnections(arg0 context.Context, arg1, arg2, arg3 string) ([]*v100.MaskingViewConnection, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMaskingViewConnections", ctx, symID, maskingViewID, volumeID) + ret := m.ctrl.Call(m, "GetMaskingViewConnections", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]*v100.MaskingViewConnection) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMaskingViewConnections indicates an expected call of GetMaskingViewConnections. -func (mr *MockPmaxClientMockRecorder) GetMaskingViewConnections(ctx, symID, maskingViewID, volumeID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetMaskingViewConnections(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaskingViewConnections", reflect.TypeOf((*MockPmaxClient)(nil).GetMaskingViewConnections), ctx, symID, maskingViewID, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaskingViewConnections", reflect.TypeOf((*MockPmaxClient)(nil).GetMaskingViewConnections), arg0, arg1, arg2, arg3) } // GetMaskingViewList mocks base method. -func (m *MockPmaxClient) GetMaskingViewList(ctx context.Context, symID string) (*v100.MaskingViewList, error) { +func (m *MockPmaxClient) GetMaskingViewList(arg0 context.Context, arg1 string) (*v100.MaskingViewList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMaskingViewList", ctx, symID) + ret := m.ctrl.Call(m, "GetMaskingViewList", arg0, arg1) ret0, _ := ret[0].(*v100.MaskingViewList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMaskingViewList indicates an expected call of GetMaskingViewList. -func (mr *MockPmaxClientMockRecorder) GetMaskingViewList(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetMaskingViewList(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaskingViewList", reflect.TypeOf((*MockPmaxClient)(nil).GetMaskingViewList), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMaskingViewList", reflect.TypeOf((*MockPmaxClient)(nil).GetMaskingViewList), arg0, arg1) } // GetMigrationEnvironment mocks base method. -func (m *MockPmaxClient) GetMigrationEnvironment(ctx context.Context, localSymID, remoteSymID string) (*v100.MigrationEnv, error) { +func (m *MockPmaxClient) GetMigrationEnvironment(arg0 context.Context, arg1, arg2 string) (*v100.MigrationEnv, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMigrationEnvironment", ctx, localSymID, remoteSymID) + ret := m.ctrl.Call(m, "GetMigrationEnvironment", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.MigrationEnv) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMigrationEnvironment indicates an expected call of GetMigrationEnvironment. -func (mr *MockPmaxClientMockRecorder) GetMigrationEnvironment(ctx, localSymID, remoteSymID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetMigrationEnvironment(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMigrationEnvironment", reflect.TypeOf((*MockPmaxClient)(nil).GetMigrationEnvironment), ctx, localSymID, remoteSymID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMigrationEnvironment", reflect.TypeOf((*MockPmaxClient)(nil).GetMigrationEnvironment), arg0, arg1, arg2) } // GetNASServerByID mocks base method. -func (m *MockPmaxClient) GetNASServerByID(ctx context.Context, symID, nasID string) (*v100.NASServer, error) { +func (m *MockPmaxClient) GetNASServerByID(arg0 context.Context, arg1, arg2 string) (*v100.NASServer, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNASServerByID", ctx, symID, nasID) + ret := m.ctrl.Call(m, "GetNASServerByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.NASServer) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNASServerByID indicates an expected call of GetNASServerByID. -func (mr *MockPmaxClientMockRecorder) GetNASServerByID(ctx, symID, nasID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetNASServerByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNASServerByID", reflect.TypeOf((*MockPmaxClient)(nil).GetNASServerByID), ctx, symID, nasID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNASServerByID", reflect.TypeOf((*MockPmaxClient)(nil).GetNASServerByID), arg0, arg1, arg2) } // GetNASServerList mocks base method. -func (m *MockPmaxClient) GetNASServerList(ctx context.Context, symID string, query v100.QueryParams) (*v100.NASServerIterator, error) { +func (m *MockPmaxClient) GetNASServerList(arg0 context.Context, arg1 string, arg2 v100.QueryParams) (*v100.NASServerIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNASServerList", ctx, symID, query) + ret := m.ctrl.Call(m, "GetNASServerList", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.NASServerIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNASServerList indicates an expected call of GetNASServerList. -func (mr *MockPmaxClientMockRecorder) GetNASServerList(ctx, symID, query any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetNASServerList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNASServerList", reflect.TypeOf((*MockPmaxClient)(nil).GetNASServerList), ctx, symID, query) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNASServerList", reflect.TypeOf((*MockPmaxClient)(nil).GetNASServerList), arg0, arg1, arg2) } // GetNFSExportByID mocks base method. -func (m *MockPmaxClient) GetNFSExportByID(ctx context.Context, symID, nfsExportID string) (*v100.NFSExport, error) { +func (m *MockPmaxClient) GetNFSExportByID(arg0 context.Context, arg1, arg2 string) (*v100.NFSExport, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNFSExportByID", ctx, symID, nfsExportID) + ret := m.ctrl.Call(m, "GetNFSExportByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.NFSExport) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNFSExportByID indicates an expected call of GetNFSExportByID. -func (mr *MockPmaxClientMockRecorder) GetNFSExportByID(ctx, symID, nfsExportID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetNFSExportByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSExportByID", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSExportByID), ctx, symID, nfsExportID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSExportByID", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSExportByID), arg0, arg1, arg2) } // GetNFSExportList mocks base method. -func (m *MockPmaxClient) GetNFSExportList(ctx context.Context, symID string, query v100.QueryParams) (*v100.NFSExportIterator, error) { +func (m *MockPmaxClient) GetNFSExportList(arg0 context.Context, arg1 string, arg2 v100.QueryParams) (*v100.NFSExportIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNFSExportList", ctx, symID, query) + ret := m.ctrl.Call(m, "GetNFSExportList", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.NFSExportIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNFSExportList indicates an expected call of GetNFSExportList. -func (mr *MockPmaxClientMockRecorder) GetNFSExportList(ctx, symID, query any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetNFSExportList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSExportList", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSExportList), ctx, symID, query) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSExportList", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSExportList), arg0, arg1, arg2) +} + +// GetNFSServerByID mocks base method. +func (m *MockPmaxClient) GetNFSServerByID(arg0 context.Context, arg1, arg2 string) (*v100.NFSServer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNFSServerByID", arg0, arg1, arg2) + ret0, _ := ret[0].(*v100.NFSServer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNFSServerByID indicates an expected call of GetNFSServerByID. +func (mr *MockPmaxClientMockRecorder) GetNFSServerByID(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSServerByID", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSServerByID), arg0, arg1, arg2) +} + +// GetNFSServerList mocks base method. +func (m *MockPmaxClient) GetNFSServerList(arg0 context.Context, arg1 string) (*v100.NFSServerIterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNFSServerList", arg0, arg1) + ret0, _ := ret[0].(*v100.NFSServerIterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNFSServerList indicates an expected call of GetNFSServerList. +func (mr *MockPmaxClientMockRecorder) GetNFSServerList(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSServerList", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSServerList), arg0, arg1) } // GetNVMeTCPTargets mocks base method. -func (m *MockPmaxClient) GetNVMeTCPTargets(ctx context.Context, symID string) ([]pmax.NVMeTCPTarget, error) { +func (m *MockPmaxClient) GetNVMeTCPTargets(arg0 context.Context, arg1 string) ([]pmax.NVMeTCPTarget, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNVMeTCPTargets", ctx, symID) + ret := m.ctrl.Call(m, "GetNVMeTCPTargets", arg0, arg1) ret0, _ := ret[0].([]pmax.NVMeTCPTarget) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNVMeTCPTargets indicates an expected call of GetNVMeTCPTargets. -func (mr *MockPmaxClientMockRecorder) GetNVMeTCPTargets(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetNVMeTCPTargets(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNVMeTCPTargets", reflect.TypeOf((*MockPmaxClient)(nil).GetNVMeTCPTargets), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNVMeTCPTargets", reflect.TypeOf((*MockPmaxClient)(nil).GetNVMeTCPTargets), arg0, arg1) } // GetPort mocks base method. -func (m *MockPmaxClient) GetPort(ctx context.Context, symID, directorID, portID string) (*v100.Port, error) { +func (m *MockPmaxClient) GetPort(arg0 context.Context, arg1, arg2, arg3 string) (*v100.Port, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPort", ctx, symID, directorID, portID) + ret := m.ctrl.Call(m, "GetPort", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Port) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPort indicates an expected call of GetPort. -func (mr *MockPmaxClientMockRecorder) GetPort(ctx, symID, directorID, portID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetPort(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPort", reflect.TypeOf((*MockPmaxClient)(nil).GetPort), ctx, symID, directorID, portID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPort", reflect.TypeOf((*MockPmaxClient)(nil).GetPort), arg0, arg1, arg2, arg3) } // GetPortGroupByID mocks base method. -func (m *MockPmaxClient) GetPortGroupByID(ctx context.Context, symID, portGroupID string) (*v100.PortGroup, error) { +func (m *MockPmaxClient) GetPortGroupByID(arg0 context.Context, arg1, arg2 string) (*v100.PortGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPortGroupByID", ctx, symID, portGroupID) + ret := m.ctrl.Call(m, "GetPortGroupByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.PortGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPortGroupByID indicates an expected call of GetPortGroupByID. -func (mr *MockPmaxClientMockRecorder) GetPortGroupByID(ctx, symID, portGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetPortGroupByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortGroupByID", reflect.TypeOf((*MockPmaxClient)(nil).GetPortGroupByID), ctx, symID, portGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortGroupByID", reflect.TypeOf((*MockPmaxClient)(nil).GetPortGroupByID), arg0, arg1, arg2) } // GetPortGroupList mocks base method. -func (m *MockPmaxClient) GetPortGroupList(ctx context.Context, symID, portGroupType string) (*v100.PortGroupList, error) { +func (m *MockPmaxClient) GetPortGroupList(arg0 context.Context, arg1, arg2 string) (*v100.PortGroupList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPortGroupList", ctx, symID, portGroupType) + ret := m.ctrl.Call(m, "GetPortGroupList", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.PortGroupList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPortGroupList indicates an expected call of GetPortGroupList. -func (mr *MockPmaxClientMockRecorder) GetPortGroupList(ctx, symID, portGroupType any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetPortGroupList(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortGroupList", reflect.TypeOf((*MockPmaxClient)(nil).GetPortGroupList), arg0, arg1, arg2) +} + +// GetPortGroupListByType mocks base method. +func (m *MockPmaxClient) GetPortGroupListByType(arg0 context.Context, arg1, arg2 string) (*v100.PortGroupListResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPortGroupListByType", arg0, arg1, arg2) + ret0, _ := ret[0].(*v100.PortGroupListResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPortGroupListByType indicates an expected call of GetPortGroupListByType. +func (mr *MockPmaxClientMockRecorder) GetPortGroupListByType(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortGroupList", reflect.TypeOf((*MockPmaxClient)(nil).GetPortGroupList), ctx, symID, portGroupType) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortGroupListByType", reflect.TypeOf((*MockPmaxClient)(nil).GetPortGroupListByType), arg0, arg1, arg2) } // GetPortList mocks base method. -func (m *MockPmaxClient) GetPortList(ctx context.Context, symID, directorID, query string) (*v100.PortList, error) { +func (m *MockPmaxClient) GetPortList(arg0 context.Context, arg1, arg2, arg3 string) (*v100.PortList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPortList", ctx, symID, directorID, query) + ret := m.ctrl.Call(m, "GetPortList", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.PortList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPortList indicates an expected call of GetPortList. -func (mr *MockPmaxClientMockRecorder) GetPortList(ctx, symID, directorID, query any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetPortList(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortList", reflect.TypeOf((*MockPmaxClient)(nil).GetPortList), ctx, symID, directorID, query) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortList", reflect.TypeOf((*MockPmaxClient)(nil).GetPortList), arg0, arg1, arg2, arg3) } // GetPortListByProtocol mocks base method. -func (m *MockPmaxClient) GetPortListByProtocol(ctx context.Context, symID, protocol string) (*v100.PortList, error) { +func (m *MockPmaxClient) GetPortListByProtocol(arg0 context.Context, arg1, arg2 string) (*v100.PortList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPortListByProtocol", ctx, symID, protocol) + ret := m.ctrl.Call(m, "GetPortListByProtocol", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.PortList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPortListByProtocol indicates an expected call of GetPortListByProtocol. -func (mr *MockPmaxClientMockRecorder) GetPortListByProtocol(ctx, symID, protocol any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetPortListByProtocol(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortListByProtocol", reflect.TypeOf((*MockPmaxClient)(nil).GetPortListByProtocol), arg0, arg1, arg2) +} + +// GetPorts mocks base method. +func (m *MockPmaxClient) GetPorts(arg0 context.Context, arg1 string) (*v100.PortV1, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPorts", arg0, arg1) + ret0, _ := ret[0].(*v100.PortV1) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPorts indicates an expected call of GetPorts. +func (mr *MockPmaxClientMockRecorder) GetPorts(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPortListByProtocol", reflect.TypeOf((*MockPmaxClient)(nil).GetPortListByProtocol), ctx, symID, protocol) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPorts", reflect.TypeOf((*MockPmaxClient)(nil).GetPorts), arg0, arg1) } // GetPrivVolumeByID mocks base method. -func (m *MockPmaxClient) GetPrivVolumeByID(ctx context.Context, symID, volumeID string) (*v100.VolumeResultPrivate, error) { +func (m *MockPmaxClient) GetPrivVolumeByID(arg0 context.Context, arg1, arg2 string) (*v100.VolumeResultPrivate, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPrivVolumeByID", ctx, symID, volumeID) + ret := m.ctrl.Call(m, "GetPrivVolumeByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.VolumeResultPrivate) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPrivVolumeByID indicates an expected call of GetPrivVolumeByID. -func (mr *MockPmaxClientMockRecorder) GetPrivVolumeByID(ctx, symID, volumeID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetPrivVolumeByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrivVolumeByID", reflect.TypeOf((*MockPmaxClient)(nil).GetPrivVolumeByID), ctx, symID, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrivVolumeByID", reflect.TypeOf((*MockPmaxClient)(nil).GetPrivVolumeByID), arg0, arg1, arg2) } // GetProtectedStorageGroup mocks base method. -func (m *MockPmaxClient) GetProtectedStorageGroup(ctx context.Context, symID, storageGroup string) (*v100.RDFStorageGroup, error) { +func (m *MockPmaxClient) GetProtectedStorageGroup(arg0 context.Context, arg1, arg2 string) (*v100.RDFStorageGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetProtectedStorageGroup", ctx, symID, storageGroup) + ret := m.ctrl.Call(m, "GetProtectedStorageGroup", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.RDFStorageGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // GetProtectedStorageGroup indicates an expected call of GetProtectedStorageGroup. -func (mr *MockPmaxClientMockRecorder) GetProtectedStorageGroup(ctx, symID, storageGroup any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetProtectedStorageGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProtectedStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).GetProtectedStorageGroup), ctx, symID, storageGroup) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProtectedStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).GetProtectedStorageGroup), arg0, arg1, arg2) } // GetRDFDevicePairInfo mocks base method. -func (m *MockPmaxClient) GetRDFDevicePairInfo(ctx context.Context, symID, rdfGroup, volumeID string) (*v100.RDFDevicePair, error) { +func (m *MockPmaxClient) GetRDFDevicePairInfo(arg0 context.Context, arg1, arg2, arg3 string) (*v100.RDFDevicePair, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRDFDevicePairInfo", ctx, symID, rdfGroup, volumeID) + ret := m.ctrl.Call(m, "GetRDFDevicePairInfo", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.RDFDevicePair) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRDFDevicePairInfo indicates an expected call of GetRDFDevicePairInfo. -func (mr *MockPmaxClientMockRecorder) GetRDFDevicePairInfo(ctx, symID, rdfGroup, volumeID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetRDFDevicePairInfo(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRDFDevicePairInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetRDFDevicePairInfo), ctx, symID, rdfGroup, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRDFDevicePairInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetRDFDevicePairInfo), arg0, arg1, arg2, arg3) } // GetRDFGroupByID mocks base method. -func (m *MockPmaxClient) GetRDFGroupByID(ctx context.Context, symID, rdfGroup string) (*v100.RDFGroup, error) { +func (m *MockPmaxClient) GetRDFGroupByID(arg0 context.Context, arg1, arg2 string) (*v100.RDFGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRDFGroupByID", ctx, symID, rdfGroup) + ret := m.ctrl.Call(m, "GetRDFGroupByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.RDFGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRDFGroupByID indicates an expected call of GetRDFGroupByID. -func (mr *MockPmaxClientMockRecorder) GetRDFGroupByID(ctx, symID, rdfGroup any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetRDFGroupByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRDFGroupByID", reflect.TypeOf((*MockPmaxClient)(nil).GetRDFGroupByID), ctx, symID, rdfGroup) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRDFGroupByID", reflect.TypeOf((*MockPmaxClient)(nil).GetRDFGroupByID), arg0, arg1, arg2) } // GetRDFGroupList mocks base method. -func (m *MockPmaxClient) GetRDFGroupList(ctx context.Context, symID string, queryParams v100.QueryParams) (*v100.RDFGroupList, error) { +func (m *MockPmaxClient) GetRDFGroupList(arg0 context.Context, arg1 string, arg2 v100.QueryParams) (*v100.RDFGroupList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRDFGroupList", ctx, symID, queryParams) + ret := m.ctrl.Call(m, "GetRDFGroupList", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.RDFGroupList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRDFGroupList indicates an expected call of GetRDFGroupList. -func (mr *MockPmaxClientMockRecorder) GetRDFGroupList(ctx, symID, queryParams any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetRDFGroupList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRDFGroupList", reflect.TypeOf((*MockPmaxClient)(nil).GetRDFGroupList), ctx, symID, queryParams) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRDFGroupList", reflect.TypeOf((*MockPmaxClient)(nil).GetRDFGroupList), arg0, arg1, arg2) } // GetRemoteRDFPortOnSAN mocks base method. -func (m *MockPmaxClient) GetRemoteRDFPortOnSAN(ctx context.Context, localSymID, rdfDir, rdfPort string) (*v100.RemoteRDFPortDetails, error) { +func (m *MockPmaxClient) GetRemoteRDFPortOnSAN(arg0 context.Context, arg1, arg2, arg3 string) (*v100.RemoteRDFPortDetails, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRemoteRDFPortOnSAN", ctx, localSymID, rdfDir, rdfPort) + ret := m.ctrl.Call(m, "GetRemoteRDFPortOnSAN", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.RemoteRDFPortDetails) ret1, _ := ret[1].(error) return ret0, ret1 } // GetRemoteRDFPortOnSAN indicates an expected call of GetRemoteRDFPortOnSAN. -func (mr *MockPmaxClientMockRecorder) GetRemoteRDFPortOnSAN(ctx, localSymID, rdfDir, rdfPort any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetRemoteRDFPortOnSAN(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteRDFPortOnSAN", reflect.TypeOf((*MockPmaxClient)(nil).GetRemoteRDFPortOnSAN), ctx, localSymID, rdfDir, rdfPort) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteRDFPortOnSAN", reflect.TypeOf((*MockPmaxClient)(nil).GetRemoteRDFPortOnSAN), arg0, arg1, arg2, arg3) } // GetReplicationCapabilities mocks base method. -func (m *MockPmaxClient) GetReplicationCapabilities(ctx context.Context) (*v100.SymReplicationCapabilities, error) { +func (m *MockPmaxClient) GetReplicationCapabilities(arg0 context.Context) (*v100.SymReplicationCapabilities, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetReplicationCapabilities", ctx) + ret := m.ctrl.Call(m, "GetReplicationCapabilities", arg0) ret0, _ := ret[0].(*v100.SymReplicationCapabilities) ret1, _ := ret[1].(error) return ret0, ret1 } // GetReplicationCapabilities indicates an expected call of GetReplicationCapabilities. -func (mr *MockPmaxClientMockRecorder) GetReplicationCapabilities(ctx any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetReplicationCapabilities(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationCapabilities", reflect.TypeOf((*MockPmaxClient)(nil).GetReplicationCapabilities), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReplicationCapabilities", reflect.TypeOf((*MockPmaxClient)(nil).GetReplicationCapabilities), arg0) } // GetSnapVolumeList mocks base method. -func (m *MockPmaxClient) GetSnapVolumeList(ctx context.Context, symID string, queryParams v100.QueryParams) (*v100.SymVolumeList, error) { +func (m *MockPmaxClient) GetSnapVolumeList(arg0 context.Context, arg1 string, arg2 v100.QueryParams) (*v100.SymVolumeList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapVolumeList", ctx, symID, queryParams) + ret := m.ctrl.Call(m, "GetSnapVolumeList", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.SymVolumeList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSnapVolumeList indicates an expected call of GetSnapVolumeList. -func (mr *MockPmaxClientMockRecorder) GetSnapVolumeList(ctx, symID, queryParams any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSnapVolumeList(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapVolumeList", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapVolumeList), ctx, symID, queryParams) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapVolumeList", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapVolumeList), arg0, arg1, arg2) } // GetSnapshotGenerationInfo mocks base method. -func (m *MockPmaxClient) GetSnapshotGenerationInfo(ctx context.Context, symID, volume, SnapID string, generation int64) (*v100.VolumeSnapshotGeneration, error) { +func (m *MockPmaxClient) GetSnapshotGenerationInfo(arg0 context.Context, arg1, arg2, arg3 string, arg4 int64) (*v100.VolumeSnapshotGeneration, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapshotGenerationInfo", ctx, symID, volume, SnapID, generation) + ret := m.ctrl.Call(m, "GetSnapshotGenerationInfo", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.VolumeSnapshotGeneration) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSnapshotGenerationInfo indicates an expected call of GetSnapshotGenerationInfo. -func (mr *MockPmaxClientMockRecorder) GetSnapshotGenerationInfo(ctx, symID, volume, SnapID, generation any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSnapshotGenerationInfo(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotGenerationInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotGenerationInfo), ctx, symID, volume, SnapID, generation) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotGenerationInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotGenerationInfo), arg0, arg1, arg2, arg3, arg4) } // GetSnapshotGenerations mocks base method. -func (m *MockPmaxClient) GetSnapshotGenerations(ctx context.Context, symID, volume, SnapID string) (*v100.VolumeSnapshotGenerations, error) { +func (m *MockPmaxClient) GetSnapshotGenerations(arg0 context.Context, arg1, arg2, arg3 string) (*v100.VolumeSnapshotGenerations, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapshotGenerations", ctx, symID, volume, SnapID) + ret := m.ctrl.Call(m, "GetSnapshotGenerations", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.VolumeSnapshotGenerations) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSnapshotGenerations indicates an expected call of GetSnapshotGenerations. -func (mr *MockPmaxClientMockRecorder) GetSnapshotGenerations(ctx, symID, volume, SnapID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSnapshotGenerations(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotGenerations", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotGenerations), ctx, symID, volume, SnapID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotGenerations", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotGenerations), arg0, arg1, arg2, arg3) } // GetSnapshotInfo mocks base method. -func (m *MockPmaxClient) GetSnapshotInfo(ctx context.Context, symID, volume, SnapID string) (*v100.VolumeSnapshot, error) { +func (m *MockPmaxClient) GetSnapshotInfo(arg0 context.Context, arg1, arg2, arg3 string) (*v100.VolumeSnapshot, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapshotInfo", ctx, symID, volume, SnapID) + ret := m.ctrl.Call(m, "GetSnapshotInfo", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.VolumeSnapshot) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSnapshotInfo indicates an expected call of GetSnapshotInfo. -func (mr *MockPmaxClientMockRecorder) GetSnapshotInfo(ctx, symID, volume, SnapID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSnapshotInfo(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotInfo), ctx, symID, volume, SnapID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotInfo), arg0, arg1, arg2, arg3) } // GetSnapshotPolicy mocks base method. -func (m *MockPmaxClient) GetSnapshotPolicy(ctx context.Context, symID, snapshotPolicyID string) (*v100.SnapshotPolicy, error) { +func (m *MockPmaxClient) GetSnapshotPolicy(arg0 context.Context, arg1, arg2 string) (*v100.SnapshotPolicy, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapshotPolicy", ctx, symID, snapshotPolicyID) + ret := m.ctrl.Call(m, "GetSnapshotPolicy", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.SnapshotPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSnapshotPolicy indicates an expected call of GetSnapshotPolicy. -func (mr *MockPmaxClientMockRecorder) GetSnapshotPolicy(ctx, symID, snapshotPolicyID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSnapshotPolicy(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotPolicy), ctx, symID, snapshotPolicyID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotPolicy), arg0, arg1, arg2) } // GetSnapshotPolicyList mocks base method. -func (m *MockPmaxClient) GetSnapshotPolicyList(ctx context.Context, symID string) (*v100.SnapshotPolicyList, error) { +func (m *MockPmaxClient) GetSnapshotPolicyList(arg0 context.Context, arg1 string) (*v100.SnapshotPolicyList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapshotPolicyList", ctx, symID) + ret := m.ctrl.Call(m, "GetSnapshotPolicyList", arg0, arg1) ret0, _ := ret[0].(*v100.SnapshotPolicyList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSnapshotPolicyList indicates an expected call of GetSnapshotPolicyList. -func (mr *MockPmaxClientMockRecorder) GetSnapshotPolicyList(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSnapshotPolicyList(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotPolicyList", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotPolicyList), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotPolicyList", reflect.TypeOf((*MockPmaxClient)(nil).GetSnapshotPolicyList), arg0, arg1) } // GetStorageGroup mocks base method. -func (m *MockPmaxClient) GetStorageGroup(ctx context.Context, symID, storageGroupID string) (*v100.StorageGroup, error) { +func (m *MockPmaxClient) GetStorageGroup(arg0 context.Context, arg1, arg2 string) (*v100.StorageGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroup", ctx, symID, storageGroupID) + ret := m.ctrl.Call(m, "GetStorageGroup", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.StorageGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroup indicates an expected call of GetStorageGroup. -func (mr *MockPmaxClientMockRecorder) GetStorageGroup(ctx, symID, storageGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroup), ctx, symID, storageGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroup), arg0, arg1, arg2) } // GetStorageGroupIDList mocks base method. -func (m *MockPmaxClient) GetStorageGroupIDList(ctx context.Context, symID, storageGroupIDMatch string, like bool) (*v100.StorageGroupIDList, error) { +func (m *MockPmaxClient) GetStorageGroupIDList(arg0 context.Context, arg1, arg2 string, arg3 bool) (*v100.StorageGroupIDList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupIDList", ctx, symID, storageGroupIDMatch, like) + ret := m.ctrl.Call(m, "GetStorageGroupIDList", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.StorageGroupIDList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupIDList indicates an expected call of GetStorageGroupIDList. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupIDList(ctx, symID, storageGroupIDMatch, like any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupIDList(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupIDList), ctx, symID, storageGroupIDMatch, like) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupIDList), arg0, arg1, arg2, arg3) } // GetStorageGroupMetrics mocks base method. -func (m *MockPmaxClient) GetStorageGroupMetrics(ctx context.Context, symID, storageGroupID string, metricsQuery []string, firstAvailableDate, lastAvailableTime int64) (*v100.StorageGroupMetricsIterator, error) { +func (m *MockPmaxClient) GetStorageGroupMetrics(arg0 context.Context, arg1, arg2 string, arg3 []string, arg4, arg5 int64) (*v100.StorageGroupMetricsIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupMetrics", ctx, symID, storageGroupID, metricsQuery, firstAvailableDate, lastAvailableTime) + ret := m.ctrl.Call(m, "GetStorageGroupMetrics", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.StorageGroupMetricsIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupMetrics indicates an expected call of GetStorageGroupMetrics. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupMetrics(ctx, symID, storageGroupID, metricsQuery, firstAvailableDate, lastAvailableTime any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupMetrics(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupMetrics", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupMetrics), ctx, symID, storageGroupID, metricsQuery, firstAvailableDate, lastAvailableTime) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupMetrics", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupMetrics), arg0, arg1, arg2, arg3, arg4, arg5) } // GetStorageGroupMigration mocks base method. -func (m *MockPmaxClient) GetStorageGroupMigration(ctx context.Context, localSymID string) (*v100.MigrationStorageGroups, error) { +func (m *MockPmaxClient) GetStorageGroupMigration(arg0 context.Context, arg1 string) (*v100.MigrationStorageGroups, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupMigration", ctx, localSymID) + ret := m.ctrl.Call(m, "GetStorageGroupMigration", arg0, arg1) ret0, _ := ret[0].(*v100.MigrationStorageGroups) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupMigration indicates an expected call of GetStorageGroupMigration. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupMigration(ctx, localSymID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupMigration(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupMigration", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupMigration), ctx, localSymID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupMigration", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupMigration), arg0, arg1) } // GetStorageGroupMigrationByID mocks base method. -func (m *MockPmaxClient) GetStorageGroupMigrationByID(ctx context.Context, localSymID, storageGroupID string) (*v100.MigrationSession, error) { +func (m *MockPmaxClient) GetStorageGroupMigrationByID(arg0 context.Context, arg1, arg2 string) (*v100.MigrationSession, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupMigrationByID", ctx, localSymID, storageGroupID) + ret := m.ctrl.Call(m, "GetStorageGroupMigrationByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.MigrationSession) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupMigrationByID indicates an expected call of GetStorageGroupMigrationByID. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupMigrationByID(ctx, localSymID, storageGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupMigrationByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupMigrationByID", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupMigrationByID), ctx, localSymID, storageGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupMigrationByID", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupMigrationByID), arg0, arg1, arg2) } // GetStorageGroupPerfKeys mocks base method. -func (m *MockPmaxClient) GetStorageGroupPerfKeys(ctx context.Context, symID string) (*v100.StorageGroupKeysResult, error) { +func (m *MockPmaxClient) GetStorageGroupPerfKeys(arg0 context.Context, arg1 string) (*v100.StorageGroupKeysResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupPerfKeys", ctx, symID) + ret := m.ctrl.Call(m, "GetStorageGroupPerfKeys", arg0, arg1) ret0, _ := ret[0].(*v100.StorageGroupKeysResult) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupPerfKeys indicates an expected call of GetStorageGroupPerfKeys. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupPerfKeys(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupPerfKeys(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupPerfKeys", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupPerfKeys), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupPerfKeys", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupPerfKeys), arg0, arg1) } // GetStorageGroupRDFInfo mocks base method. -func (m *MockPmaxClient) GetStorageGroupRDFInfo(ctx context.Context, symID, sgName, rdfGroupNo string) (*v100.StorageGroupRDFG, error) { +func (m *MockPmaxClient) GetStorageGroupRDFInfo(arg0 context.Context, arg1, arg2, arg3 string) (*v100.StorageGroupRDFG, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupRDFInfo", ctx, symID, sgName, rdfGroupNo) + ret := m.ctrl.Call(m, "GetStorageGroupRDFInfo", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.StorageGroupRDFG) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupRDFInfo indicates an expected call of GetStorageGroupRDFInfo. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupRDFInfo(ctx, symID, sgName, rdfGroupNo any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupRDFInfo(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupRDFInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupRDFInfo), ctx, symID, sgName, rdfGroupNo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupRDFInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupRDFInfo), arg0, arg1, arg2, arg3) } // GetStorageGroupSnapshotPolicy mocks base method. -func (m *MockPmaxClient) GetStorageGroupSnapshotPolicy(ctx context.Context, symID, snapshotPolicyID, storageGroupID string) (*v100.StorageGroupSnapshotPolicy, error) { +func (m *MockPmaxClient) GetStorageGroupSnapshotPolicy(arg0 context.Context, arg1, arg2, arg3 string) (*v100.StorageGroupSnapshotPolicy, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupSnapshotPolicy", ctx, symID, snapshotPolicyID, storageGroupID) + ret := m.ctrl.Call(m, "GetStorageGroupSnapshotPolicy", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.StorageGroupSnapshotPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupSnapshotPolicy indicates an expected call of GetStorageGroupSnapshotPolicy. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshotPolicy(ctx, symID, snapshotPolicyID, storageGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshotPolicy(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshotPolicy), ctx, symID, snapshotPolicyID, storageGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshotPolicy), arg0, arg1, arg2, arg3) } // GetStorageGroupSnapshotSnap mocks base method. -func (m *MockPmaxClient) GetStorageGroupSnapshotSnap(ctx context.Context, symID, storageGroupID, snapshotID, snapID string) (*v100.StorageGroupSnap, error) { +func (m *MockPmaxClient) GetStorageGroupSnapshotSnap(arg0 context.Context, arg1, arg2, arg3, arg4 string) (*v100.StorageGroupSnap, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupSnapshotSnap", ctx, symID, storageGroupID, snapshotID, snapID) + ret := m.ctrl.Call(m, "GetStorageGroupSnapshotSnap", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.StorageGroupSnap) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupSnapshotSnap indicates an expected call of GetStorageGroupSnapshotSnap. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshotSnap(ctx, symID, storageGroupID, snapshotID, snapID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshotSnap(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshotSnap", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshotSnap), ctx, symID, storageGroupID, snapshotID, snapID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshotSnap", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshotSnap), arg0, arg1, arg2, arg3, arg4) } // GetStorageGroupSnapshotSnapIDs mocks base method. -func (m *MockPmaxClient) GetStorageGroupSnapshotSnapIDs(ctx context.Context, symID, storageGroupID, snapshotID string) (*v100.SnapID, error) { +func (m *MockPmaxClient) GetStorageGroupSnapshotSnapIDs(arg0 context.Context, arg1, arg2, arg3 string) (*v100.SnapID, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupSnapshotSnapIDs", ctx, symID, storageGroupID, snapshotID) + ret := m.ctrl.Call(m, "GetStorageGroupSnapshotSnapIDs", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.SnapID) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupSnapshotSnapIDs indicates an expected call of GetStorageGroupSnapshotSnapIDs. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshotSnapIDs(ctx, symID, storageGroupID, snapshotID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshotSnapIDs(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshotSnapIDs", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshotSnapIDs), ctx, symID, storageGroupID, snapshotID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshotSnapIDs", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshotSnapIDs), arg0, arg1, arg2, arg3) } // GetStorageGroupSnapshots mocks base method. -func (m *MockPmaxClient) GetStorageGroupSnapshots(ctx context.Context, symID, storageGroupID string, excludeManualSnaps, excludeSlSnaps bool) (*v100.StorageGroupSnapshot, error) { +func (m *MockPmaxClient) GetStorageGroupSnapshots(arg0 context.Context, arg1, arg2 string, arg3, arg4 bool) (*v100.StorageGroupSnapshot, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStorageGroupSnapshots", ctx, symID, storageGroupID, excludeManualSnaps, excludeSlSnaps) + ret := m.ctrl.Call(m, "GetStorageGroupSnapshots", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*v100.StorageGroupSnapshot) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStorageGroupSnapshots indicates an expected call of GetStorageGroupSnapshots. -func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshots(ctx, symID, storageGroupID, excludeManualSnaps, excludeSlSnaps any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStorageGroupSnapshots(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshots", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshots), ctx, symID, storageGroupID, excludeManualSnaps, excludeSlSnaps) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupSnapshots", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupSnapshots), arg0, arg1, arg2, arg3, arg4) +} + +// GetStorageGroupVolumeCounts mocks base method. +func (m *MockPmaxClient) GetStorageGroupVolumeCounts(arg0 context.Context, arg1, arg2 string) (*v100.StorageGroupVolumeCounts, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetStorageGroupVolumeCounts", arg0, arg1, arg2) + ret0, _ := ret[0].(*v100.StorageGroupVolumeCounts) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetStorageGroupVolumeCounts indicates an expected call of GetStorageGroupVolumeCounts. +func (mr *MockPmaxClientMockRecorder) GetStorageGroupVolumeCounts(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageGroupVolumeCounts", reflect.TypeOf((*MockPmaxClient)(nil).GetStorageGroupVolumeCounts), arg0, arg1, arg2) } // GetStoragePool mocks base method. -func (m *MockPmaxClient) GetStoragePool(ctx context.Context, symID, storagePoolID string) (*v100.StoragePool, error) { +func (m *MockPmaxClient) GetStoragePool(arg0 context.Context, arg1, arg2 string) (*v100.StoragePool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStoragePool", ctx, symID, storagePoolID) + ret := m.ctrl.Call(m, "GetStoragePool", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.StoragePool) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStoragePool indicates an expected call of GetStoragePool. -func (mr *MockPmaxClientMockRecorder) GetStoragePool(ctx, symID, storagePoolID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStoragePool(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoragePool", reflect.TypeOf((*MockPmaxClient)(nil).GetStoragePool), ctx, symID, storagePoolID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoragePool", reflect.TypeOf((*MockPmaxClient)(nil).GetStoragePool), arg0, arg1, arg2) } // GetStoragePoolList mocks base method. -func (m *MockPmaxClient) GetStoragePoolList(ctx context.Context, symID string) (*v100.StoragePoolList, error) { +func (m *MockPmaxClient) GetStoragePoolList(arg0 context.Context, arg1 string) (*v100.StoragePoolList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStoragePoolList", ctx, symID) + ret := m.ctrl.Call(m, "GetStoragePoolList", arg0, arg1) ret0, _ := ret[0].(*v100.StoragePoolList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetStoragePoolList indicates an expected call of GetStoragePoolList. -func (mr *MockPmaxClientMockRecorder) GetStoragePoolList(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetStoragePoolList(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoragePoolList", reflect.TypeOf((*MockPmaxClient)(nil).GetStoragePoolList), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoragePoolList", reflect.TypeOf((*MockPmaxClient)(nil).GetStoragePoolList), arg0, arg1) } // GetSymmetrixByID mocks base method. -func (m *MockPmaxClient) GetSymmetrixByID(ctx context.Context, id string) (*v100.Symmetrix, error) { +func (m *MockPmaxClient) GetSymmetrixByID(arg0 context.Context, arg1 string) (*v100.Symmetrix, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSymmetrixByID", ctx, id) + ret := m.ctrl.Call(m, "GetSymmetrixByID", arg0, arg1) ret0, _ := ret[0].(*v100.Symmetrix) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSymmetrixByID indicates an expected call of GetSymmetrixByID. -func (mr *MockPmaxClientMockRecorder) GetSymmetrixByID(ctx, id any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSymmetrixByID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSymmetrixByID", reflect.TypeOf((*MockPmaxClient)(nil).GetSymmetrixByID), ctx, id) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSymmetrixByID", reflect.TypeOf((*MockPmaxClient)(nil).GetSymmetrixByID), arg0, arg1) } // GetSymmetrixIDList mocks base method. -func (m *MockPmaxClient) GetSymmetrixIDList(ctx context.Context) (*v100.SymmetrixIDList, error) { +func (m *MockPmaxClient) GetSymmetrixIDList(arg0 context.Context) (*v100.SymmetrixIDList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSymmetrixIDList", ctx) + ret := m.ctrl.Call(m, "GetSymmetrixIDList", arg0) ret0, _ := ret[0].(*v100.SymmetrixIDList) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSymmetrixIDList indicates an expected call of GetSymmetrixIDList. -func (mr *MockPmaxClientMockRecorder) GetSymmetrixIDList(ctx any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetSymmetrixIDList(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSymmetrixIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetSymmetrixIDList), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSymmetrixIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetSymmetrixIDList), arg0) +} + +// GetVersionDetails mocks base method. +func (m *MockPmaxClient) GetVersionDetails(arg0 context.Context) (*v100.VersionDetails, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVersionDetails", arg0) + ret0, _ := ret[0].(*v100.VersionDetails) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVersionDetails indicates an expected call of GetVersionDetails. +func (mr *MockPmaxClientMockRecorder) GetVersionDetails(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersionDetails", reflect.TypeOf((*MockPmaxClient)(nil).GetVersionDetails), arg0) } // GetVolumeByID mocks base method. -func (m *MockPmaxClient) GetVolumeByID(ctx context.Context, symID, volumeID string) (*v100.Volume, error) { +func (m *MockPmaxClient) GetVolumeByID(arg0 context.Context, arg1, arg2 string) (*v100.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeByID", ctx, symID, volumeID) + ret := m.ctrl.Call(m, "GetVolumeByID", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeByID indicates an expected call of GetVolumeByID. -func (mr *MockPmaxClientMockRecorder) GetVolumeByID(ctx, symID, volumeID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeByID(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByID", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeByID), ctx, symID, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByID", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeByID), arg0, arg1, arg2) } // GetVolumeIDList mocks base method. -func (m *MockPmaxClient) GetVolumeIDList(ctx context.Context, symID, volumeIdentifierMatch string, like bool) ([]string, error) { +func (m *MockPmaxClient) GetVolumeIDList(arg0 context.Context, arg1, arg2 string, arg3 bool) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeIDList", ctx, symID, volumeIdentifierMatch, like) + ret := m.ctrl.Call(m, "GetVolumeIDList", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeIDList indicates an expected call of GetVolumeIDList. -func (mr *MockPmaxClientMockRecorder) GetVolumeIDList(ctx, symID, volumeIdentifierMatch, like any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeIDList(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDList), ctx, symID, volumeIdentifierMatch, like) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDList", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDList), arg0, arg1, arg2, arg3) } // GetVolumeIDListInStorageGroup mocks base method. -func (m *MockPmaxClient) GetVolumeIDListInStorageGroup(ctx context.Context, symID, storageGroupID string) ([]string, error) { +func (m *MockPmaxClient) GetVolumeIDListInStorageGroup(arg0 context.Context, arg1, arg2 string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeIDListInStorageGroup", ctx, symID, storageGroupID) + ret := m.ctrl.Call(m, "GetVolumeIDListInStorageGroup", arg0, arg1, arg2) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeIDListInStorageGroup indicates an expected call of GetVolumeIDListInStorageGroup. -func (mr *MockPmaxClientMockRecorder) GetVolumeIDListInStorageGroup(ctx, symID, storageGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeIDListInStorageGroup(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDListInStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDListInStorageGroup), ctx, symID, storageGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDListInStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDListInStorageGroup), arg0, arg1, arg2) } // GetVolumeIDListWithParams mocks base method. -func (m *MockPmaxClient) GetVolumeIDListWithParams(ctx context.Context, symID string, queryParams map[string]string) ([]string, error) { +func (m *MockPmaxClient) GetVolumeIDListWithParams(arg0 context.Context, arg1 string, arg2 map[string]string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeIDListWithParams", ctx, symID, queryParams) + ret := m.ctrl.Call(m, "GetVolumeIDListWithParams", arg0, arg1, arg2) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeIDListWithParams indicates an expected call of GetVolumeIDListWithParams. -func (mr *MockPmaxClientMockRecorder) GetVolumeIDListWithParams(ctx, symID, queryParams any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeIDListWithParams(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDListWithParams", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDListWithParams), ctx, symID, queryParams) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDListWithParams", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDListWithParams), arg0, arg1, arg2) } // GetVolumeIDsIterator mocks base method. -func (m *MockPmaxClient) GetVolumeIDsIterator(ctx context.Context, symID, volumeIdentifierMatch string, like bool) (*v100.VolumeIterator, error) { +func (m *MockPmaxClient) GetVolumeIDsIterator(arg0 context.Context, arg1, arg2 string, arg3 bool) (*v100.VolumeIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeIDsIterator", ctx, symID, volumeIdentifierMatch, like) + ret := m.ctrl.Call(m, "GetVolumeIDsIterator", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.VolumeIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeIDsIterator indicates an expected call of GetVolumeIDsIterator. -func (mr *MockPmaxClientMockRecorder) GetVolumeIDsIterator(ctx, symID, volumeIdentifierMatch, like any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeIDsIterator(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDsIterator", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDsIterator), ctx, symID, volumeIdentifierMatch, like) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDsIterator", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDsIterator), arg0, arg1, arg2, arg3) } // GetVolumeIDsIteratorPage mocks base method. -func (m *MockPmaxClient) GetVolumeIDsIteratorPage(ctx context.Context, iter *v100.VolumeIterator, from, to int) ([]string, error) { +func (m *MockPmaxClient) GetVolumeIDsIteratorPage(arg0 context.Context, arg1 *v100.VolumeIterator, arg2, arg3 int) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeIDsIteratorPage", ctx, iter, from, to) + ret := m.ctrl.Call(m, "GetVolumeIDsIteratorPage", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeIDsIteratorPage indicates an expected call of GetVolumeIDsIteratorPage. -func (mr *MockPmaxClientMockRecorder) GetVolumeIDsIteratorPage(ctx, iter, from, to any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeIDsIteratorPage(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDsIteratorPage", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDsIteratorPage), ctx, iter, from, to) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDsIteratorPage", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDsIteratorPage), arg0, arg1, arg2, arg3) } // GetVolumeIDsIteratorWithParams mocks base method. -func (m *MockPmaxClient) GetVolumeIDsIteratorWithParams(ctx context.Context, symID string, queryParams map[string]string) (*v100.VolumeIterator, error) { +func (m *MockPmaxClient) GetVolumeIDsIteratorWithParams(arg0 context.Context, arg1 string, arg2 map[string]string) (*v100.VolumeIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeIDsIteratorWithParams", ctx, symID, queryParams) + ret := m.ctrl.Call(m, "GetVolumeIDsIteratorWithParams", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.VolumeIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeIDsIteratorWithParams indicates an expected call of GetVolumeIDsIteratorWithParams. -func (mr *MockPmaxClientMockRecorder) GetVolumeIDsIteratorWithParams(ctx, symID, queryParams any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeIDsIteratorWithParams(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDsIteratorWithParams", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDsIteratorWithParams), ctx, symID, queryParams) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeIDsIteratorWithParams", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeIDsIteratorWithParams), arg0, arg1, arg2) } // GetVolumeSnapInfo mocks base method. -func (m *MockPmaxClient) GetVolumeSnapInfo(ctx context.Context, symID, volume string) (*v100.SnapshotVolumeGeneration, error) { +func (m *MockPmaxClient) GetVolumeSnapInfo(arg0 context.Context, arg1, arg2 string) (*v100.SnapshotVolumeGeneration, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeSnapInfo", ctx, symID, volume) + ret := m.ctrl.Call(m, "GetVolumeSnapInfo", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.SnapshotVolumeGeneration) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumeSnapInfo indicates an expected call of GetVolumeSnapInfo. -func (mr *MockPmaxClientMockRecorder) GetVolumeSnapInfo(ctx, symID, volume any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumeSnapInfo(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeSnapInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeSnapInfo), ctx, symID, volume) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeSnapInfo", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumeSnapInfo), arg0, arg1, arg2) +} + +// GetVolumesByIdentifier mocks base method. +func (m *MockPmaxClient) GetVolumesByIdentifier(arg0 context.Context, arg1, arg2 string) (*v100.Volumev1, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVolumesByIdentifier", arg0, arg1, arg2) + ret0, _ := ret[0].(*v100.Volumev1) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVolumesByIdentifier indicates an expected call of GetVolumesByIdentifier. +func (mr *MockPmaxClientMockRecorder) GetVolumesByIdentifier(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesByIdentifier", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesByIdentifier), arg0, arg1, arg2) } // GetVolumesInStorageGroupIterator mocks base method. -func (m *MockPmaxClient) GetVolumesInStorageGroupIterator(ctx context.Context, symID, storageGroupID string) (*v100.VolumeIterator, error) { +func (m *MockPmaxClient) GetVolumesInStorageGroupIterator(arg0 context.Context, arg1, arg2 string) (*v100.VolumeIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumesInStorageGroupIterator", ctx, symID, storageGroupID) + ret := m.ctrl.Call(m, "GetVolumesInStorageGroupIterator", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.VolumeIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumesInStorageGroupIterator indicates an expected call of GetVolumesInStorageGroupIterator. -func (mr *MockPmaxClientMockRecorder) GetVolumesInStorageGroupIterator(ctx, symID, storageGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumesInStorageGroupIterator(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesInStorageGroupIterator", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesInStorageGroupIterator), ctx, symID, storageGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesInStorageGroupIterator", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesInStorageGroupIterator), arg0, arg1, arg2) } // GetVolumesMetrics mocks base method. -func (m *MockPmaxClient) GetVolumesMetrics(ctx context.Context, symID, storageGroups string, metricsQuery []string, firstAvailableDate, lastAvailableTime int64) (*v100.VolumeMetricsIterator, error) { +func (m *MockPmaxClient) GetVolumesMetrics(arg0 context.Context, arg1, arg2 string, arg3 []string, arg4, arg5 int64) (*v100.VolumeMetricsIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumesMetrics", ctx, symID, storageGroups, metricsQuery, firstAvailableDate, lastAvailableTime) + ret := m.ctrl.Call(m, "GetVolumesMetrics", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.VolumeMetricsIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumesMetrics indicates an expected call of GetVolumesMetrics. -func (mr *MockPmaxClientMockRecorder) GetVolumesMetrics(ctx, symID, storageGroups, metricsQuery, firstAvailableDate, lastAvailableTime any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumesMetrics(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesMetrics", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesMetrics), ctx, symID, storageGroups, metricsQuery, firstAvailableDate, lastAvailableTime) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesMetrics", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesMetrics), arg0, arg1, arg2, arg3, arg4, arg5) } // GetVolumesMetricsByID mocks base method. -func (m *MockPmaxClient) GetVolumesMetricsByID(ctx context.Context, symID, volID string, metricsQuery []string, firstAvailableTime, lastAvailableTime int64) (*v100.VolumeMetricsIterator, error) { +func (m *MockPmaxClient) GetVolumesMetricsByID(arg0 context.Context, arg1, arg2 string, arg3 []string, arg4, arg5 int64) (*v100.VolumeMetricsIterator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumesMetricsByID", ctx, symID, volID, metricsQuery, firstAvailableTime, lastAvailableTime) + ret := m.ctrl.Call(m, "GetVolumesMetricsByID", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.VolumeMetricsIterator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetVolumesMetricsByID indicates an expected call of GetVolumesMetricsByID. -func (mr *MockPmaxClientMockRecorder) GetVolumesMetricsByID(ctx, symID, volID, metricsQuery, firstAvailableTime, lastAvailableTime any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) GetVolumesMetricsByID(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesMetricsByID", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesMetricsByID), ctx, symID, volID, metricsQuery, firstAvailableTime, lastAvailableTime) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesMetricsByID", reflect.TypeOf((*MockPmaxClient)(nil).GetVolumesMetricsByID), arg0, arg1, arg2, arg3, arg4, arg5) } // InitiateDeallocationOfTracksFromVolume mocks base method. -func (m *MockPmaxClient) InitiateDeallocationOfTracksFromVolume(ctx context.Context, symID, volumeID string) (*v100.Job, error) { +func (m *MockPmaxClient) InitiateDeallocationOfTracksFromVolume(arg0 context.Context, arg1, arg2 string) (*v100.Job, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InitiateDeallocationOfTracksFromVolume", ctx, symID, volumeID) + ret := m.ctrl.Call(m, "InitiateDeallocationOfTracksFromVolume", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.Job) ret1, _ := ret[1].(error) return ret0, ret1 } // InitiateDeallocationOfTracksFromVolume indicates an expected call of InitiateDeallocationOfTracksFromVolume. -func (mr *MockPmaxClientMockRecorder) InitiateDeallocationOfTracksFromVolume(ctx, symID, volumeID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) InitiateDeallocationOfTracksFromVolume(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiateDeallocationOfTracksFromVolume", reflect.TypeOf((*MockPmaxClient)(nil).InitiateDeallocationOfTracksFromVolume), ctx, symID, volumeID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitiateDeallocationOfTracksFromVolume", reflect.TypeOf((*MockPmaxClient)(nil).InitiateDeallocationOfTracksFromVolume), arg0, arg1, arg2) } // IsAllowedArray mocks base method. -func (m *MockPmaxClient) IsAllowedArray(array string) (bool, error) { +func (m *MockPmaxClient) IsAllowedArray(arg0 string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsAllowedArray", array) + ret := m.ctrl.Call(m, "IsAllowedArray", arg0) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsAllowedArray indicates an expected call of IsAllowedArray. -func (mr *MockPmaxClientMockRecorder) IsAllowedArray(array any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) IsAllowedArray(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAllowedArray", reflect.TypeOf((*MockPmaxClient)(nil).IsAllowedArray), array) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAllowedArray", reflect.TypeOf((*MockPmaxClient)(nil).IsAllowedArray), arg0) } // JobToString mocks base method. -func (m *MockPmaxClient) JobToString(job *v100.Job) string { +func (m *MockPmaxClient) JobToString(arg0 *v100.Job) string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "JobToString", job) + ret := m.ctrl.Call(m, "JobToString", arg0) ret0, _ := ret[0].(string) return ret0 } // JobToString indicates an expected call of JobToString. -func (mr *MockPmaxClientMockRecorder) JobToString(job any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) JobToString(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JobToString", reflect.TypeOf((*MockPmaxClient)(nil).JobToString), job) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JobToString", reflect.TypeOf((*MockPmaxClient)(nil).JobToString), arg0) } // MigrateStorageGroup mocks base method. -func (m *MockPmaxClient) MigrateStorageGroup(ctx context.Context, symID, storageGroupID, srpID, serviceLevel string, thickVolumes bool) (*v100.StorageGroup, error) { +func (m *MockPmaxClient) MigrateStorageGroup(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 bool) (*v100.StorageGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MigrateStorageGroup", ctx, symID, storageGroupID, srpID, serviceLevel, thickVolumes) + ret := m.ctrl.Call(m, "MigrateStorageGroup", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.StorageGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // MigrateStorageGroup indicates an expected call of MigrateStorageGroup. -func (mr *MockPmaxClientMockRecorder) MigrateStorageGroup(ctx, symID, storageGroupID, srpID, serviceLevel, thickVolumes any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) MigrateStorageGroup(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MigrateStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).MigrateStorageGroup), ctx, symID, storageGroupID, srpID, serviceLevel, thickVolumes) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MigrateStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).MigrateStorageGroup), arg0, arg1, arg2, arg3, arg4, arg5) } // ModifyFileSystem mocks base method. -func (m *MockPmaxClient) ModifyFileSystem(ctx context.Context, symID, fsID string, payload v100.ModifyFileSystem) (*v100.FileSystem, error) { +func (m *MockPmaxClient) ModifyFileSystem(arg0 context.Context, arg1, arg2 string, arg3 v100.ModifyFileSystem) (*v100.FileSystem, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyFileSystem", ctx, symID, fsID, payload) + ret := m.ctrl.Call(m, "ModifyFileSystem", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.FileSystem) ret1, _ := ret[1].(error) return ret0, ret1 } // ModifyFileSystem indicates an expected call of ModifyFileSystem. -func (mr *MockPmaxClientMockRecorder) ModifyFileSystem(ctx, symID, fsID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifyFileSystem(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyFileSystem", reflect.TypeOf((*MockPmaxClient)(nil).ModifyFileSystem), ctx, symID, fsID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyFileSystem", reflect.TypeOf((*MockPmaxClient)(nil).ModifyFileSystem), arg0, arg1, arg2, arg3) } // ModifyMigrationSession mocks base method. -func (m *MockPmaxClient) ModifyMigrationSession(ctx context.Context, localSymID, action, storageGroup string) error { +func (m *MockPmaxClient) ModifyMigrationSession(arg0 context.Context, arg1, arg2, arg3 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyMigrationSession", ctx, localSymID, action, storageGroup) + ret := m.ctrl.Call(m, "ModifyMigrationSession", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // ModifyMigrationSession indicates an expected call of ModifyMigrationSession. -func (mr *MockPmaxClientMockRecorder) ModifyMigrationSession(ctx, localSymID, action, storageGroup any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifyMigrationSession(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyMigrationSession", reflect.TypeOf((*MockPmaxClient)(nil).ModifyMigrationSession), ctx, localSymID, action, storageGroup) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyMigrationSession", reflect.TypeOf((*MockPmaxClient)(nil).ModifyMigrationSession), arg0, arg1, arg2, arg3) } // ModifyMobilityForVolume mocks base method. -func (m *MockPmaxClient) ModifyMobilityForVolume(ctx context.Context, symID, volumeID string, mobility bool) (*v100.Volume, error) { +func (m *MockPmaxClient) ModifyMobilityForVolume(arg0 context.Context, arg1, arg2 string, arg3 bool) (*v100.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyMobilityForVolume", ctx, symID, volumeID, mobility) + ret := m.ctrl.Call(m, "ModifyMobilityForVolume", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // ModifyMobilityForVolume indicates an expected call of ModifyMobilityForVolume. -func (mr *MockPmaxClientMockRecorder) ModifyMobilityForVolume(ctx, symID, volumeID, mobility any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifyMobilityForVolume(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyMobilityForVolume", reflect.TypeOf((*MockPmaxClient)(nil).ModifyMobilityForVolume), ctx, symID, volumeID, mobility) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyMobilityForVolume", reflect.TypeOf((*MockPmaxClient)(nil).ModifyMobilityForVolume), arg0, arg1, arg2, arg3) } // ModifyNASServer mocks base method. -func (m *MockPmaxClient) ModifyNASServer(ctx context.Context, symID, nasID string, payload v100.ModifyNASServer) (*v100.NASServer, error) { +func (m *MockPmaxClient) ModifyNASServer(arg0 context.Context, arg1, arg2 string, arg3 v100.ModifyNASServer) (*v100.NASServer, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyNASServer", ctx, symID, nasID, payload) + ret := m.ctrl.Call(m, "ModifyNASServer", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.NASServer) ret1, _ := ret[1].(error) return ret0, ret1 } // ModifyNASServer indicates an expected call of ModifyNASServer. -func (mr *MockPmaxClientMockRecorder) ModifyNASServer(ctx, symID, nasID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifyNASServer(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNASServer", reflect.TypeOf((*MockPmaxClient)(nil).ModifyNASServer), ctx, symID, nasID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNASServer", reflect.TypeOf((*MockPmaxClient)(nil).ModifyNASServer), arg0, arg1, arg2, arg3) } // ModifyNFSExport mocks base method. -func (m *MockPmaxClient) ModifyNFSExport(ctx context.Context, symID, nfsExportID string, payload v100.ModifyNFSExport) (*v100.NFSExport, error) { +func (m *MockPmaxClient) ModifyNFSExport(arg0 context.Context, arg1, arg2 string, arg3 v100.ModifyNFSExport) (*v100.NFSExport, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyNFSExport", ctx, symID, nfsExportID, payload) + ret := m.ctrl.Call(m, "ModifyNFSExport", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.NFSExport) ret1, _ := ret[1].(error) return ret0, ret1 } // ModifyNFSExport indicates an expected call of ModifyNFSExport. -func (mr *MockPmaxClientMockRecorder) ModifyNFSExport(ctx, symID, nfsExportID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifyNFSExport(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNFSExport", reflect.TypeOf((*MockPmaxClient)(nil).ModifyNFSExport), ctx, symID, nfsExportID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNFSExport", reflect.TypeOf((*MockPmaxClient)(nil).ModifyNFSExport), arg0, arg1, arg2, arg3) } // ModifySnapshot mocks base method. -func (m *MockPmaxClient) ModifySnapshot(ctx context.Context, symID string, sourceVol, targetVol []v100.VolumeList, SnapID, action, newSnapID string, generation int64, isCopy bool) error { +func (m *MockPmaxClient) ModifySnapshot(arg0 context.Context, arg1 string, arg2, arg3 []v100.VolumeList, arg4, arg5, arg6 string, arg7 int64, arg8 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifySnapshot", ctx, symID, sourceVol, targetVol, SnapID, action, newSnapID, generation, isCopy) + ret := m.ctrl.Call(m, "ModifySnapshot", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) ret0, _ := ret[0].(error) return ret0 } // ModifySnapshot indicates an expected call of ModifySnapshot. -func (mr *MockPmaxClientMockRecorder) ModifySnapshot(ctx, symID, sourceVol, targetVol, SnapID, action, newSnapID, generation, isCopy any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifySnapshot(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifySnapshot", reflect.TypeOf((*MockPmaxClient)(nil).ModifySnapshot), ctx, symID, sourceVol, targetVol, SnapID, action, newSnapID, generation, isCopy) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifySnapshot", reflect.TypeOf((*MockPmaxClient)(nil).ModifySnapshot), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } // ModifySnapshotS mocks base method. -func (m *MockPmaxClient) ModifySnapshotS(ctx context.Context, symID string, sourceVol, targetVol []v100.VolumeList, SnapID, action, newSnapID string, generation int64, isCopy bool) error { +func (m *MockPmaxClient) ModifySnapshotS(arg0 context.Context, arg1 string, arg2, arg3 []v100.VolumeList, arg4, arg5, arg6 string, arg7 int64, arg8 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifySnapshotS", ctx, symID, sourceVol, targetVol, SnapID, action, newSnapID, generation, isCopy) + ret := m.ctrl.Call(m, "ModifySnapshotS", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) ret0, _ := ret[0].(error) return ret0 } // ModifySnapshotS indicates an expected call of ModifySnapshotS. -func (mr *MockPmaxClientMockRecorder) ModifySnapshotS(ctx, symID, sourceVol, targetVol, SnapID, action, newSnapID, generation, isCopy any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifySnapshotS(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifySnapshotS", reflect.TypeOf((*MockPmaxClient)(nil).ModifySnapshotS), ctx, symID, sourceVol, targetVol, SnapID, action, newSnapID, generation, isCopy) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifySnapshotS", reflect.TypeOf((*MockPmaxClient)(nil).ModifySnapshotS), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } // ModifyStorageGroupSnapshot mocks base method. -func (m *MockPmaxClient) ModifyStorageGroupSnapshot(ctx context.Context, symID, storageGroupID, snapshotID, snapID string, payload *v100.ModifyStorageGroupSnapshot) (*v100.StorageGroupSnap, error) { +func (m *MockPmaxClient) ModifyStorageGroupSnapshot(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 *v100.ModifyStorageGroupSnapshot) (*v100.StorageGroupSnap, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyStorageGroupSnapshot", ctx, symID, storageGroupID, snapshotID, snapID, payload) + ret := m.ctrl.Call(m, "ModifyStorageGroupSnapshot", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(*v100.StorageGroupSnap) ret1, _ := ret[1].(error) return ret0, ret1 } // ModifyStorageGroupSnapshot indicates an expected call of ModifyStorageGroupSnapshot. -func (mr *MockPmaxClientMockRecorder) ModifyStorageGroupSnapshot(ctx, symID, storageGroupID, snapshotID, snapID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) ModifyStorageGroupSnapshot(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyStorageGroupSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).ModifyStorageGroupSnapshot), ctx, symID, storageGroupID, snapshotID, snapID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyStorageGroupSnapshot", reflect.TypeOf((*MockPmaxClient)(nil).ModifyStorageGroupSnapshot), arg0, arg1, arg2, arg3, arg4, arg5) } // RefreshSymmetrix mocks base method. -func (m *MockPmaxClient) RefreshSymmetrix(ctx context.Context, symID string) error { +func (m *MockPmaxClient) RefreshSymmetrix(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RefreshSymmetrix", ctx, symID) + ret := m.ctrl.Call(m, "RefreshSymmetrix", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RefreshSymmetrix indicates an expected call of RefreshSymmetrix. -func (mr *MockPmaxClientMockRecorder) RefreshSymmetrix(ctx, symID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) RefreshSymmetrix(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSymmetrix", reflect.TypeOf((*MockPmaxClient)(nil).RefreshSymmetrix), ctx, symID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSymmetrix", reflect.TypeOf((*MockPmaxClient)(nil).RefreshSymmetrix), arg0, arg1) } // RemoveVolumesFromProtectedStorageGroup mocks base method. -func (m *MockPmaxClient) RemoveVolumesFromProtectedStorageGroup(ctx context.Context, symID, storageGroupID, remoteSymID, remoteStorageGroupID string, force bool, volumeIDs ...string) (*v100.StorageGroup, error) { +func (m *MockPmaxClient) RemoveVolumesFromProtectedStorageGroup(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 bool, arg6 ...string) (*v100.StorageGroup, error) { m.ctrl.T.Helper() - varargs := []any{ctx, symID, storageGroupID, remoteSymID, remoteStorageGroupID, force} - for _, a := range volumeIDs { + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4, arg5} + for _, a := range arg6 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RemoveVolumesFromProtectedStorageGroup", varargs...) @@ -1982,17 +2095,17 @@ func (m *MockPmaxClient) RemoveVolumesFromProtectedStorageGroup(ctx context.Cont } // RemoveVolumesFromProtectedStorageGroup indicates an expected call of RemoveVolumesFromProtectedStorageGroup. -func (mr *MockPmaxClientMockRecorder) RemoveVolumesFromProtectedStorageGroup(ctx, symID, storageGroupID, remoteSymID, remoteStorageGroupID, force any, volumeIDs ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) RemoveVolumesFromProtectedStorageGroup(arg0, arg1, arg2, arg3, arg4, arg5 interface{}, arg6 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, storageGroupID, remoteSymID, remoteStorageGroupID, force}, volumeIDs...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4, arg5}, arg6...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVolumesFromProtectedStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).RemoveVolumesFromProtectedStorageGroup), varargs...) } // RemoveVolumesFromStorageGroup mocks base method. -func (m *MockPmaxClient) RemoveVolumesFromStorageGroup(ctx context.Context, symID, storageGroupID string, force bool, volumeIDs ...string) (*v100.StorageGroup, error) { +func (m *MockPmaxClient) RemoveVolumesFromStorageGroup(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 ...string) (*v100.StorageGroup, error) { m.ctrl.T.Helper() - varargs := []any{ctx, symID, storageGroupID, force} - for _, a := range volumeIDs { + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RemoveVolumesFromStorageGroup", varargs...) @@ -2002,273 +2115,244 @@ func (m *MockPmaxClient) RemoveVolumesFromStorageGroup(ctx context.Context, symI } // RemoveVolumesFromStorageGroup indicates an expected call of RemoveVolumesFromStorageGroup. -func (mr *MockPmaxClientMockRecorder) RemoveVolumesFromStorageGroup(ctx, symID, storageGroupID, force any, volumeIDs ...any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) RemoveVolumesFromStorageGroup(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, symID, storageGroupID, force}, volumeIDs...) + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVolumesFromStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).RemoveVolumesFromStorageGroup), varargs...) } // RenameMaskingView mocks base method. -func (m *MockPmaxClient) RenameMaskingView(ctx context.Context, symID, maskingViewID, newName string) (*v100.MaskingView, error) { +func (m *MockPmaxClient) RenameMaskingView(arg0 context.Context, arg1, arg2, arg3 string) (*v100.MaskingView, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RenameMaskingView", ctx, symID, maskingViewID, newName) + ret := m.ctrl.Call(m, "RenameMaskingView", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.MaskingView) ret1, _ := ret[1].(error) return ret0, ret1 } // RenameMaskingView indicates an expected call of RenameMaskingView. -func (mr *MockPmaxClientMockRecorder) RenameMaskingView(ctx, symID, maskingViewID, newName any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) RenameMaskingView(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameMaskingView", reflect.TypeOf((*MockPmaxClient)(nil).RenameMaskingView), ctx, symID, maskingViewID, newName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameMaskingView", reflect.TypeOf((*MockPmaxClient)(nil).RenameMaskingView), arg0, arg1, arg2, arg3) } // RenamePortGroup mocks base method. -func (m *MockPmaxClient) RenamePortGroup(ctx context.Context, symID, portGroupID, newName string) (*v100.PortGroup, error) { +func (m *MockPmaxClient) RenamePortGroup(arg0 context.Context, arg1, arg2, arg3 string) (*v100.PortGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RenamePortGroup", ctx, symID, portGroupID, newName) + ret := m.ctrl.Call(m, "RenamePortGroup", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.PortGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // RenamePortGroup indicates an expected call of RenamePortGroup. -func (mr *MockPmaxClientMockRecorder) RenamePortGroup(ctx, symID, portGroupID, newName any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) RenamePortGroup(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenamePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).RenamePortGroup), ctx, symID, portGroupID, newName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenamePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).RenamePortGroup), arg0, arg1, arg2, arg3) } // RenameVolume mocks base method. -func (m *MockPmaxClient) RenameVolume(ctx context.Context, symID, volumeID, newName string) (*v100.Volume, error) { +func (m *MockPmaxClient) RenameVolume(arg0 context.Context, arg1, arg2, arg3 string) (*v100.Volume, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RenameVolume", ctx, symID, volumeID, newName) + ret := m.ctrl.Call(m, "RenameVolume", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Volume) ret1, _ := ret[1].(error) return ret0, ret1 } // RenameVolume indicates an expected call of RenameVolume. -func (mr *MockPmaxClientMockRecorder) RenameVolume(ctx, symID, volumeID, newName any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) RenameVolume(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameVolume", reflect.TypeOf((*MockPmaxClient)(nil).RenameVolume), ctx, symID, volumeID, newName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameVolume", reflect.TypeOf((*MockPmaxClient)(nil).RenameVolume), arg0, arg1, arg2, arg3) } // SetAllowedArrays mocks base method. -func (m *MockPmaxClient) SetAllowedArrays(arrays []string) error { +func (m *MockPmaxClient) SetAllowedArrays(arg0 []string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetAllowedArrays", arrays) + ret := m.ctrl.Call(m, "SetAllowedArrays", arg0) ret0, _ := ret[0].(error) return ret0 } // SetAllowedArrays indicates an expected call of SetAllowedArrays. -func (mr *MockPmaxClientMockRecorder) SetAllowedArrays(arrays any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) SetAllowedArrays(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAllowedArrays", reflect.TypeOf((*MockPmaxClient)(nil).SetAllowedArrays), arrays) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAllowedArrays", reflect.TypeOf((*MockPmaxClient)(nil).SetAllowedArrays), arg0) } // UpdateHostFlags mocks base method. -func (m *MockPmaxClient) UpdateHostFlags(ctx context.Context, symID, hostID string, hostFlags *v100.HostFlags) (*v100.Host, error) { +func (m *MockPmaxClient) UpdateHostFlags(arg0 context.Context, arg1, arg2 string, arg3 *v100.HostFlags) (*v100.Host, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHostFlags", ctx, symID, hostID, hostFlags) + ret := m.ctrl.Call(m, "UpdateHostFlags", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Host) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateHostFlags indicates an expected call of UpdateHostFlags. -func (mr *MockPmaxClientMockRecorder) UpdateHostFlags(ctx, symID, hostID, hostFlags any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateHostFlags(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostFlags", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostFlags), ctx, symID, hostID, hostFlags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostFlags", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostFlags), arg0, arg1, arg2, arg3) } // UpdateHostGroupFlags mocks base method. -func (m *MockPmaxClient) UpdateHostGroupFlags(ctx context.Context, symID, hostGroupID string, hostFlags *v100.HostFlags) (*v100.HostGroup, error) { +func (m *MockPmaxClient) UpdateHostGroupFlags(arg0 context.Context, arg1, arg2 string, arg3 *v100.HostFlags) (*v100.HostGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHostGroupFlags", ctx, symID, hostGroupID, hostFlags) + ret := m.ctrl.Call(m, "UpdateHostGroupFlags", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.HostGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateHostGroupFlags indicates an expected call of UpdateHostGroupFlags. -func (mr *MockPmaxClientMockRecorder) UpdateHostGroupFlags(ctx, symID, hostGroupID, hostFlags any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateHostGroupFlags(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostGroupFlags", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostGroupFlags), ctx, symID, hostGroupID, hostFlags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostGroupFlags", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostGroupFlags), arg0, arg1, arg2, arg3) } // UpdateHostGroupHosts mocks base method. -func (m *MockPmaxClient) UpdateHostGroupHosts(ctx context.Context, symID, hostGroupID string, hostIDs []string) (*v100.HostGroup, error) { +func (m *MockPmaxClient) UpdateHostGroupHosts(arg0 context.Context, arg1, arg2 string, arg3 []string) (*v100.HostGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHostGroupHosts", ctx, symID, hostGroupID, hostIDs) + ret := m.ctrl.Call(m, "UpdateHostGroupHosts", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.HostGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateHostGroupHosts indicates an expected call of UpdateHostGroupHosts. -func (mr *MockPmaxClientMockRecorder) UpdateHostGroupHosts(ctx, symID, hostGroupID, hostIDs any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateHostGroupHosts(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostGroupHosts", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostGroupHosts), ctx, symID, hostGroupID, hostIDs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostGroupHosts", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostGroupHosts), arg0, arg1, arg2, arg3) } // UpdateHostGroupName mocks base method. -func (m *MockPmaxClient) UpdateHostGroupName(ctx context.Context, symID, oldHostGroupID, newHostGroupID string) (*v100.HostGroup, error) { +func (m *MockPmaxClient) UpdateHostGroupName(arg0 context.Context, arg1, arg2, arg3 string) (*v100.HostGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHostGroupName", ctx, symID, oldHostGroupID, newHostGroupID) + ret := m.ctrl.Call(m, "UpdateHostGroupName", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.HostGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateHostGroupName indicates an expected call of UpdateHostGroupName. -func (mr *MockPmaxClientMockRecorder) UpdateHostGroupName(ctx, symID, oldHostGroupID, newHostGroupID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateHostGroupName(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostGroupName", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostGroupName), ctx, symID, oldHostGroupID, newHostGroupID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostGroupName", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostGroupName), arg0, arg1, arg2, arg3) } // UpdateHostInitiators mocks base method. -func (m *MockPmaxClient) UpdateHostInitiators(ctx context.Context, symID string, host *v100.Host, initiatorIDs []string) (*v100.Host, error) { +func (m *MockPmaxClient) UpdateHostInitiators(arg0 context.Context, arg1 string, arg2 *v100.Host, arg3 []string) (*v100.Host, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHostInitiators", ctx, symID, host, initiatorIDs) + ret := m.ctrl.Call(m, "UpdateHostInitiators", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Host) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateHostInitiators indicates an expected call of UpdateHostInitiators. -func (mr *MockPmaxClientMockRecorder) UpdateHostInitiators(ctx, symID, host, initiatorIDs any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateHostInitiators(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostInitiators", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostInitiators), ctx, symID, host, initiatorIDs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostInitiators", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostInitiators), arg0, arg1, arg2, arg3) } // UpdateHostName mocks base method. -func (m *MockPmaxClient) UpdateHostName(ctx context.Context, symID, oldHostID, newHostID string) (*v100.Host, error) { +func (m *MockPmaxClient) UpdateHostName(arg0 context.Context, arg1, arg2, arg3 string) (*v100.Host, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHostName", ctx, symID, oldHostID, newHostID) + ret := m.ctrl.Call(m, "UpdateHostName", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Host) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateHostName indicates an expected call of UpdateHostName. -func (mr *MockPmaxClientMockRecorder) UpdateHostName(ctx, symID, oldHostID, newHostID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateHostName(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostName", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostName), ctx, symID, oldHostID, newHostID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHostName", reflect.TypeOf((*MockPmaxClient)(nil).UpdateHostName), arg0, arg1, arg2, arg3) } // UpdatePortGroup mocks base method. -func (m *MockPmaxClient) UpdatePortGroup(ctx context.Context, symID, portGroupID string, ports []v100.PortKey) (*v100.PortGroup, error) { +func (m *MockPmaxClient) UpdatePortGroup(arg0 context.Context, arg1, arg2 string, arg3 []v100.PortKey) (*v100.PortGroup, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdatePortGroup", ctx, symID, portGroupID, ports) + ret := m.ctrl.Call(m, "UpdatePortGroup", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.PortGroup) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdatePortGroup indicates an expected call of UpdatePortGroup. -func (mr *MockPmaxClientMockRecorder) UpdatePortGroup(ctx, symID, portGroupID, ports any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdatePortGroup(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).UpdatePortGroup), ctx, symID, portGroupID, ports) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePortGroup", reflect.TypeOf((*MockPmaxClient)(nil).UpdatePortGroup), arg0, arg1, arg2, arg3) } // UpdateSnapshotPolicy mocks base method. -func (m *MockPmaxClient) UpdateSnapshotPolicy(ctx context.Context, symID, action, snapshotPolicyID string, optionalPayload map[string]any) error { +func (m *MockPmaxClient) UpdateSnapshotPolicy(arg0 context.Context, arg1, arg2, arg3 string, arg4 map[string]interface{}) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateSnapshotPolicy", ctx, symID, action, snapshotPolicyID, optionalPayload) + ret := m.ctrl.Call(m, "UpdateSnapshotPolicy", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // UpdateSnapshotPolicy indicates an expected call of UpdateSnapshotPolicy. -func (mr *MockPmaxClientMockRecorder) UpdateSnapshotPolicy(ctx, symID, action, snapshotPolicyID, optionalPayload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateSnapshotPolicy(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).UpdateSnapshotPolicy), ctx, symID, action, snapshotPolicyID, optionalPayload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSnapshotPolicy", reflect.TypeOf((*MockPmaxClient)(nil).UpdateSnapshotPolicy), arg0, arg1, arg2, arg3, arg4) } // UpdateStorageGroup mocks base method. -func (m *MockPmaxClient) UpdateStorageGroup(ctx context.Context, symID, storageGroupID string, payload any) (*v100.Job, error) { +func (m *MockPmaxClient) UpdateStorageGroup(arg0 context.Context, arg1, arg2 string, arg3 interface{}) (*v100.Job, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateStorageGroup", ctx, symID, storageGroupID, payload) + ret := m.ctrl.Call(m, "UpdateStorageGroup", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*v100.Job) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateStorageGroup indicates an expected call of UpdateStorageGroup. -func (mr *MockPmaxClientMockRecorder) UpdateStorageGroup(ctx, symID, storageGroupID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateStorageGroup(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).UpdateStorageGroup), ctx, symID, storageGroupID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStorageGroup", reflect.TypeOf((*MockPmaxClient)(nil).UpdateStorageGroup), arg0, arg1, arg2, arg3) } // UpdateStorageGroupS mocks base method. -func (m *MockPmaxClient) UpdateStorageGroupS(ctx context.Context, symID, storageGroupID string, payload any) error { +func (m *MockPmaxClient) UpdateStorageGroupS(arg0 context.Context, arg1, arg2 string, arg3 interface{}) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateStorageGroupS", ctx, symID, storageGroupID, payload) + ret := m.ctrl.Call(m, "UpdateStorageGroupS", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // UpdateStorageGroupS indicates an expected call of UpdateStorageGroupS. -func (mr *MockPmaxClientMockRecorder) UpdateStorageGroupS(ctx, symID, storageGroupID, payload any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) UpdateStorageGroupS(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStorageGroupS", reflect.TypeOf((*MockPmaxClient)(nil).UpdateStorageGroupS), ctx, symID, storageGroupID, payload) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStorageGroupS", reflect.TypeOf((*MockPmaxClient)(nil).UpdateStorageGroupS), arg0, arg1, arg2, arg3) } // WaitOnJobCompletion mocks base method. -func (m *MockPmaxClient) WaitOnJobCompletion(ctx context.Context, symID, jobID string) (*v100.Job, error) { +func (m *MockPmaxClient) WaitOnJobCompletion(arg0 context.Context, arg1, arg2 string) (*v100.Job, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitOnJobCompletion", ctx, symID, jobID) + ret := m.ctrl.Call(m, "WaitOnJobCompletion", arg0, arg1, arg2) ret0, _ := ret[0].(*v100.Job) ret1, _ := ret[1].(error) return ret0, ret1 } // WaitOnJobCompletion indicates an expected call of WaitOnJobCompletion. -func (mr *MockPmaxClientMockRecorder) WaitOnJobCompletion(ctx, symID, jobID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) WaitOnJobCompletion(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitOnJobCompletion", reflect.TypeOf((*MockPmaxClient)(nil).WaitOnJobCompletion), ctx, symID, jobID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitOnJobCompletion", reflect.TypeOf((*MockPmaxClient)(nil).WaitOnJobCompletion), arg0, arg1, arg2) } // WithSymmetrixID mocks base method. -func (m *MockPmaxClient) WithSymmetrixID(symmetrixID string) pmax.Pmax { +func (m *MockPmaxClient) WithSymmetrixID(arg0 string) pmax.Pmax { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WithSymmetrixID", symmetrixID) + ret := m.ctrl.Call(m, "WithSymmetrixID", arg0) ret0, _ := ret[0].(pmax.Pmax) return ret0 } // WithSymmetrixID indicates an expected call of WithSymmetrixID. -func (mr *MockPmaxClientMockRecorder) WithSymmetrixID(symmetrixID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithSymmetrixID", reflect.TypeOf((*MockPmaxClient)(nil).WithSymmetrixID), symmetrixID) -} - -// GetNFSServerList mocks base method. -func (m *MockPmaxClient) GetNFSServerList(ctx context.Context, symID string) (*v100.NFSServerIterator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNFSServerList", ctx, symID) - ret0, _ := ret[0].(*v100.NFSServerIterator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetNFSServerList indicates an expected call of GetNFSServerList. -func (mr *MockPmaxClientMockRecorder) GetNFSServerList(ctx, symID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSServerList", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSServerList), ctx, symID) -} - -func (m *MockPmaxClient) GetNFSServerByID(ctx context.Context, symID, nfsID string) (*v100.NFSServer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNFSServerByID", ctx, symID, nfsID) - ret0, _ := ret[0].(*v100.NFSServer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetNFSServerByID indicates an expected call of GetNFSServerByID. -func (mr *MockPmaxClientMockRecorder) GetNFSServerByID(ctx, symID, nfsID any) *gomock.Call { +func (mr *MockPmaxClientMockRecorder) WithSymmetrixID(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNFSServerByID", reflect.TypeOf((*MockPmaxClient)(nil).GetNFSServerByID), ctx, symID, nfsID) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithSymmetrixID", reflect.TypeOf((*MockPmaxClient)(nil).WithSymmetrixID), arg0) } diff --git a/pkg/symmetrix/powermax_test.go b/pkg/symmetrix/powermax_test.go index c74e4305..16d59d0f 100644 --- a/pkg/symmetrix/powermax_test.go +++ b/pkg/symmetrix/powermax_test.go @@ -24,7 +24,7 @@ import ( "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - "go.uber.org/mock/gomock" + "github.com/golang/mock/gomock" ) func TestGetPowerMaxClient(t *testing.T) { diff --git a/plugin.go b/plugin.go index 285d153a..d105d1af 100644 --- a/plugin.go +++ b/plugin.go @@ -1,7 +1,7 @@ //go:build linux && plugin /* - Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. + Copyright © 2021-2025 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/samples/secret/karavi-authorization-config.json b/samples/secret/karavi-authorization-config.json deleted file mode 100644 index d6c92e38..00000000 --- a/samples/secret/karavi-authorization-config.json +++ /dev/null @@ -1 +0,0 @@ -[{"username":"","password":"","intendedEndpoint":"https://unisphere-address:8443","endpoint":"https://localhost:9400","systemID":"000000000001","skipCertificateValidation":true,"isDefault":true}] diff --git a/service/controller.go b/service/controller.go index c07ca4d8..27f49c52 100644 --- a/service/controller.go +++ b/service/controller.go @@ -1,5 +1,5 @@ /* - Copyright © 2021-2025 Dell Inc. or its subsidiaries. All Rights Reserved. + Copyright © 2021-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,10 +15,12 @@ package service import ( + "encoding/base64" "errors" "fmt" "math/rand" "path" + "regexp" "slices" "sort" "strconv" @@ -38,9 +40,8 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "github.com/container-storage-interface/spec/lib/go/csi" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" + "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -115,6 +116,7 @@ const ( NFS = "nfs" NASServerName = "nasServer" fileSystemID = "file_system_id" + FcIscsiID = "SCSI_FC" ) // Keys for parameters to CreateVolume @@ -215,6 +217,7 @@ var ( func (s *service) GetPortIdentifier(ctx context.Context, symID string, dirPortKey string, pmaxClient pmax.Pmax) (string, error) { s.cacheMutex.Lock() defer s.cacheMutex.Unlock() + log := log.WithContext(ctx) portIdentifier := "" cache := getPmaxCache(symID) cacheExpired := false @@ -289,11 +292,114 @@ func (s *service) GetPowerMaxClient(primarySymID string, arrayIDs ...string) (pm return symmetrix.GetPowerMaxClient(primarySymID) } +// getDynamicSG identifies the most suitable Storage Group (SG) for volume placement on a given PowerMax array. +// It retrieves real-time volume counts from the array and selects the SG matching the base name or +// its suffixed versions (e.g., base--1) that has the lowest volume count under the allowed limit. +// +// Parameters: +// - ctx: Context for tracing and cancellation. +// - arrayID: The unique identifier of the target PowerMax array. +// - baseSGName: The primary name/prefix used to identify compatible storage groups. +// - s: The service instance providing the PowerMax client. +// +// Returns: +// - sgName: The name of the best existing SG or a proposed new SG name. +// - needsCreation: A boolean indicating if the proposed sgName must be created on the array. +// - err: Error if the array client cannot be reached or array limits are exceeded. +func getDynamicSG(ctx context.Context, arrayID, baseSGName string, s *service) (string, bool, error) { + pmaxClient, err := s.GetPowerMaxClient(arrayID) + if err != nil { + return "", false, fmt.Errorf("failed to get PowerMax client for array %s: %w", arrayID, err) + } + + // Get storage group volume counts from the array + log := log.WithContext(ctx) + sgVolumeCounts, err := pmaxClient.GetStorageGroupVolumeCounts(ctx, arrayID, baseSGName) + if err != nil { + return "", false, fmt.Errorf("failed to get storage group volume counts for array %s: %w", arrayID, err) + } + + var validID *regexp.Regexp + if baseSGName != "" { + // Pattern matches baseName--[digits] exactly + pattern := "^" + regexp.QuoteMeta(baseSGName) + "--\\d+$" + validID, _ = regexp.Compile(pattern) + } + + log.Infof("getDynamicSG called for array: %s, base name: %s", arrayID, baseSGName) + + var bestSG string + minCount := sgVolumeLimit + sgCounts := make(map[string]int) + + for _, sg := range sgVolumeCounts.StorageGroups { + isMatch := sg.ID == baseSGName + if !isMatch && validID != nil && validID.MatchString(sg.ID) { + isMatch = true + } + + if isMatch { + sgCounts[sg.ID] = sg.VolumeCount + // Track the SG with the absolute lowest volume count + if sg.VolumeCount < minCount { + minCount = sg.VolumeCount + bestSG = sg.ID + } + } + } + + // If no storage groups found at all, start with the base name + if len(sgCounts) == 0 { + log.Infof("No storage groups found matching prefix: %s. Proposing base name.", baseSGName) + return baseSGName, true, nil + } + + log.Debug(printSGCounts(sgCounts, arrayID)) + + // If we found an existing SG that hasn't hit the limit, return the one with the least volumes + if bestSG != "" { + log.Infof("Selected best existing SG: %s with %d volumes", bestSG, minCount) + return bestSG, false, nil + } + + // All existing matching SGs are full (>= sgVolumeLimit). + // Propose a new name using an incrementing suffix. + suffixIndex := 0 + for { + var proposedName string + if suffixIndex == 0 { + proposedName = baseSGName + } else { + proposedName = fmt.Sprintf("%s--%d", baseSGName, suffixIndex) + } + + // Check if the proposed name is available (not in the list we got from the array) + if _, exists := sgCounts[proposedName]; !exists { + log.Infof("All existing SGs full. Proposing new SG: %s", proposedName) + return proposedName, true, nil + } + + suffixIndex++ + } +} + +func printSGCounts(sgCounts map[string]int, arrayID string) string { + var sb strings.Builder + sb.WriteString("\n") + sb.WriteString("#### Storage Group Volume Counts for array: " + arrayID + ":\n") + for id, count := range sgCounts { + sb.WriteString(fmt.Sprintf(" SG ID: %s, Volume Count: %d\n", id, count)) + } + sb.WriteString("\n") + return sb.String() +} + func (s *service) CreateVolume( ctx context.Context, req *csi.CreateVolumeRequest) ( *csi.CreateVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -362,6 +468,26 @@ func (s *service) CreateVolume( hostDynDistribution := s.resolveParameter(params, symmetrixID, DynamicDistributionParam, "") namespace := s.resolveParameter(params, "", CSIPVCNamespace, "") + versionDetails, err := pmaxClient.GetVersionDetails(ctx) + if err != nil { + log.Error("Error in getversion API " + err.Error()) + return nil, status.Errorf(codes.Internal, "Error in getVersion API: %s", err.Error()) + } + var version int + if versionDetails.APIVersion != "" { + version, err = strconv.Atoi(versionDetails.APIVersion) + if err != nil { + log.Error("Error parsing getVersion" + err.Error()) + return nil, status.Errorf(codes.Internal, "Error in parsing getVersion: %s", err.Error()) + } + } + + // Dynamic SG check is available only from 10.1 + if s.opts.dynamicSGEnabled && version < 101 { + log.Errorf("Dynamic SG is enabled, but not supported for array %s with version %d. Minimum expected array version is 10.1", symmetrixID, version) + return nil, status.Errorf(codes.Internal, "Dynamic SG is enabled, but not supported for array %s with version %d. Minimum expected array version is 10.1", symmetrixID, version) + } + // File related params useNFS := false nasServer := "" @@ -539,7 +665,7 @@ func (s *service) CreateVolume( if useNFS { // calculate size in MiB - reqSizeInMiB := cr.GetRequiredBytes() / MiBSizeInBytes + reqSizeInMiB := (cr.GetRequiredBytes() + MiBSizeInBytes - 1) / MiBSizeInBytes return file.CreateFileSystem(ctx, reqID, accessibility, params, symmetrixID, storagePoolID, serviceLevel, nasServer, volumeIdentifier, allowRoot, reqSizeInMiB, pmaxClient) } // Storage Group is required to be derived from the parameters (such as service level and storage resource pool which are supplied in parameters) @@ -556,6 +682,18 @@ func (s *service) CreateVolume( storageGroupName = fmt.Sprintf("%s-%s", storageGroupName, hostLimitName) } } + + var dynamicSGName string + var needCreation bool + if s.opts.dynamicSGEnabled { + if dynamicSGName, needCreation, err = getDynamicSG(ctx, symmetrixID, storageGroupName, s); err != nil { + log.Error("failed to get dynamic SG: " + err.Error()) + return nil, status.Error(codes.Internal, err.Error()) + } + log.Infof("####### dynamic storage group name: %s, base SG name: %s, needCreation: %v", dynamicSGName, storageGroupName, needCreation) + storageGroupName = dynamicSGName + } + // localProtectionGroupID refers to name of Storage Group which has protected local volumes // remoteProtectionGroupID refers to name of Storage Group which has protected remote volumes var localProtectionGroupID string @@ -612,10 +750,17 @@ func (s *service) CreateVolume( isSGUnprotected = true } } + // Check existence of the Storage Group and create if necessary. - sg, err := pmaxClient.GetStorageGroup(ctx, symmetrixID, storageGroupName) - if err != nil || sg == nil { - log.Debug(fmt.Sprintf("Unable to find storage group: %s", storageGroupName)) + if !s.opts.dynamicSGEnabled { + sg, err := pmaxClient.GetStorageGroup(ctx, symmetrixID, storageGroupName) + if err != nil || sg == nil { + log.Debug(fmt.Sprintf("Unable to find storage group: %s", storageGroupName)) + needCreation = true + } + } + + if needCreation { hostLimitsParam := &types.SetHostIOLimitsParam{ HostIOLimitMBSec: hostMBsec, HostIOLimitIOSec: hostIOsec, @@ -633,134 +778,286 @@ func (s *service) CreateVolume( return nil, status.Errorf(codes.Internal, "Error creating storage group: %s", err.Error()) } } - var vol *types.Volume - // Idempotency test. We will read the volume and check for: - // 1. Existence of a volume with matching volume name - // 2. Matching cylinderSize - // 3. Is a member of the storage group - // 4. Check if snapshot/volume target - log.Debug("Calling GetVolumeIDList for idempotency test") - // For now an exact match - volumeIDList, err := pmaxClient.GetVolumeIDList(ctx, symmetrixID, volumeIdentifier, false) - if err != nil { - log.Error("Error looking up volume for idempotence check: " + err.Error()) - return nil, status.Errorf(codes.Internal, "Error looking up volume for idempotence check: %s", err.Error()) - } alreadyExists := false - // isLocalVolumePresent restrict CreateVolumeInProtectedSG call if the volume is present in local SG but not in remote SG isLocalVolumePresent := false - // Look up the volume(s), if any, returned for the idempotency check to see if there are any matches - // We ignore any volume not in the desired storage group (even though they have the same name). - for _, volumeID := range volumeIDList { - // Fetch the volume - log.WithFields(fields).Info("Calling GetVolumeByID for idempotence check") - vol, err = pmaxClient.GetVolumeByID(ctx, symmetrixID, volumeID) - if err != nil { - log.Error("Error fetching volume for idempotence check: " + err.Error()) - return nil, status.Errorf(codes.Internal, "Error fetching volume for idempotence check: %s", err.Error()) - } - if len(vol.StorageGroupIDList) < 1 { - log.Error("Idempotence check: StorageGroupIDList is empty for (%s): " + volumeID) - return nil, status.Errorf(codes.Internal, "Idempotence check: StorageGroupIDList is empty for (%s)", volumeID) - } - matchesStorageGroup := false - for _, sgid := range vol.StorageGroupIDList { - if sgid == storageGroupName { - matchesStorageGroup = true - } - } - // with Authorization, a tenant prefix is applied to the volume identifier on the array - // csi-CSM-pmax-69298b3d3d-namespace -> tn1-csi-CSM-pmax-69298b3d3d-namespace - // since we don't know the tenant prefix, the volume identifier on the array is checked to contain the standard volume identifier - if matchesStorageGroup && (vol.VolumeIdentifier == volumeIdentifier || strings.Contains(vol.VolumeIdentifier, volumeIdentifier)) { - // A volume with the same name exists and has the same size - if vol.CapacityCYL != requiredCylinders { - log.Error("A volume with the same name exists but has a different size than required.") - alreadyExists = true - continue + var vol *types.Volume + var volumeList *types.Volumev1 + + if version >= 103 { + log.Debug("API version is greater than or equal to 103. Using enhanced API") + // Idempotency test. We will read the volume and check for: + // 1. Existence of a volume with matching volume name + // 2. Matching cylinderSize + // 3. Is a member of the storage group + // 4. Check if snapshot/volume target + log.Debug("Calling GetVolumeIDList for idempotency test") + volumeList, err = pmaxClient.GetVolumesByIdentifier(ctx, symmetrixID, volumeIdentifier) + if err != nil { + log.Error("Error getting the volumes for idempotence check: " + err.Error()) + return nil, status.Errorf(codes.Internal, "Error getting the volumes for idempotence check: %s", err.Error()) + } + + // isLocalVolumePresent restrict CreateVolumeInProtectedSG call if the volume is present in local SG but not in remote SG + // isLocalVolumePresent := false + // Look up the volume(s), if any, returned for the idempotency check to see if there are any matches + // We ignore any volume not in the desired storage group (even though they have the same name). + for _, eachVol := range volumeList.Volumes { + if len(eachVol.StorageGroups) < 1 { + log.Error("Idempotence check: StorageGroupIDList is empty for (%s): " + eachVol.ID) + return nil, status.Errorf(codes.Internal, "Idempotence check: StorageGroupIDList is empty for (%s)", eachVol.ID) } - var remoteVolumeID string - if replicationEnabled == "true" { - remoteVolumeID, _, err = s.GetRemoteVolumeID(ctx, symmetrixID, localRDFGrpNo, vol.VolumeID, pmaxClient) - if err != nil && !strings.Contains(err.Error(), "The device must be an RDF device") { - return nil, status.Errorf(codes.Internal, "Failed to fetch rdf pair information for (%s) - Error (%s)", vol.VolumeID, err.Error()) + matchesStorageGroup := false + for _, sgid := range eachVol.StorageGroups { + if strings.Contains(sgid.StorageGroupID, storageGroupName) { + matchesStorageGroup = true + storageGroupName = sgid.StorageGroupID } - if remoteVolumeID == "" { - // Missing corresponding Remote Volume Name for existing local volume - // The SG is unprotected as Local volume and Local SG exists but missing corresponding SRDF info - // If the SG was protected, there must exist a corresponding remote replica volume - log.Debugf("Local Volume already exist, skipping creation (%s)", vol.VolumeID) - isLocalVolumePresent = true + } + + // with Authorization, a tenant prefix is applied to the volume identifier on the array + // csi-CSM-pmax-69298b3d3d-namespace -> tn1-csi-CSM-pmax-69298b3d3d-namespace + // since we don't know the tenant prefix, the volume identifier on the array is checked to contain the standard volume identifier + if matchesStorageGroup && (eachVol.Identifier == volumeIdentifier || strings.Contains(eachVol.Identifier, volumeIdentifier)) { + // A volume with the same name exists and has the same size + if eachVol.CapCyl != float64(requiredCylinders) { + log.Error("A volume with the same name exists but has a different size than required.") + alreadyExists = true continue } - } - if volContent != "" { + var remoteVolumeID string if replicationEnabled == "true" { - if srcSnapID != "" { - err = s.LinkSRDFVolToSnapshot(ctx, reqID, symID, srcVol.VolumeID, snapID, localProtectionGroupID, localRDFGrpNo, vol, bias, false, pmaxClient) - if err != nil { - return nil, err - } - } else if srcVolID != "" { - // Build the temporary snapshot identifier - tmpSnapID := fmt.Sprintf("%s%s-%d", TempSnap, s.getClusterPrefix(), time.Now().Nanosecond()) - err = s.LinkSRDFVolToVolume(ctx, reqID, symID, srcVol, vol, tmpSnapID, localProtectionGroupID, localRDFGrpNo, "false", false, pmaxClient) - if err != nil { - return nil, status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) - } + remoteVolumeID, _, err = s.GetRemoteVolumeID(ctx, symmetrixID, localRDFGrpNo, eachVol.ID, pmaxClient) + if err != nil && !strings.Contains(err.Error(), "The device must be an RDF device") { + return nil, status.Errorf(codes.Internal, "Failed to fetch rdf pair information for (%s) - Error (%s)", eachVol.ID, err.Error()) } - } else { // replication is not enabled - if srcSnapID != "" { - err = s.UnlinkTargets(ctx, symID, SrcDevID, pmaxClient) - if err != nil { - return nil, status.Errorf(codes.Internal, "Failed unlink existing target from snapshot (%s)", err.Error()) + if remoteVolumeID == "" { + // Missing corresponding Remote Volume Name for existing local volume + // The SG is unprotected as Local volume and Local SG exists but missing corresponding SRDF info + // If the SG was protected, there must exist a corresponding remote replica volume + log.Debugf("Local Volume already exist, skipping creation (%s)", eachVol.ID) + isLocalVolumePresent = true + vol = &types.Volume{} + vol.VolumeID = eachVol.ID + vol.CapacityGB = eachVol.CapCyl + vol.VolumeIdentifier = eachVol.Identifier + var sgIDs []string + for _, sg := range eachVol.StorageGroups { + if sg.StorageGroupID != "" { + sgIDs = append(sgIDs, sg.StorageGroupID) + } } - err = s.LinkVolumeToSnapshot(ctx, symID, srcVol.VolumeID, vol.VolumeID, snapID, reqID, false, pmaxClient) - if err != nil { - return nil, status.Errorf(codes.Internal, "Failed to create volume from snapshot (%s)", err.Error()) + vol.StorageGroupIDList = sgIDs + continue + } + } + if volContent != "" { + if replicationEnabled == "true" { + if srcSnapID != "" { + err = s.LinkSRDFVolToSnapshot(ctx, reqID, symID, srcVol.VolumeID, snapID, localProtectionGroupID, localRDFGrpNo, vol, bias, false, pmaxClient) + if err != nil { + return nil, err + } + } else if srcVolID != "" { + err = s.LinkSRDFCloneVolume(ctx, reqID, symID, srcVol, vol, localProtectionGroupID, localRDFGrpNo, "false", pmaxClient) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) + } } - } else if srcVolID != "" { - tmpSnapID := fmt.Sprintf("%s%s-%d", TempSnap, s.getClusterPrefix(), time.Now().Nanosecond()) - err = s.LinkVolumeToVolume(ctx, symID, srcVol, vol.VolumeID, tmpSnapID, reqID, false, pmaxClient) - if err != nil { - return nil, status.Errorf(codes.Internal, "Failed to create volume from volume (%s)", err.Error()) + } else { // replication is not enabled + if srcSnapID != "" { + err = s.UnlinkTargets(ctx, symID, SrcDevID, pmaxClient) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed unlink existing target from snapshot (%s)", err.Error()) + } + err = s.LinkVolumeToSnapshot(ctx, symID, srcVol.VolumeID, eachVol.ID, snapID, reqID, false, pmaxClient) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create volume from snapshot (%s)", err.Error()) + } + } else if srcVolID != "" && eachVol.ID != "" { + replicaRequest := types.ReplicationRequest{ + ReplicationPair: []types.ReplicationPair{ + { + SourceVolumeName: srcVol.VolumeID, + TargetVolumeName: eachVol.ID, + }, + }, + Establish: true, + EstablishTerminate: true, + } + err = pmaxClient.CloneVolumeFromVolume(ctx, symID, replicaRequest) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create volume from volume (%s)", err.Error()) + } } } } - } - log.WithFields(fields).Info("Idempotent volume detected, returning success") - vol.VolumeID = fmt.Sprintf("%s-%s-%s", vol.VolumeIdentifier, symmetrixID, vol.VolumeID) - volResp := s.getCSIVolume(vol) - // Set the volume context - attributes := map[string]string{ - ServiceLevelParam: serviceLevel, - StoragePoolParam: storagePoolID, - path.Join(s.opts.ReplicationContextPrefix, SymmetrixIDParam): symmetrixID, - CapacityGB: fmt.Sprintf("%.2f", vol.CapacityGB), - ContentSource: volContent, - StorageGroup: storageGroupName, - // Format the time output - "CreationTime": time.Now().Format("20060102150405"), + log.WithFields(fields).Info("Idempotent volume detected, returning success") + eachVol.ID = fmt.Sprintf("%s-%s-%s", eachVol.Identifier, symmetrixID, eachVol.ID) + volResp := s.buildCSIVolume(&eachVol) + // Set the volume context + attributes := map[string]string{ + ServiceLevelParam: serviceLevel, + StoragePoolParam: storagePoolID, + path.Join(s.opts.ReplicationContextPrefix, SymmetrixIDParam): symmetrixID, + CapacityGB: fmt.Sprintf("%.2f", eachVol.CapCyl), + ContentSource: volContent, + StorageGroup: storageGroupName, + // Format the time output + "CreationTime": time.Now().Format("20060102150405"), + } + if replicationEnabled == "true" { + addReplicationParamsToVolumeAttributes(attributes, s.opts.ReplicationContextPrefix, remoteSymID, repMode, remoteVolumeID, localRDFGrpNo, remoteRDFGrpNo) + } + volResp.VolumeContext = attributes + csiResp := &csi.CreateVolumeResponse{ + Volume: volResp, + } + volResp.ContentSource = contentSource + if accessibility != nil { + volResp.AccessibleTopology = accessibility.Preferred + } + return csiResp, nil } - - if symmIDFoundInAZ { - s.addZoneLabelsToVolumeAttributes(attributes, symmetrixID) + } + } else { + // Idempotency test. We will read the volume and check for: + // 1. Existence of a volume with matching volume name + // 2. Matching cylinderSize + // 3. Is a member of the storage group + // 4. Check if snapshot/volume target + log.Debug("Calling GetVolumeIDList for idempotency test") + // For now an exact match + volumeIDList, err := pmaxClient.GetVolumeIDList(ctx, symmetrixID, volumeIdentifier, false) + if err != nil { + log.Error("Error looking up volume for idempotence check: " + err.Error()) + return nil, status.Errorf(codes.Internal, "Error looking up volume for idempotence check: %s", err.Error()) + } + // isLocalVolumePresent restrict CreateVolumeInProtectedSG call if the volume is present in local SG but not in remote SG + // isLocalVolumePresent := false + // Look up the volume(s), if any, returned for the idempotency check to see if there are any matches + // We ignore any volume not in the desired storage group (even though they have the same name). + for _, volumeID := range volumeIDList { + // Fetch the volume + log.WithFields(fields).Info("Calling GetVolumeByID for idempotence check") + vol, err = pmaxClient.GetVolumeByID(ctx, symmetrixID, volumeID) + if err != nil { + log.Error("Error fetching volume for idempotence check: " + err.Error()) + return nil, status.Errorf(codes.Internal, "Error fetching volume for idempotence check: %s", err.Error()) } - - if replicationEnabled == "true" { - addReplicationParamsToVolumeAttributes(attributes, s.opts.ReplicationContextPrefix, remoteSymID, repMode, remoteVolumeID, localRDFGrpNo, remoteRDFGrpNo) + if len(vol.StorageGroupIDList) < 1 { + log.Error("Idempotence check: StorageGroupIDList is empty for (%s): " + volumeID) + return nil, status.Errorf(codes.Internal, "Idempotence check: StorageGroupIDList is empty for (%s)", volumeID) } - volResp.VolumeContext = attributes - csiResp := &csi.CreateVolumeResponse{ - Volume: volResp, + matchesStorageGroup := false + for _, sgid := range vol.StorageGroupIDList { + if strings.Contains(sgid, storageGroupName) { + matchesStorageGroup = true + storageGroupName = sgid + } } - volResp.ContentSource = contentSource - if accessibility != nil { - volResp.AccessibleTopology = accessibility.Preferred + + // with Authorization, a tenant prefix is applied to the volume identifier on the array + // csi-CSM-pmax-69298b3d3d-namespace -> tn1-csi-CSM-pmax-69298b3d3d-namespace + // since we don't know the tenant prefix, the volume identifier on the array is checked to contain the standard volume identifier + if matchesStorageGroup && (vol.VolumeIdentifier == volumeIdentifier || strings.Contains(vol.VolumeIdentifier, volumeIdentifier)) { + // A volume with the same name exists and has the same size + if vol.CapacityCYL != requiredCylinders { + log.Error("A volume with the same name exists but has a different size than required.") + alreadyExists = true + continue + } + var remoteVolumeID string + if replicationEnabled == "true" { + remoteVolumeID, _, err = s.GetRemoteVolumeID(ctx, symmetrixID, localRDFGrpNo, vol.VolumeID, pmaxClient) + if err != nil && !strings.Contains(err.Error(), "The device must be an RDF device") { + return nil, status.Errorf(codes.Internal, "Failed to fetch rdf pair information for (%s) - Error (%s)", vol.VolumeID, err.Error()) + } + if remoteVolumeID == "" { + // Missing corresponding Remote Volume Name for existing local volume + // The SG is unprotected as Local volume and Local SG exists but missing corresponding SRDF info + // If the SG was protected, there must exist a corresponding remote replica volume + log.Debugf("Local Volume already exist, skipping creation (%s)", vol.VolumeID) + isLocalVolumePresent = true + continue + } + } + if volContent != "" { + if replicationEnabled == "true" { + if srcSnapID != "" { + err = s.LinkSRDFVolToSnapshot(ctx, reqID, symID, srcVol.VolumeID, snapID, localProtectionGroupID, localRDFGrpNo, vol, bias, false, pmaxClient) + if err != nil { + return nil, err + } + } else if srcVolID != "" { + err = s.LinkSRDFCloneVolume(ctx, reqID, symID, srcVol, vol, localProtectionGroupID, localRDFGrpNo, "false", pmaxClient) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) + } + } + } else { // replication is not enabled + if srcSnapID != "" { + err = s.UnlinkTargets(ctx, symID, SrcDevID, pmaxClient) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed unlink existing target from snapshot (%s)", err.Error()) + } + err = s.LinkVolumeToSnapshot(ctx, symID, srcVol.VolumeID, vol.VolumeID, snapID, reqID, false, pmaxClient) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create volume from snapshot (%s)", err.Error()) + } + } else if srcVolID != "" { + replicaRequest := types.ReplicationRequest{ + ReplicationPair: []types.ReplicationPair{ + { + SourceVolumeName: srcVol.VolumeID, + TargetVolumeName: vol.VolumeID, + }, + }, + Establish: true, + EstablishTerminate: true, + } + + err = pmaxClient.CloneVolumeFromVolume(ctx, symID, replicaRequest) + if err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create volume from volume (%s)", err.Error()) + } + } + } + } + + log.WithFields(fields).Info("Idempotent volume detected, returning success") + vol.VolumeID = fmt.Sprintf("%s-%s-%s", vol.VolumeIdentifier, symmetrixID, vol.VolumeID) + volResp := s.getCSIVolume(vol) + // Set the volume context + attributes := map[string]string{ + ServiceLevelParam: serviceLevel, + StoragePoolParam: storagePoolID, + path.Join(s.opts.ReplicationContextPrefix, SymmetrixIDParam): symmetrixID, + CapacityGB: fmt.Sprintf("%.2f", vol.CapacityGB), + ContentSource: volContent, + StorageGroup: storageGroupName, + // Format the time output + "CreationTime": time.Now().Format("20060102150405"), + } + + if symmIDFoundInAZ { + s.addZoneLabelsToVolumeAttributes(attributes, symmetrixID) + } + + if replicationEnabled == "true" { + addReplicationParamsToVolumeAttributes(attributes, s.opts.ReplicationContextPrefix, remoteSymID, repMode, remoteVolumeID, localRDFGrpNo, remoteRDFGrpNo) + } + volResp.VolumeContext = attributes + csiResp := &csi.CreateVolumeResponse{ + Volume: volResp, + } + volResp.ContentSource = contentSource + if accessibility != nil { + volResp.AccessibleTopology = accessibility.Preferred + } + return csiResp, nil } - return csiResp, nil } } if alreadyExists { @@ -813,15 +1110,23 @@ func (s *service) CreateVolume( // If volume content source is specified, initiate no_copy to newly created volume if contentSource != nil { if srcVolID != "" { - // Build the temporary snapshot identifier - snapID := fmt.Sprintf("%s%s-%d", TempSnap, s.getClusterPrefix(), time.Now().Nanosecond()) if replicationEnabled == "true" { - err = s.LinkSRDFVolToVolume(ctx, reqID, symID, srcVol, vol, snapID, localProtectionGroupID, localRDFGrpNo, "false", false, pmaxClient) + err = s.LinkSRDFCloneVolume(ctx, reqID, symID, srcVol, vol, localProtectionGroupID, localRDFGrpNo, "false", pmaxClient) if err != nil { return nil, status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) } } else { - err = s.LinkVolumeToVolume(ctx, symID, srcVol, vol.VolumeID, snapID, reqID, false, pmaxClient) + replicaRequest := types.ReplicationRequest{ + ReplicationPair: []types.ReplicationPair{ + { + SourceVolumeName: srcVol.VolumeID, + TargetVolumeName: vol.VolumeID, + }, + }, + Establish: true, + EstablishTerminate: true, + } + err = pmaxClient.CloneVolumeFromVolume(ctx, symID, replicaRequest) if err != nil { return nil, status.Errorf(codes.Internal, "Failed to create volume from volume (%s)", err.Error()) } @@ -877,11 +1182,13 @@ func (s *service) CreateVolume( csiResp := &csi.CreateVolumeResponse{ Volume: volResp, } + fields[storageGroupName] = storageGroupName log.WithFields(fields).Infof("Created volume with ID: %s", volResp.VolumeId) return csiResp, nil } func (s *service) createMetroVolume(ctx context.Context, req *csi.CreateVolumeRequest, reqID, storagePoolID, symID, storageGroupName, serviceLevel, thick, remoteSymID, localRDFGrpNo, remoteRDFGrpNo, remoteServiceLevel, remoteSRPID, namespace, applicationPrefix, bias, hostLimitName, hostMBsec, hostIOsec, hostDynDist string) (*csi.CreateVolumeResponse, error) { + log := log.WithContext(ctx) repMode := Metro accessibility := req.GetAccessibilityRequirements() pmaxClient, err := s.GetPowerMaxClient(symID, remoteSymID) @@ -1136,8 +1443,9 @@ func (s *service) createMetroVolume(ctx context.Context, req *csi.CreateVolumeRe } matchesStorageGroup := false for _, sgid := range vol.StorageGroupIDList { - if sgid == storageGroupName { + if strings.Contains(sgid, storageGroupName) { matchesStorageGroup = true + storageGroupName = sgid } } if matchesStorageGroup && vol.VolumeIdentifier == volumeIdentifier { @@ -1179,11 +1487,9 @@ func (s *service) createMetroVolume(ctx context.Context, req *csi.CreateVolumeRe return nil, err } } else if srcVolID != "" { - // Build the temporary snapshot identifier - snapID := fmt.Sprintf("%s%s-%d", TempSnap, s.getClusterPrefix(), time.Now().Nanosecond()) - err = s.LinkSRDFVolToVolume(ctx, reqID, symID, srcVol, vol, snapID, localProtectionGroupID, localRDFGrpNo, bias, true, pmaxClient) + err = s.LinkSRDFCloneVolume(ctx, reqID, symID, srcVol, vol, localProtectionGroupID, localRDFGrpNo, "false", pmaxClient) if err != nil { - return nil, err + return nil, status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) } } } @@ -1300,9 +1606,7 @@ func (s *service) createMetroVolume(ctx context.Context, req *csi.CreateVolumeRe return nil, err } } else if srcVolID != "" { - // Build the temporary snapshot identifier - snapID := fmt.Sprintf("%s%s-%d", TempSnap, s.getClusterPrefix(), time.Now().Nanosecond()) - err = s.LinkSRDFVolToVolume(ctx, reqID, symID, srcVol, vol, snapID, localProtectionGroupID, localRDFGrpNo, bias, true, pmaxClient) + err = s.LinkSRDFCloneVolume(ctx, reqID, symID, srcVol, vol, localProtectionGroupID, localRDFGrpNo, "false", pmaxClient) if err != nil { return nil, status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) } @@ -1339,6 +1643,7 @@ func (s *service) createMetroVolume(ctx context.Context, req *csi.CreateVolumeRe } func (s *service) LinkSRDFVolToVolume(ctx context.Context, reqID, symID string, vol, tgtVol *types.Volume, snapID, localProtectionGroupID, localRDFGrpNo string, bias string, isCopy bool, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) // Create a snapshot from the Source // Set max 1 hr lifetime for the temporary snapshot log.Debugf("Creating snapshot %s on %s and linking it to %s", snapID, vol.VolumeID, tgtVol.VolumeID) @@ -1366,6 +1671,7 @@ func (s *service) LinkSRDFVolToVolume(ctx context.Context, reqID, symID string, } func (s *service) LinkSRDFVolToSnapshot(ctx context.Context, reqID, symID, srcVolID, snapID, localProtectionGroupID, localRDFGrpNo string, tgtVol *types.Volume, bias string, isCopy bool, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) // Take lock on SG var lockHandle string lockHandle = fmt.Sprintf("%s%s", localProtectionGroupID, symID) @@ -1418,12 +1724,18 @@ var releaseLockFunc = func(lockHandle, reqID string, lockNum int) { } func (s *service) getOrCreateProtectedStorageGroup(ctx context.Context, symID, localProtectionGroupID, _, localRDFGrpNo, repMode, reqID string, pmaxClient pmax.Pmax) (*types.RDFStorageGroup, error) { + log := log.WithContext(ctx) var lockHandle string lockHandle = fmt.Sprintf("%s%s", localProtectionGroupID, symID) lockNum := requestLockFunc(lockHandle, reqID) defer releaseLockFunc(lockHandle, reqID, lockNum) sg, err := pmaxClient.GetProtectedStorageGroup(ctx, symID, localProtectionGroupID) - if err != nil || sg == nil { + if err != nil { + if !(strings.Contains(err.Error(), cannotBeFound) || strings.Contains(err.Error(), errorNotFound)) { + log.Errorf("GetProtectedStorageGroup failed for (%s): (%s)", localProtectionGroupID, err.Error()) + return nil, status.Errorf(codes.Internal, "GetProtectedStorageGroup failed for (%s): (%s)", localProtectionGroupID, err.Error()) + } + // Verify the creation of new protected storage group is valid err = s.verifyProtectionGroupID(ctx, symID, localRDFGrpNo, repMode, pmaxClient) if err != nil { @@ -1475,6 +1787,7 @@ func (s *service) verifyProtectionGroupID(ctx context.Context, symID, localRdfGr // volume to create, and returns an error if volume size would be greater than // the given limit. Returned size is in number of cylinders func (s *service) validateVolSize(ctx context.Context, cr *csi.CapacityRange, symmetrixID, storagePoolID string, pmaxClient pmax.Pmax) (int, error) { + log := log.WithContext(ctx) var minSizeBytes, maxSizeBytes int64 minSizeBytes = cr.GetRequiredBytes() maxSizeBytes = cr.GetLimitBytes() @@ -1542,8 +1855,8 @@ func (s *service) validateVolSize(ctx context.Context, cr *csi.CapacityRange, sy "bad capacity: requested minimum size (%d bytes) is greater than the maximum available capacity (%d bytes)", minSizeBytes, maxAvailBytes) } if minSizeBytes < MinVolumeSizeBytes { - log.Warningf("bad capacity: requested size (%d bytes) is less than the minimum volume size (%d bytes) supported by PowerMax..", minSizeBytes, MinVolumeSizeBytes) - log.Warning("Proceeding with minimum volume size supported by PowerMax Array ......") + log.Warnf("bad capacity: requested size (%d bytes) is less than the minimum volume size (%d bytes) supported by PowerMax..", minSizeBytes, MinVolumeSizeBytes) + log.Warnf("Proceeding with minimum volume size supported by PowerMax Array ......") minSizeBytes = MinVolumeSizeBytes } if maxSizeBytes < minSizeBytes { @@ -1651,7 +1964,7 @@ func (s *service) parseCsiID(csiID string) ( ) { if csiID == "" { err = fmt.Errorf("A Volume ID is required for the request") - return + return volName, arrayID, devID, remoteSymID, remoteVolID, err } // get the Device ID and Array ID idComponents := strings.Split(csiID, "-") @@ -1660,7 +1973,7 @@ func (s *service) parseCsiID(csiID string) ( if numOfIDComponents < 3 { // Not well-formed err = fmt.Errorf("The CSI ID %s is not formed correctly", csiID) - return + return volName, arrayID, devID, remoteSymID, remoteVolID, err } // check for file system and non FS, format: // fS: csi-ABC-pmax-448c258b72-ns1-nsx-000120000549-649112ce-742b-b93a-abcd-026048200208 @@ -1682,7 +1995,7 @@ func (s *service) parseCsiID(csiID string) ( if length <= lengthOfTrailer+2 { // Not well formed... err = fmt.Errorf("The CSI ID %s is not formed correctly", csiID) - return + return volName, arrayID, devID, remoteSymID, remoteVolID, err } volName = csiID[0 : length-lengthOfTrailer] @@ -1695,7 +2008,7 @@ func (s *service) parseCsiID(csiID string) ( devID = vols[0] remoteVolID = vols[1] } - return + return volName, arrayID, devID, remoteSymID, remoteVolID, err } func (s *service) DeleteVolume( @@ -1703,6 +2016,7 @@ func (s *service) DeleteVolume( req *csi.DeleteVolumeRequest) ( *csi.DeleteVolumeResponse, error, ) { + log := log.WithContext(ctx) id := req.GetVolumeId() volName, symID, devID, remoteSymID, remDevID, err := s.parseCsiID(id) if err != nil { @@ -1771,6 +2085,7 @@ func (s *service) DeleteVolume( } func (s *service) deleteVolume(ctx context.Context, reqID, symID, volName, devID, id string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) // log all parameters used in DeleteVolume call fields := map[string]interface{}{ "SymmetrixID": symID, @@ -1840,13 +2155,14 @@ func (s *service) deleteVolume(ctx context.Context, reqID, symID, volName, devID } err = s.MarkVolumeForDeletion(ctx, symID, vol, pmaxClient) if err != nil { - log.Error("RequestSoftVolDelete failed with error - ", err.Error()) + log.Errorf("RequestSoftVolDelete failed with error - %s", err.Error()) return status.Errorf(codes.Internal, "Failed marking volume for deletion with error (%s)", err.Error()) } return nil } func (s *service) deleteFileSystem(ctx context.Context, reqID, symID, fsName, fsID, _ string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) // log all parameters used in DeleteVolume call fields := map[string]interface{}{ "SymmetrixID": symID, @@ -1889,7 +2205,7 @@ func (s *service) deleteFileSystem(ctx context.Context, reqID, symID, fsName, fs // Delete the file System as there is no NFS export err = file.DeleteFileSystem(ctx, symID, fsID, pmaxClient) if err != nil { - log.Error("DeleteFileSystem failed with error - ", err.Error()) + log.Errorf("DeleteFileSystem failed with error - %s", err.Error()) return status.Errorf(codes.Internal, "Failed deletion of File System with error (%s)", err.Error()) } return nil @@ -1900,6 +2216,7 @@ func (s *service) ControllerPublishVolume( req *csi.ControllerPublishVolumeRequest) ( *csi.ControllerPublishVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -1907,7 +2224,6 @@ func (s *service) ControllerPublishVolume( reqID = req[0] } } - volumeContext := req.GetVolumeContext() if volumeContext != nil { log.Infof("VolumeContext:") @@ -1922,11 +2238,12 @@ func (s *service) ControllerPublishVolume( return nil, status.Error(codes.InvalidArgument, "volume ID is required") } - _, symID, devID, remoteSymID, remoteVolumeID, err := s.parseCsiID(volID) + volumeName, symID, devID, remoteSymID, remoteVolumeID, err := s.parseCsiID(volID) if err != nil { log.Errorf("Invalid volumeid: %s", volID) return nil, status.Errorf(codes.InvalidArgument, "Invalid volume id: %s", volID) } + pmaxClient, err := s.GetPowerMaxClient(symID, remoteSymID) if err != nil { log.Error(err.Error()) @@ -2040,6 +2357,7 @@ func (s *service) ControllerPublishVolume( // Update the values, if NVME is false hostID, tgtStorageGroupID, tgtMaskingViewID = s.GetHostSGAndMVIDFromNodeID(nodeID, isISCSI) } + if !nodeInCache { // Update the map val, ok := nodeCache.LoadOrStore(cacheID, hostID) @@ -2049,17 +2367,20 @@ func (s *service) ControllerPublishVolume( log.Debugf("REQ ID: %s Some other goroutine added hostID: %s for node: %s to node cache", reqID, val.(string), nodeID) if hostID != val.(string) { - log.Warningf("REQ ID: %s Mismatch between calculated value: %s and latest value: %s from node cache", + log.Warnf("REQ ID: %s Mismatch between calculated value: %s and latest value: %s from node cache", reqID, val.(string), hostID) } } } - publishContext := map[string]string{ - PublishContextDeviceWWN: vol.EffectiveWWN, + publishContext := make(map[string]string) + if len(vol.EffectiveWWN) > 0 { + publishContext[PublishContextDeviceWWN] = vol.EffectiveWWN + } else { + return nil, status.Errorf(codes.Internal, "PublishVolume: Volume %s has no effective WWN, Unisphere may not be synchronized with array or synchronization may be in progress", volID) } - ctrlPubRes, ctrlPubErr := s.publishVolume(ctx, publishContext, tgtStorageGroupID, hostID, symID, symID, tgtMaskingViewID, devID, reqID, am, pmaxClient, true) + ctrlPubRes, ctrlPubErr := s.publishVolume(ctx, publishContext, tgtStorageGroupID, hostID, symID, symID, tgtMaskingViewID, devID, reqID, volumeName, am, pmaxClient, true) if ctrlPubErr != nil { return nil, ctrlPubErr } @@ -2083,18 +2404,20 @@ func (s *service) ControllerPublishVolume( return nil, status.Errorf(codes.Internal, "PublishVolume: Could not retrieve remote volume: (%s)", err.Error()) } publishContext[RemotePublishContextDeviceWWN] = remoteVol.EffectiveWWN - return s.publishVolume(ctx, publishContext, tgtStorageGroupID, hostID, symID, remoteSymID, tgtMaskingViewID, remoteVolumeID, reqID, am, pmaxClient, false) + return s.publishVolume(ctx, publishContext, tgtStorageGroupID, hostID, symID, remoteSymID, tgtMaskingViewID, remoteVolumeID, reqID, volumeName, am, pmaxClient, false) } return ctrlPubRes, ctrlPubErr } -func (s *service) publishVolume(ctx context.Context, publishContext map[string]string, tgtStorageGroupID, hostID, clientSymID, symID, tgtMaskingViewID, deviceID, reqID string, accessMode *csi.VolumeCapability_AccessMode, pmaxClient pmax.Pmax, isLocal bool) (*csi.ControllerPublishVolumeResponse, error) { - waitChan, lockChan, err := s.sgSvc.requestAddVolumeToSGMV(ctx, tgtStorageGroupID, tgtMaskingViewID, hostID, reqID, clientSymID, symID, deviceID, accessMode) +func (s *service) publishVolume(ctx context.Context, publishContext map[string]string, tgtStorageGroupID, hostID, clientSymID, symID, tgtMaskingViewID, deviceID, reqID, volumeName string, accessMode *csi.VolumeCapability_AccessMode, pmaxClient pmax.Pmax, isLocal bool) (*csi.ControllerPublishVolumeResponse, error) { + waitChan, lockChan, err := s.sgSvc.requestAddVolumeToSGMV(ctx, tgtStorageGroupID, tgtMaskingViewID, hostID, reqID, clientSymID, symID, deviceID, volumeName, accessMode) if err != nil { - log.Error(err) + log.Error(err.Error()) return nil, err } - log.Infof("reqID %s devID %s waitChan %v lockChan %v", reqID, deviceID, waitChan, lockChan) + + log := log.WithContext(ctx) + log.Infof("reqID %s devID %s volumeName %s waitChan %v lockChan %v", reqID, deviceID, volumeName, waitChan, lockChan) var connections []*types.MaskingViewConnection for done := false; !done; { @@ -2130,6 +2453,7 @@ func (s *service) publishVolume(ctx context.Context, publishContext map[string]s func (s *service) updatePublishContext(ctx context.Context, publishContext map[string]string, symID, tgtMaskingViewID, deviceID, reqID string, connections []*types.MaskingViewConnection, pmaxClient pmax.Pmax, isLocal bool, ) (*csi.ControllerPublishVolumeResponse, error) { + log := log.WithContext(ctx) // If we got connections already from runAddVolumesToSGMV, see if there are any for our deviceID. err := errors.New("no connections") if len(connections) > 0 { @@ -2237,6 +2561,7 @@ func getMVLockKey(symID, tgtMaskingViewID string) string { // and the existence of the host on array, it returns a bool to indicate if the Host // on array is NVMe or not func (s *service) IsNodeNVMe(ctx context.Context, symID, nodeID string, pmaxClient pmax.Pmax) (bool, error) { + log := log.WithContext(ctx) nvmeTCPHostID, _, nvmeTCPMaskingViewID := s.GetNVMETCPHostSGAndMVIDFromNodeID(nodeID) if s.opts.TransportProtocol == NvmeTCPTransportProtocol || s.useNVMeTCP { log.Debug("Preferred transport protocol is set to NVME/TCP") @@ -2460,6 +2785,7 @@ func (s *service) ControllerUnpublishVolume( req *csi.ControllerUnpublishVolumeRequest) ( *csi.ControllerUnpublishVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -2548,6 +2874,7 @@ func (s *service) ControllerUnpublishVolume( } func (s *service) unpublishVolume(ctx context.Context, reqID string, vol *types.Volume, nodeID, clientSymID, symID, devID string) error { + log := log.WithContext(ctx) // log all parameters used in ControllerUnpublishVolume call fields := map[string]interface{}{ "SymmetrixID": symID, @@ -2566,6 +2893,7 @@ func (s *service) unpublishVolume(ctx context.Context, reqID string, vol *types. if s.opts.IsVsphereEnabled { _, tgtFCStorageGroupID, tgtFCMaskingViewID = s.GetVSphereFCHostSGAndMVIDFromNodeID() } + // Check if volume is part of the Storage group currentSGIDs := vol.StorageGroupIDList volumeInStorageGroup := false @@ -2601,7 +2929,7 @@ func (s *service) unpublishVolume(ctx context.Context, reqID string, vol *types. } waitChan, lockChan, err := s.sgSvc.requestRemoveVolumeFromSGMV(ctx, tgtStorageGroupID, tgtMaskingViewID, reqID, clientSymID, symID, devID) if err != nil { - log.Error(err) + log.Error(err.Error()) return err } log.Infof("reqID %s devID %s waitChan %v lockChan %v", reqID, devID, waitChan, lockChan) @@ -2627,6 +2955,7 @@ func (s *service) ValidateVolumeCapabilities( req *csi.ValidateVolumeCapabilitiesRequest) ( *csi.ValidateVolumeCapabilitiesResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -2848,6 +3177,7 @@ func (s *service) GetCapacity( req *csi.GetCapacityRequest) ( *csi.GetCapacityResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -2932,6 +3262,7 @@ func (s *service) GetCapacity( // Return the storage pool capacities of types.SrpCap func (s *service) getStoragePoolCapacities(ctx context.Context, symmetrixID, storagePoolID string, pmaxClient pmax.Pmax) (*types.SrpCap, *types.FbaCap, *types.CkdCap, error) { + log := log.WithContext(ctx) // Get storage pool info srp, err := pmaxClient.GetStoragePool(ctx, symmetrixID, storagePoolID) if err != nil { @@ -3032,6 +3363,7 @@ func (s *service) ControllerGetCapabilities( } func (s *service) controllerProbe(ctx context.Context) error { + log := log.WithContext(ctx) defer log.Debug("Exiting controllerProbe") // Check that we have the details needed to login to the Gateway if !s.opts.UseProxy && s.opts.Endpoint == "" { @@ -3114,6 +3446,7 @@ func (s *service) SelectOrCreatePortGroup(ctx context.Context, symID string, hos // SelectOrCreateFCPGForHost - Selects or creates a Fibre Channel PG given a symid and host func (s *service) SelectOrCreateFCPGForHost(ctx context.Context, symID string, host *types.Host, pmaxClient pmax.Pmax) (string, error) { + log := log.WithContext(ctx) if host == nil { return "", fmt.Errorf("SelectOrCreateFCPGForHost: host can't be nil") } @@ -3150,44 +3483,98 @@ func (s *service) SelectOrCreateFCPGForHost(ctx context.Context, symID string, h } } if len(portListFromHost) == 0 { - return "", fmt.Errorf("Failed to find a valid initiator for hostID %s from %s", hostID, symID) + return "", fmt.Errorf("failed to find a valid initiator for hostID %s from %s", hostID, symID) } - fcPortGroupList, err := pmaxClient.GetPortGroupList(ctx, symID, "fibre") + + versionDetails, err := pmaxClient.GetVersionDetails(ctx) if err != nil { - return "", fmt.Errorf("Failed to fetch Fibre channel port groups for array: %s", symID) + return "", fmt.Errorf("error in getversion API: %s", symID) } - log.Debugf("List of Fibre Channel Port Groups fetched from array: %v", fcPortGroupList) - filteredPGList := make([]string, 0) - for _, portGroupID := range fcPortGroupList.PortGroupIDs { - pgPrefix := "csi-" + s.opts.ClusterPrefix - if strings.Contains(portGroupID, pgPrefix) { - filteredPGList = append(filteredPGList, portGroupID) + + var version int + + if versionDetails.APIVersion != "" { + version, err = strconv.Atoi(versionDetails.APIVersion) + if err != nil { + return "", fmt.Errorf("error in parsing Version: %s", err.Error()) } } - for _, portGroupID := range filteredPGList { - portGroup, err := pmaxClient.GetPortGroupByID(ctx, symID, portGroupID) + + if version >= 103 { + log.Debug("API version is greater than or equal to 103. Using enhanced API") + portGroupList, err := pmaxClient.GetPortGroupListByType(ctx, symID, "fibre") if err != nil { - log.Error("Failed to fetch port group details") - continue + return "", fmt.Errorf("failed to fetch Fibre channel port groups for array(enhanced API): %s", symID) } - var portList []string - if (portGroup.PortGroupType == "Fibre") || (portGroup.PortGroupType == "SCSI_FC") { - for _, portKey := range portGroup.SymmetrixPortKey { - dirPort := fmt.Sprintf("%s:%s", portKey.DirectorID, portKey.PortID) - portList = append(portList, dirPort) + + for _, portGroup := range portGroupList.Results { + var portList []string + if portGroup.Protocol == FcIscsiID { + log.Debugf("PortGroup: %s, Protocol: %s", portGroup.ID, portGroup.Protocol) + for _, port := range portGroup.Ports { + log.Debugf("Port: %s, Type: %s, Director ID: %s", port.PortID, port.Type, port.Director.ID) + + decodedPortID, err := base64.RawStdEncoding.DecodeString(port.PortID) + if err != nil { + return "", fmt.Errorf("Failed to fetch Fibre channel port ID: %v", err) + } + log.Infof("Decoded port ID: %s", decodedPortID) + out := strings.Split(string(decodedPortID), "|") // we will get in the format id|number, but we need it in id:number so split and take here + if len(out) >= 2 { + dirPort := fmt.Sprintf("%s:%s", port.Director.ID, out[1]) + portList = append(portList, dirPort) + } + } } sort.Strings(portList) sort.Strings(portListFromHost) + if stringSlicesEqual(portList, portListFromHost) { - validPortGroupID = portGroupID - log.Debug(fmt.Sprintf("Found valid port group %s on the array %s", - portGroupID, symID)) + validPortGroupID = portGroup.ID + log.Debug(fmt.Sprintf("Found valid port group %s on the array %s", portGroup.ID, symID)) break } } + } else { + fcPortGroupList, err := pmaxClient.GetPortGroupList(ctx, symID, "fibre") + if err != nil { + return "", fmt.Errorf("Failed to fetch Fibre channel port groups for array: %s", symID) + } + log.Debugf("List of Fibre Channel Port Groups fetched from array: %v", fcPortGroupList) + filteredPGList := make([]string, 0) + for _, portGroupID := range fcPortGroupList.PortGroupIDs { + pgPrefix := "csi-" + s.opts.ClusterPrefix + if strings.Contains(portGroupID, pgPrefix) { + filteredPGList = append(filteredPGList, portGroupID) + } + } + for _, portGroupID := range filteredPGList { + portGroup, err := pmaxClient.GetPortGroupByID(ctx, symID, portGroupID) + if err != nil { + log.Error("Failed to fetch port group details") + continue + } + var portList []string + if (portGroup.PortGroupType == "Fibre") || (portGroup.PortGroupType == "SCSI_FC") { + for _, portKey := range portGroup.SymmetrixPortKey { + dirPort := fmt.Sprintf("%s:%s", portKey.DirectorID, portKey.PortID) + portList = append(portList, dirPort) + } + sort.Strings(portList) + sort.Strings(portListFromHost) + + if stringSlicesEqual(portList, portListFromHost) { + validPortGroupID = portGroupID + log.Debug(fmt.Sprintf("Found valid port group %s on the array %s", + portGroupID, symID)) + break + } + } + } } + if validPortGroupID == "" { - log.Warning("No port group found on the array. Attempting to create one") + log.Warn("No port group found on the array. Attempting to create one") // Create a PG dirNames := "" portKeys := make([]types.PortKey, 0) @@ -3224,6 +3611,7 @@ func (s *service) CreateSnapshot( req *csi.CreateSnapshotRequest) ( *csi.CreateSnapshotResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -3362,6 +3750,7 @@ func (s *service) DeleteSnapshot( req *csi.DeleteSnapshotRequest) ( *csi.DeleteSnapshotResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -3466,6 +3855,7 @@ func (s *service) ControllerExpandVolume( ctx context.Context, req *csi.ControllerExpandVolumeRequest) ( *csi.ControllerExpandVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -3568,6 +3958,7 @@ func (s *service) ControllerExpandVolume( // MarkVolumeForDeletion renames the volume with deletion prefix and sends a // request to deletion_worker queue func (s *service) MarkVolumeForDeletion(ctx context.Context, symID string, vol *types.Volume, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) if vol == nil { return fmt.Errorf("MarkVolumeForDeletion: Null volume object") } @@ -3609,6 +4000,7 @@ func (s *service) GetProtectedStorageGroupID(storageGroupIDList []string, filter } func (s *service) CreateStorageProtectionGroup(ctx context.Context, req *csiext.CreateStorageProtectionGroupRequest) (*csiext.CreateStorageProtectionGroupResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -3713,8 +4105,7 @@ func (s *service) CreateStorageProtectionGroup(ctx context.Context, req *csiext. pgStatus, err := s.getStorageProtectionGroupStatus(ctx, localProtectionGroupID, reqID, localParams) if err != nil { // Ignore the error for now, status should get updated in a future monitoring call - log.Warning(fmt.Sprintf("failed to get status for SG: %s, error: %s. continuing", - localProtectionGroupID, err.Error())) + log.Warnf("failed to get status for SG: %s, error: %s. continuing", localProtectionGroupID, err.Error()) } // found both SGs, return response csiExtResp := &csiext.CreateStorageProtectionGroupResponse{ @@ -3728,6 +4119,8 @@ func (s *service) CreateStorageProtectionGroup(ctx context.Context, req *csiext. } func (s *service) CreateRemoteVolume(ctx context.Context, req *csiext.CreateRemoteVolumeRequest) (*csiext.CreateRemoteVolumeResponse, error) { + log := log.WithContext(ctx) + var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -3955,6 +4348,7 @@ func (s *service) CreateRemoteVolume(ctx context.Context, req *csiext.CreateRemo } func (s *service) DeleteStorageProtectionGroup(ctx context.Context, req *csiext.DeleteStorageProtectionGroupRequest) (*csiext.DeleteStorageProtectionGroupResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -4008,6 +4402,7 @@ func (s *service) DeleteStorageProtectionGroup(ctx context.Context, req *csiext. func (s *service) DeleteLocalVolume(ctx context.Context, req *csiext.DeleteLocalVolumeRequest, ) (*csiext.DeleteLocalVolumeResponse, error) { + log := log.WithContext(ctx) id := req.GetVolumeHandle() volName, symID, devID, _, _, err := s.parseCsiID(id) if err != nil { @@ -4064,6 +4459,7 @@ func addMetaData(params map[string]string) map[string][]string { } func (s *service) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -4166,6 +4562,7 @@ func getHostIDFromMaskingView(maskingView string) string { } func (s *service) ExecuteAction(ctx context.Context, req *csiext.ExecuteActionRequest) (*csiext.ExecuteActionResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -4260,8 +4657,7 @@ func (s *service) ExecuteAction(ctx context.Context, req *csiext.ExecuteActionRe pgStatus, err = s.getStorageProtectionGroupStatus(ctx, protectionGroupID, reqID, localParams) if err != nil { // Ignore the error for now, status should get updated in a future monitoring call - log.Warning(fmt.Sprintf("failed to get status for SG: %s, error: %s. continuing", - protectionGroupID, err.Error())) + log.Warnf("failed to get status for SG: %s, error: %s. continuing", protectionGroupID, err.Error()) } } resp := &csiext.ExecuteActionResponse{ @@ -4297,6 +4693,7 @@ func getActionString(actionType csiext.ActionTypes) (string, bool) { func (s *service) getStorageProtectionGroupStatus(ctx context.Context, protectionGroupID, reqID string, params map[string]string, ) (*csiext.StorageProtectionGroupStatus, error) { + log := log.WithContext(ctx) symID := params[path.Join(s.opts.ReplicationContextPrefix, SymmetrixIDParam)] pmaxClient, err := symmetrix.GetPowerMaxClient(symID) if err != nil { @@ -4352,6 +4749,7 @@ func getPGStatusFromSGRDFInfo(psg *types.StorageGroupRDFG) *csiext.StorageProtec } func (s *service) GetStorageProtectionGroupStatus(ctx context.Context, req *csiext.GetStorageProtectionGroupStatusRequest) (*csiext.GetStorageProtectionGroupStatusResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -4490,3 +4888,50 @@ func (s *service) addZoneLabelsToVolumeAttributes(attributes map[string]string, } } } + +func (s *service) LinkSRDFCloneVolume(ctx context.Context, reqID string, symID string, srcVol, vol *types.Volume, localProtectionGroupID, localRDFGrpNo string, bias string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) + // Take lock on SG + var lockHandle string + lockHandle = fmt.Sprintf("%s%s", localProtectionGroupID, symID) + lockNum := RequestLock(lockHandle, reqID) + defer ReleaseLock(lockHandle, reqID, lockNum) + bbias, _ := strconv.ParseBool(bias) + + replicaRequest := types.ReplicationRequest{ + ReplicationPair: []types.ReplicationPair{ + { + SourceVolumeName: srcVol.VolumeID, + TargetVolumeName: vol.VolumeID, + }, + }, + Establish: true, + EstablishTerminate: true, + } + // before cloning, we need to suspend the protected storage group + err := suspend(ctx, symID, localProtectionGroupID, localRDFGrpNo, pmaxClient) + if err != nil { + log.Errorf("suspend failed for (%s) err: %s", localProtectionGroupID, err.Error()) + return status.Errorf(codes.Internal, "Failed to suspend clone volume (%s)", err.Error()) + } + + err = pmaxClient.CloneVolumeFromVolume(ctx, symID, replicaRequest) + if err != nil { + return status.Errorf(codes.Internal, "Failed to create SRDF volume from volume (%s)", err.Error()) + } + + // after cloning, we need to establish the protected storage group + var parseErr error + bbias, parseErr = strconv.ParseBool(bias) + if parseErr != nil { + log.Warnf("Invalid bias value '%s', defaulting to false", bias) + bbias = false + } + + err = establish(ctx, symID, localProtectionGroupID, localRDFGrpNo, bbias, pmaxClient) + if err != nil { + log.Errorf("establish failed for (%s) err: %s", localProtectionGroupID, err.Error()) + return status.Errorf(codes.Internal, "Failed to establish clone volume (%s)", err.Error()) + } + return nil +} diff --git a/service/controller_test.go b/service/controller_test.go index 31be3fcf..8449d956 100644 --- a/service/controller_test.go +++ b/service/controller_test.go @@ -1,5 +1,5 @@ /* - Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. + Copyright © 2025-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package service import ( "errors" + "fmt" "net/http" "reflect" "strings" @@ -23,7 +24,6 @@ import ( "testing" "time" - "github.com/container-storage-interface/spec/lib/go/csi" "github.com/dell/csi-powermax/v2/k8sutils" "github.com/dell/csi-powermax/v2/pkg/symmetrix" "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" @@ -32,9 +32,10 @@ import ( "github.com/dell/gonvme" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" + "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/golang/mock/gomock" + gmock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" - gmock "go.uber.org/mock/gomock" "golang.org/x/net/context" ) @@ -487,6 +488,69 @@ func Test_service_createMetroVolume(t *testing.T) { wantErr: true, wantErrMsg: "couldn't find volume", }, + { + name: "when the client fails to get the source volume for enhanced api", + fields: serviceFields{}, + args: args{ + ctx: context.Background(), + req: &csi.CreateVolumeRequest{ + AccessibilityRequirements: &csi.TopologyRequirement{}, + CapacityRange: goodCapacityRange, + VolumeContentSource: &csi.VolumeContentSource{ + Type: &csi.VolumeContentSource_Snapshot{ + Snapshot: &csi.VolumeContentSource_SnapshotSource{ + SnapshotId: validLocalVolumeID, + }, + }, + }, + }, + symID: symIDLocal, + remoteSymID: symIDRemote, + storagePoolID: "POOL_1", + remoteSRPID: "POOL_1", + }, + setup: func() { + initDefaultClient() + // local powermax has enough space to continue + pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDLocal, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) + // remote powermax does not have enough space and should trigger an error + pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDRemote, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) + pmaxClient.EXPECT().IsAllowedArray(gomock.Any()).Times(1).Return(true, nil) + pmaxClient.EXPECT().GetReplicationCapabilities(gomock.Any()).Times(1).Return(&types.SymReplicationCapabilities{ + SymmetrixCapability: []types.SymmetrixCapability{ + { + SymmetrixID: symIDLocal, + SnapVxCapable: true, + }, + }, + }, nil) + pmaxClient.EXPECT().GetVolumeByID(gomock.Any(), "000120000001", "011AB").Times(1).Return(&types.Volume{}, nil) + pmaxClient.EXPECT(). + GetVolumesByIdentifier( + gomock.Any(), + gomock.Eq("000120000001"), + gomock.Eq("csi-CSM-csivol-7599623185-default"), + ). + AnyTimes(). + Return(&types.Volumev1{ + Volumes: []types.VolumeEnhanced{ + { + ID: validLocalVolumeID, + CapCyl: 600, // source volume capacity in cylinders + Identifier: "csi-CSM-csivol-7599623185-default", + Type: "Snapshot", // or "Volume" depending on your logic + System: types.SystemInfo{}, // optional + StorageGroups: []types.StorageGroupID{ + {StorageGroupID: "SG_1"}, + }, + }, + }, + }, nil) + }, + want: nil, + wantErr: true, + wantErrMsg: "Name cannot be empty", + }, { name: "when requested capacity is smaller than the source", fields: serviceFields{}, @@ -532,6 +596,70 @@ func Test_service_createMetroVolume(t *testing.T) { wantErr: true, wantErrMsg: "Requested capacity is smaller than the source", }, + { + name: "when requested capacity is smaller than the source for enhanced api", + fields: serviceFields{}, + args: args{ + ctx: context.Background(), + req: &csi.CreateVolumeRequest{ + AccessibilityRequirements: &csi.TopologyRequirement{}, + CapacityRange: goodCapacityRange, + VolumeContentSource: &csi.VolumeContentSource{ + Type: &csi.VolumeContentSource_Snapshot{ + Snapshot: &csi.VolumeContentSource_SnapshotSource{ + SnapshotId: validLocalVolumeID, + }, + }, + }, + }, + symID: symIDLocal, + remoteSymID: symIDRemote, + storagePoolID: "POOL_1", + remoteSRPID: "POOL_1", + }, + setup: func() { + initDefaultClient() + // local powermax has enough space to continue + pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDLocal, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) + // remote powermax does not have enough space and should trigger an error + pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDRemote, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) + pmaxClient.EXPECT().IsAllowedArray(gomock.Any()).Times(1).Return(true, nil) + pmaxClient.EXPECT().GetReplicationCapabilities(gomock.Any()).Times(1).Return(&types.SymReplicationCapabilities{ + SymmetrixCapability: []types.SymmetrixCapability{ + { + SymmetrixID: symIDLocal, + SnapVxCapable: true, + }, + }, + }, nil) + + pmaxClient.EXPECT().GetVolumeByID(gomock.Any(), "000120000001", "011AB").Times(1).Return(&types.Volume{}, nil) + pmaxClient.EXPECT(). + GetVolumesByIdentifier( + gomock.Any(), + gomock.Eq("000120000001"), + gomock.Eq("csi-CSM-csivol-7599623185-default"), + ). + AnyTimes(). + Return(&types.Volumev1{ + Volumes: []types.VolumeEnhanced{ + { + ID: validLocalVolumeID, + CapCyl: 600, // source volume capacity in cylinders + Identifier: "csi-CSM-csivol-7599623185-default", + Type: "Snapshot", // or "Volume" depending on your logic + System: types.SystemInfo{}, // optional + StorageGroups: []types.StorageGroupID{ + {StorageGroupID: "SG_1"}, + }, + }, + }, + }, nil) + }, + want: nil, + wantErr: true, + wantErrMsg: "Name cannot be empty", + }, { name: "the volume is block but block is not enabled", fields: serviceFields{ @@ -605,6 +733,51 @@ func Test_service_createMetroVolume(t *testing.T) { wantErr: true, wantErrMsg: "Name cannot be empty", }, + { + name: "fails to get protected storage group", + fields: serviceFields{ + opts: Opts{ + EnableBlock: true, + }, + }, + args: args{ + ctx: context.Background(), + req: &csi.CreateVolumeRequest{ + AccessibilityRequirements: &csi.TopologyRequirement{}, + Name: "csivol-01234abcde", + CapacityRange: goodCapacityRange, + VolumeCapabilities: []*csi.VolumeCapability{ + { + AccessType: &csi.VolumeCapability_Block{ + Block: &csi.VolumeCapability_BlockVolume{}, + }, + }, + }, + }, + symID: symIDLocal, + remoteSymID: symIDRemote, + storagePoolID: "POOL_1", + remoteSRPID: "POOL_1", + namespace: "my-test-service", + applicationPrefix: "my-test-db", + hostLimitName: "a-host-limit-name", + }, + setup: func() { + requestLockFunc = func(_, _ string) int { + return 0 + } + releaseLockFunc = func(_, _ string, _ int) {} + + initDefaultClient() + pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDLocal, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) + pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDRemote, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) + pmaxClient.EXPECT().GetProtectedStorageGroup(gomock.Any(), symIDLocal, gomock.Any()).Times(1). + Return(&types.RDFStorageGroup{}, errors.New("error retrieving storage group")) + }, + want: nil, + wantErr: true, + wantErrMsg: "error retrieving storage group", + }, { name: "fails to create a protected storage group", fields: serviceFields{ @@ -646,7 +819,7 @@ func Test_service_createMetroVolume(t *testing.T) { // remote powermax does not have enough space and should trigger an error pmaxClient.EXPECT().GetStoragePool(gomock.Any(), symIDRemote, "POOL_1").Times(1).Return(&types.StoragePool{SrpCap: goodSrpCapacity}, nil) pmaxClient.EXPECT().GetProtectedStorageGroup(gomock.Any(), symIDLocal, gomock.Any()).Times(1). - Return(&types.RDFStorageGroup{}, errors.New("failed to get storage group")) + Return(&types.RDFStorageGroup{}, errors.New("cannot be found")) pmaxClient.EXPECT().GetStorageGroupIDList(gomock.Any(), symIDLocal, gomock.Any(), false).Times(1). Return(nil, errors.New("failed to get storage group ID list")) }, @@ -2984,3 +3157,177 @@ func Test_service_addZoneLabelsToVolumeAttributes(t *testing.T) { }) } } + +func TestGetDynamicSG(t *testing.T) { + tests := []struct { + name string + arrayID string + initializeArray string + baseSGName string + mockClient *mocks.MockPmaxClient + expectedSGName string + expectedNeedsCreation bool + expectedErr error + }{ + { + name: "successful get base SG name as new dynamic SG when no SGs exist", + arrayID: "sg-array1", + initializeArray: "sg-array1", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(client) + client.EXPECT().GetStorageGroupVolumeCounts(gomock.Any(), "sg-array1", "csi-W55-Diamond-SRP_1-SG").Return(&types.StorageGroupVolumeCounts{ + StorageGroups: []types.StorageGroupVolumeCount{}, + }, nil) + return client + }(), + expectedSGName: "csi-W55-Diamond-SRP_1-SG", + expectedNeedsCreation: true, + expectedErr: nil, + }, + { + name: "successful get existing dynamic SG with space", + arrayID: "sg-array1", + initializeArray: "sg-array1", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(client) + client.EXPECT().GetStorageGroupVolumeCounts(gomock.Any(), "sg-array1", "csi-W55-Diamond-SRP_1-SG").Return(&types.StorageGroupVolumeCounts{ + StorageGroups: []types.StorageGroupVolumeCount{ + {ID: "csi-W55-Diamond-SRP_1-SG", VolumeCount: 1}, + }, + }, nil) + return client + }(), + expectedSGName: "csi-W55-Diamond-SRP_1-SG", + expectedNeedsCreation: false, + expectedErr: nil, + }, + { + name: "successful get new dynamic SG when no space in existing SGs", + arrayID: "sg-array1", + initializeArray: "sg-array1", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(client) + client.EXPECT().GetStorageGroupVolumeCounts(gomock.Any(), "sg-array1", "csi-W55-Diamond-SRP_1-SG").Return(&types.StorageGroupVolumeCounts{ + StorageGroups: []types.StorageGroupVolumeCount{ + {ID: "csi-W55-Diamond-SRP_1-SG", VolumeCount: 2}, + }, + }, nil) + return client + }(), + expectedSGName: "csi-W55-Diamond-SRP_1-SG--1", + expectedNeedsCreation: true, + expectedErr: nil, + }, + { + name: "successful get existing dynamic SG with minimal vol count", + arrayID: "sg-array1", + initializeArray: "sg-array1", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(client) + client.EXPECT().GetStorageGroupVolumeCounts(gomock.Any(), "sg-array1", "csi-W55-Diamond-SRP_1-SG").Return(&types.StorageGroupVolumeCounts{ + StorageGroups: []types.StorageGroupVolumeCount{ + {ID: "csi-W55-Diamond-SRP_1-SG", VolumeCount: 2}, + {ID: "csi-W55-Diamond-SRP_1-SG--1", VolumeCount: 0}, + {ID: "csi-W55-Diamond-SRP_1-SG--2", VolumeCount: 1}, + }, + }, nil) + return client + }(), + expectedSGName: "csi-W55-Diamond-SRP_1-SG--1", + expectedNeedsCreation: false, + expectedErr: nil, + }, + { + name: "error getting PowerMax client", + arrayID: "sg-array1", + initializeArray: "", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(nil) + return client + }(), + expectedSGName: "", + expectedNeedsCreation: false, + expectedErr: fmt.Errorf("failed to get PowerMax client for array sg-array1: array: sg-array1 not found"), + }, + { + name: "error getting storage group volume counts for array", + arrayID: "sg-array1", + initializeArray: "sg-array1", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(client) + client.EXPECT().GetStorageGroupVolumeCounts(gomock.Any(), "sg-array1", "csi-W55-Diamond-SRP_1-SG").Return(nil, errors.New("error getting sg")) + + return client + }(), + expectedSGName: "", + expectedNeedsCreation: false, + expectedErr: fmt.Errorf("failed to get storage group volume counts for array sg-array1: error getting sg"), + }, + { + name: "handle huge number of SGs", + arrayID: "sg-array1", + initializeArray: "sg-array1", + baseSGName: "csi-W55-Diamond-SRP_1-SG", + mockClient: func() *mocks.MockPmaxClient { + sgs := make([]types.StorageGroupVolumeCount, 102) + sgs[0] = types.StorageGroupVolumeCount{ + ID: "csi-W55-Diamond-SRP_1-SG", + VolumeCount: 2, + } + for i := 1; i < 101; i++ { + sgs[i] = types.StorageGroupVolumeCount{ + ID: fmt.Sprintf("csi-W55-Diamond-SRP_1-SG--%d", i), + VolumeCount: 2, + } + } + client := mocks.NewMockPmaxClient(gomock.NewController(t)) + client.EXPECT().WithSymmetrixID(gomock.Any()).AnyTimes().Return(client) + client.EXPECT().GetStorageGroupVolumeCounts(gomock.Any(), "sg-array1", "csi-W55-Diamond-SRP_1-SG").Return(&types.StorageGroupVolumeCounts{ + StorageGroups: sgs, + }, nil) + return client + }(), + expectedSGName: "csi-W55-Diamond-SRP_1-SG--101", + expectedNeedsCreation: true, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sgVolumeLimit = 2 + s := &service{ + adminClient: tt.mockClient, + } + _ = symmetrix.Initialize([]string{tt.initializeArray}, tt.mockClient) + defer symmetrix.RemoveClient(tt.initializeArray) + + sgName, needsCreation, err := getDynamicSG(context.Background(), tt.arrayID, tt.baseSGName, s) + if err != nil && tt.expectedErr != nil { + if err.Error() != tt.expectedErr.Error() { + t.Errorf("expected error %v, but got %v", tt.expectedErr, err) + } + } else if err != nil { + t.Errorf("expected no error, but got %v", err) + } + if sgName != tt.expectedSGName { + t.Errorf("expected storage group name %v, but got %v", tt.expectedSGName, sgName) + } + if needsCreation != tt.expectedNeedsCreation { + t.Errorf("expected needs creation %v, but got %v", tt.expectedNeedsCreation, needsCreation) + } + }) + } +} diff --git a/service/csi_ctrl_to_node_connectivity.go b/service/csi_ctrl_to_node_connectivity.go index 7e1a374c..e7b92ea2 100644 --- a/service/csi_ctrl_to_node_connectivity.go +++ b/service/csi_ctrl_to_node_connectivity.go @@ -21,8 +21,6 @@ import ( "io" "net/http" "time" - - log "github.com/sirupsen/logrus" ) // ArrayConnectivityStatus Status of the array probe @@ -38,9 +36,10 @@ const ( // QueryArrayStatus make API call to the specified url to retrieve connection status func (s *service) QueryArrayStatus(ctx context.Context, url string) (bool, error) { + log := log.WithContext(ctx) defer func() { if err := recover(); err != nil { - log.Println("panic occurred in queryStatus:", err) + log.Infof("panic occurred in queryStatus: %v", err) } }() client := http.Client{ diff --git a/service/csi_extension_server.go b/service/csi_extension_server.go index 2ceff04f..7a62a1c7 100644 --- a/service/csi_extension_server.go +++ b/service/csi_extension_server.go @@ -21,7 +21,6 @@ import ( "time" "github.com/dell/dell-csi-extensions/podmon" - log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -32,6 +31,7 @@ const OneHour int64 = 3600000 var metricsQuery = []string{"HostMBs", "MBRead", "MBWritten", "IoRate", "Reads", "Writes", "ResponseTime"} func (s *service) ValidateVolumeHostConnectivity(ctx context.Context, req *podmon.ValidateVolumeHostConnectivityRequest) (*podmon.ValidateVolumeHostConnectivityResponse, error) { + log := log.WithContext(ctx) log.Infof("ValidateVolumeHostConnectivity called %+v", req) rep := &podmon.ValidateVolumeHostConnectivityResponse{ Messages: make([]string, 0), @@ -106,6 +106,7 @@ func (s *service) ValidateVolumeHostConnectivity(ctx context.Context, req *podmo // checkIfNodeIsConnected looks at the 'nodeId' to determine if there is connectivity to the 'arrayId' array. // The 'rep' object will be filled with the results of the check. func (s *service) checkIfNodeIsConnected(ctx context.Context, symID string, nodeID string, rep *podmon.ValidateVolumeHostConnectivityResponse) error { + log := log.WithContext(ctx) log.Infof("Checking if array %s is connected to node %s", symID, nodeID) var message string rep.Connected = false @@ -137,6 +138,7 @@ func (s *service) checkIfNodeIsConnected(ctx context.Context, symID string, node // IsIOInProgress function check the IO operation status on array func (s *service) IsIOInProgress(ctx context.Context, volID, symID string) (err error) { + log := log.WithContext(ctx) // Call PerformanceMetricsByVolume or PerformanceMetricsByFileSystem in gopowermax based on the volume type pmaxClient, err := s.GetPowerMaxClient(symID) if err != nil { @@ -164,6 +166,9 @@ func (s *service) IsIOInProgress(ctx context.Context, volID, symID string) (err log.Errorf("Error %v while checking IsIOInProgress for array having symID %s for volumeID/fileSystemID %s", err.Error(), symID, volID) return fmt.Errorf("error %v while while checking IsIOInProgress", err.Error()) } + if resp == nil || len(resp.ResultList.Result) == 0 { + return fmt.Errorf("no IOInProgress - no performance results returned for FileSystem (NFS) volume %s on array %s", volID, symID) + } // check last four entries status received in the response fileMetrics := resp.ResultList.Result for i := 0; i < len(fileMetrics); i++ { @@ -173,6 +178,9 @@ func (s *service) IsIOInProgress(ctx context.Context, volID, symID string) (err } return fmt.Errorf("no IOInProgress") } + if resp == nil || len(resp.ResultList.Result) == 0 { + return fmt.Errorf("no IOInProgress - no performance results returned for volume %s on array %s", volID, symID) + } // check last four entries status received in the response for i := len(resp.ResultList.Result[0].VolumeResult) - 1; i >= (len(resp.ResultList.Result[0].VolumeResult)-4) && i >= 0; i-- { if resp.ResultList.Result[0].VolumeResult[i].IoRate > 0.0 && checkIfEntryIsLatest(resp.ResultList.Result[0].VolumeResult[i].Timestamp) { diff --git a/service/deletion_worker.go b/service/deletion_worker.go index 887e25b1..cd928c5f 100644 --- a/service/deletion_worker.go +++ b/service/deletion_worker.go @@ -26,7 +26,6 @@ import ( "github.com/dell/csi-powermax/v2/pkg/symmetrix" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" ) // Constants used by deletion worker @@ -367,7 +366,7 @@ func (queue *deletionQueue) cleanupSnapshots(pmaxClient pmax.Pmax) bool { if len(symVol.StorageGroupIDList) == 0 { device.updateStatus(deletionStateDeleteVol, "") } else { - log.Warningf("%s: Unexpected error. Moving back volume to disAssociateSG step", device.print()) + log.Warnf("%s: Unexpected error. Moving back volume to disAssociateSG step", device.print()) device.updateStatus(deletionStateDisAssociateSG, "") } } else { @@ -412,7 +411,7 @@ func (queue *deletionQueue) removeVolumesFromStorageGroup(pmaxClient pmax.Pmax) continue } if sg.NumOfMaskingViews > 0 { - log.Warningf("%s: SG: %s in masking view. Can't proceed with deletion of devices", + log.Warnf("%s: SG: %s in masking view. Can't proceed with deletion of devices", device.print(), storageGroupID) device.updateStatus(device.Status.State, "device is in masking view, can't delete") continue @@ -829,7 +828,7 @@ func (worker *deletionWorker) populateDeletionQueue() { for _, id := range volList { volume, err := pmaxClient.GetVolumeByID(context.Background(), symID, id) if err != nil { - log.Warningf("Could not retrieve details for volume: %s. Ignoring it", id) + log.Warnf("Could not retrieve details for volume: %s. Ignoring it", id) continue } // Put volume on the queue if appropriate @@ -841,7 +840,7 @@ func (worker *deletionWorker) populateDeletionQueue() { } }() } else { - log.Warningf("(Device ID: %s, SymID: %s): skipping as it is not tagged for deletion", + log.Warnf("(Device ID: %s, SymID: %s): skipping as it is not tagged for deletion", volume.VolumeID, symID) } } diff --git a/service/deletion_worker_test.go b/service/deletion_worker_test.go index b70b3449..95e5391b 100644 --- a/service/deletion_worker_test.go +++ b/service/deletion_worker_test.go @@ -25,8 +25,8 @@ import ( "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" ) func TestCaseUnlinkTarget(t *testing.T) { diff --git a/service/envvars.go b/service/envvars.go index 8e446efc..45e46442 100644 --- a/service/envvars.go +++ b/service/envvars.go @@ -174,4 +174,10 @@ const ( // EnvRevProxySecretPath is an env variable that indicates reverseproxy secret path EnvRevProxySecretPath = "X_CSI_REVPROXY_SECRET_FILEPATH" // #nosec 101 + + // EnvDynamicSGEnabled is an env variable which indicates if dynamic SG creation is enabled + EnvDynamicSGEnabled = "X_CSI_DYNAMIC_SG_ENABLED" + + // EnvSGVolumeLimit is an env variable which indicates the configured storage group volume limit + EnvSGVolumeLimit = "X_CSI_STORAGE_GROUP_VOLUME_LIMIT" ) diff --git a/service/features/controller_publish_unpublish.feature b/service/features/controller_publish_unpublish.feature index 50a1cc6a..accd7845 100644 --- a/service/features/controller_publish_unpublish.feature +++ b/service/features/controller_publish_unpublish.feature @@ -19,6 +19,22 @@ Feature: PowerMax CSI interface | "single-node-single-writer" | | "single-node-multi-writer" | + @controllerPublish + @v1.0.0 + Scenario Outline: Publish volume with single writer, enhanced API + Given a PowerMax service + And I call CreateVolumeEnhanced "volume1" + When I request a PortGroup + And a valid CreateVolumeEnhancedResponse is returned + And I have a Node "node1" with MaskingView + And I call PublishVolume with to "node1" + Then a valid PublishVolumeResponse is returned + Examples: + | access | + | "single-writer" | + | "single-node-single-writer" | + | "single-node-multi-writer" | + @controllerPublish @v1.1.0 Scenario Outline: Publish volume with single writer for FC when PG is present @@ -481,7 +497,7 @@ Feature: PowerMax CSI interface And no error was received And I call UnpublishVolume from "node1" And no error was received - Then a valid UnpublishVolumeResponse is returned + Then a valid UnpublishVolumeResponse is returned @controllerPublish @v1.0.0 @@ -608,8 +624,8 @@ Feature: PowerMax CSI interface When I call requestAddVolumeToSGMV mv And I induce error And I call runAddVolumesToSGMV - Then the error contains - + Then the error contains + Examples: | node | mv | induced | errormsg | | "node1" | "default" | "none" | "none" | @@ -627,8 +643,8 @@ Feature: PowerMax CSI interface When I call requestAddVolumeToSGMV mv And I induce error And I call runAddVolumesToSGMV - Then the error contains - + Then the error contains + Examples: | node | mv | induced | errormsg | | "node1" | "default" | "GetHostError" | "Failed to fetch host details" | @@ -646,8 +662,8 @@ Feature: PowerMax CSI interface And I call requestAddVolumeToSGMV mv And I induce error And I call handleAddVolumeToSGMVError - Then the error contains - + Then the error contains + Examples: | node | mv | induced | errormsg | | "node1" | "default" | "none" | "none" | @@ -667,8 +683,8 @@ Feature: PowerMax CSI interface And I call runAddVolumesToSGMV And I induce error And I call handleAddVolumeToSGMVError - Then the error contains - + Then the error contains + Examples: | node | mv1 | mv2 | induced | errormsg | | "node1" | "badmv" | "default" | "none" | "none" | @@ -688,8 +704,8 @@ Feature: PowerMax CSI interface When I call requestRemoveVolumeFromSGMV mv And I induce error And I call runRemoveVolumesFromSGMV - Then the error contains - + Then the error contains + Examples: | node | mv | induced | errormsg | | "node1" | "default" | "none" | "none" | @@ -710,8 +726,8 @@ Feature: PowerMax CSI interface And I call requestRemoveVolumeFromSGMV mv And I induce error And I call handleRemoveVolumeFromSGMVError - Then the error contains - + Then the error contains + Examples: | node | mv | induced | errormsg | | "node1" | "default" | "none" | "none" | @@ -733,8 +749,8 @@ Feature: PowerMax CSI interface And I call runRemoveVolumesFromSGMV And I induce error And I call handleRemoveVolumeFromSGMVError - Then the error contains - + Then the error contains + Examples: | node | mv1 | mv2 | induced | errormsg | | "node1" | "badmv" | "default" | "none" | "none" | diff --git a/service/features/replication.feature b/service/features/replication.feature index c9da114f..feebad9a 100644 --- a/service/features/replication.feature +++ b/service/features/replication.feature @@ -278,11 +278,16 @@ Feature: PowerMax CSI Interface And I induce error When I call RDF enabled CreateVolume "volume2" in namespace "test", mode "ASYNC" and RDFGNo 13 from volume Then the error contains + Examples: - | induced | errormsg | - | "none" | "none" | - | "LinkSnapshotError" | "Failed to create SRDF volume from volume" | - | "MaxSnapSessionError" | "Failed to create SRDF volume from volume" | + | induced | errorMsg | + | none | | + | noVolumeSource | missing source volume ID | + | nonExistentVolume | source volume does not exist | + | invalidVolumeID | invalid volume ID provided | + | wrongCapacity | capacity mismatch error | + | wrongStoragePool | invalid storage pool specified | + @srdf @v2.9.0 @@ -343,10 +348,13 @@ Feature: PowerMax CSI Interface When I call RDF enabled CreateVolume "volume2" in namespace "test", mode "METRO" and RDFGNo 14 from volume Then the error contains Examples: - | induced | errormsg | - | "none" | "none" | - | "LinkSnapshotError" | "Failed to create SRDF volume from volume" | - | "MaxSnapSessionError" | "Failed to create SRDF volume from volume" | + | induced | errorMsg | + | none | | + | noVolumeSource | missing source volume ID | + | nonExistentVolume | source volume does not exist | + | invalidVolumeID | invalid volume ID provided | + | wrongCapacity | capacity mismatch error | + | wrongStoragePool | invalid storage pool specified | @srdf @v2.9.0 diff --git a/service/features/service.feature b/service/features/service.feature index 19b3d708..f28f29b5 100644 --- a/service/features/service.feature +++ b/service/features/service.feature @@ -229,85 +229,6 @@ Feature: PowerMax CSI interface And I call CreateVolume "bad capacity" Then the error contains "bad capacity" -@v1.0.0 - Scenario: Call NodeGetInfo and validate NodeId - Given a PowerMax service - And I add ISCSI array to ProtocolMap - When I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned - -@v1.5.0 - Scenario: Call NodeGetInfo and validate NodeId - Given a PowerMax service - And I add FC array to ProtocolMap - When I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned - -@v2.14.0 - Scenario: Call NodeGetInfo and validate NodeId - Given a PowerMax service - And I add NVME array to ProtocolMap - When I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned -@v2.8.0 - Scenario Outline: Validate NodeGetInfo for different protocols - Given a PowerMax service - And I add to ProtocolMap - And I have a Node "node1" with MaskingView - And arrays are logged in with protocol - When I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned - - Examples: - | protocol | - | "FC" | - | "iSCSI" | - | "NVMe" | - -@v1.0.0 - Scenario: Call NodeGetInfo without setting Node Name - Given a PowerMax service - And I induce error "UnspecifiedNodeName" - When I call NodeGetInfo - Then the error contains "Unable to get Node Name" - -@v2.8.0 - Scenario: Call NodeGetInfo with volume limit - Given a PowerMax service - When I call set attribute MaxVolumesPerNode "852" - And I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned with volume limit "852" - -@v2.8.0 - Scenario: Call NodeGetInfo with invalid volume limit - Given a PowerMax service - When I call NodeGetInfo with invalid volume limit "-10" - Then a valid NodeGetInfoResponse is returned with volume limit "0" - -@v2.8.0 - Scenario: Call NodeGetInfo with volume limit and vsphere enabled - Given a PowerMax service - When I call set attribute MaxVolumesPerNode "12" - And I call set attribute IsVsphereEnabled "true" - And I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned with volume limit "12" - -@v2.8.0 - Scenario: Call NodeGetInfo with volume limit with limits greater than 60 and vsphere enabled - Given a PowerMax service - When I call set attribute MaxVolumesPerNode "852" - And I call set attribute IsVsphereEnabled "true" - And I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned with volume limit "60" - -@v2.8.0 - Scenario: Call NodeGetInfo with volume limit with negative value and vsphere enabled - Given a PowerMax service - When I call set attribute MaxVolumesPerNode "-100" - And I call set attribute IsVsphereEnabled "true" - And I call NodeGetInfo - Then a valid NodeGetInfoResponse is returned with volume limit "60" - @v1.0.0 Scenario: Call GetCapacity with valid Storage Pool Name Given a PowerMax service @@ -551,7 +472,7 @@ Feature: PowerMax CSI interface | induced1 | errormsg | count | # | "GetSymmetrixError" | "Unable to retrieve Array List" | 0 | | "GOISCSIDiscoveryError"| "failed to login" | 0 | - | "none" | "none" | 3 | + | "none" | "none" | 4 | @v1.3.0 Scenario Outline: Validate ensureLoggedIntoEveryArray with CHAP @@ -570,7 +491,7 @@ Feature: PowerMax CSI interface # | "GetSymmetrixError" | "Unable to retrieve Array List" | 0 | | "InduceLoginError" | "failed to login" | 0 | | "InduceSetCHAPError" | "set CHAP induced error" | 0 | - | "none" | "none" | 3 | + | "none" | "none" | 4 | @v1.0.0 Scenario Outline: Test GetVolumeByID function @@ -640,13 +561,13 @@ Feature: PowerMax CSI interface Examples: | induced | volPath | errormsg | | "none" | "" | "Volume path required" | - | "none" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "none" | - | "GOFSInduceGetMountInfoFromDeviceError" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to find mount information" | - | "GOFSInduceDeviceRescanError" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to rescan device" | - | "GOFSInduceResizeMultipathError" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to resize multipath mount device" | - | "GOFSInduceFSTypeError" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to fetch filesystem" | - | "GOFSInduceResizeFSError" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to resize device" | - | "NoVolumeID" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Invalid volume id" | + | "none" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "none" | + | "GOFSInduceGetMountInfoFromDeviceError" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to find mount information" | + | "GOFSInduceDeviceRescanError" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to rescan device" | + | "GOFSInduceResizeMultipathError" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to resize multipath mount device" | + | "GOFSInduceFSTypeError" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to fetch filesystem" | + | "GOFSInduceResizeFSError" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Failed to resize device" | + | "NoVolumeID" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "Invalid volume id" | @v1.4.0 @@ -654,7 +575,7 @@ Feature: PowerMax CSI interface Given a PowerMax service And a valid volume When I invalidate the NodeID - And I call NodeExpandVolume with volumePath as "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" + And I call NodeExpandVolume with volumePath as "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" Then the error contains "Error getting NodeName from the environment" @v1.1.0 @@ -803,10 +724,10 @@ Feature: PowerMax CSI interface Examples: | induced | volPath | errormsg | | "none" | "" | "no Volume path found" | - | "none" | "/var/lib/kubelet/pods/abc-123/volumes/k8.io/pmax-0123/mount" | "none" | - | "GOFSInduceGetMountInfoFromDeviceError" | "/var/lib/kubelet/pods/abc-123/volumes/k8.io/pmax-0123/mount" | "none" | - | "NoVolumeID" | "/var/lib/kubelet/pods/abc-123/volumes/k8.io/pmax-0123/mount" | "Invalid volume id" | - | "NoMountInfo" | "/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "none" | + | "none" | "/tmp/var/lib/kubelet/pods/abc-123/volumes/k8.io/pmax-0123/mount" | "none" | + | "GOFSInduceGetMountInfoFromDeviceError" | "/tmp/var/lib/kubelet/pods/abc-123/volumes/k8.io/pmax-0123/mount" | "none" | + | "NoVolumeID" | "/tmp/var/lib/kubelet/pods/abc-123/volumes/k8.io/pmax-0123/mount" | "Invalid volume id" | + | "NoMountInfo" | "/tmp/var/lib/kubelet/csi/pv/pmax-0123/globalmount" | "none" | @v2.3.0 Scenario Outline: Test valid Topology ReadConfig in BeforeServe diff --git a/service/features/snapshot.feature b/service/features/snapshot.feature index 82129cdf..70494266 100644 --- a/service/features/snapshot.feature +++ b/service/features/snapshot.feature @@ -364,22 +364,6 @@ Feature: PowerMax CSI Interface And an invalid volume And I call Create Volume from Volume Then the error contains "Source volume identifier not in supported format" -@v1.2.0 - Scenario: Create a volume from a volume but receive error - Given a PowerMax service - And I call CreateVolume "volume1" - And a valid CreateVolumeResponse is returned - And I induce error "LinkSnapshotError" - When I call Create Volume from Volume - Then the error contains "Failed to create volume from volume" -@v1.4.0 - Scenario: Create a volume from a volume but receive error - Given a PowerMax service - And I call CreateVolume "volume1" - And a valid CreateVolumeResponse is returned - And I induce error "MaxSnapSessionError" - When I call Create Volume from Volume - Then the error contains "Failed to create volume from volume" @v1.2.0 Scenario: Terminating a snaphot Given a PowerMax service @@ -664,16 +648,6 @@ Feature: PowerMax CSI Interface Then a valid CreateVolumeResponse is returned When I call Create Volume from Volume Then a valid CreateVolumeResponse is returned -@v2.12.0 - Scenario: Create a volume from another volume and catch error message - Given a PowerMax service - And I call CreateVolume "volume1" - And a valid CreateVolumeResponse is returned - When I call Create Volume from Volume - Then a valid CreateVolumeResponse is returned - And I induce error "MaxSnapSessionError" - When I call Create Volume from Volume - Then the error contains "Failed to create volume from volume" @v2.12.0 Scenario: Create a volume from another snapshot Given a PowerMax service diff --git a/service/identity.go b/service/identity.go index a6b020ea..645e553a 100644 --- a/service/identity.go +++ b/service/identity.go @@ -1,5 +1,5 @@ /* - Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. + Copyright © 2021-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,14 +20,11 @@ import ( commonext "github.com/dell/dell-csi-extensions/common" - log "github.com/sirupsen/logrus" - "golang.org/x/net/context" csi "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/protobuf/types/known/wrapperspb" - "github.com/dell/csi-powermax/v2/core" migrext "github.com/dell/dell-csi-extensions/migration" csiext "github.com/dell/dell-csi-extensions/replication" ) @@ -37,9 +34,11 @@ func (s *service) GetPluginInfo( _ *csi.GetPluginInfoRequest) ( *csi.GetPluginInfoResponse, error, ) { + Manifest["semver"] = ManifestSemver + return &csi.GetPluginInfoResponse{ Name: s.getDriverName(), - VendorVersion: core.SemVer, + VendorVersion: ManifestSemver, Manifest: Manifest, }, nil } @@ -83,6 +82,7 @@ func (s *service) Probe( _ *csi.ProbeRequest) ( *csi.ProbeResponse, error, ) { + log := log.WithContext(ctx) log.Debug("Probe called") if !strings.EqualFold(s.mode, "node") { log.Debug("controllerProbe") @@ -119,6 +119,7 @@ func (s *service) ProbeController(ctx context.Context, _ *commonext.ProbeControllerRequest) ( *commonext.ProbeControllerResponse, error, ) { + log := log.WithContext(ctx) if !strings.EqualFold(s.mode, "node") { log.Debug("controllerProbe") if err := s.controllerProbe(ctx); err != nil { @@ -132,7 +133,7 @@ func (s *service) ProbeController(ctx context.Context, rep := new(commonext.ProbeControllerResponse) rep.Ready = ready rep.Name = s.getDriverName() - rep.VendorVersion = core.SemVer + rep.VendorVersion = ManifestSemver rep.Manifest = Manifest log.Debug(fmt.Sprintf("ProbeController returning: %v", rep.Ready.GetValue())) diff --git a/service/identity_test.go b/service/identity_test.go index 1d33e70f..ea13affc 100644 --- a/service/identity_test.go +++ b/service/identity_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "github.com/container-storage-interface/spec/lib/go/csi" commonext "github.com/dell/dell-csi-extensions/common" + "github.com/container-storage-interface/spec/lib/go/csi" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" diff --git a/service/interfaces.go b/service/interfaces.go index c7034fbf..e8614fea 100644 --- a/service/interfaces.go +++ b/service/interfaces.go @@ -20,21 +20,23 @@ import ( "github.com/coreos/go-systemd/v22/dbus" "github.com/dell/gobrick" - log "github.com/sirupsen/logrus" ) type customLogger struct{} func (lg *customLogger) Info(ctx context.Context, format string, args ...interface{}) { - log.WithFields(getLogFields(ctx)).Infof(format, args...) + log := log.WithContext(ctx) + log.Infof(format, args...) } func (lg *customLogger) Debug(ctx context.Context, format string, args ...interface{}) { - log.WithFields(getLogFields(ctx)).Debugf(format, args...) + log := log.WithContext(ctx) + log.Debugf(format, args...) } func (lg *customLogger) Error(ctx context.Context, format string, args ...interface{}) { - log.WithFields(getLogFields(ctx)).Errorf(format, args...) + log := log.WithContext(ctx) + log.Errorf(format, args...) } type iSCSIConnector interface { diff --git a/service/locks.go b/service/locks.go index c75293cd..0af1b705 100644 --- a/service/locks.go +++ b/service/locks.go @@ -18,8 +18,6 @@ import ( "math/rand" "sync" "time" - - log "github.com/sirupsen/logrus" ) type lockWorkers struct{} @@ -68,7 +66,7 @@ func LockRequestHandler() { // ResourceID not present in fifolocks map if request.Unlock { // Invalid unlock request as there is no entry for the resource ID in the fifolocks map - log.Warning("There is no lock to be released!") + log.Warn("There is no lock to be released!") } else { // Create an entry in the fifolocks map as this is the first call for this resource id waitChannels := make(chan LockRequestInfo, 100) @@ -105,13 +103,13 @@ func LockRequestHandler() { } else { // This is a request to lock // Invalid lock request as a lock is already held for the same resource id and request id - log.Warning("Invalid request. There is a lock held with the same request") + log.Warn("Invalid request. There is a lock held with the same request") } } else { // RequestID doesn't match with the CurrentRequestID for the resourceID if request.Unlock { // Attempt to release a lock not held by the caller - log.Warning("You don't hold the lock") + log.Warn("You don't hold the lock") } else { if len(lockInfo.LockRequests) == 0 && (lockInfo.CurrentLockNumber == -1) { // Entry for resource ID already present in fifolocks diff --git a/service/migration.go b/service/migration.go index 31fff40e..8dad825f 100644 --- a/service/migration.go +++ b/service/migration.go @@ -23,10 +23,11 @@ import ( "time" "github.com/dell/csi-powermax/v2/pkg/migration" + "github.com/dell/csmlog" csimgr "github.com/dell/dell-csi-extensions/migration" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" + "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" @@ -34,6 +35,7 @@ import ( ) func (s *service) VolumeMigrate(ctx context.Context, req *csimgr.VolumeMigrateRequest) (*csimgr.VolumeMigrateResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -108,7 +110,7 @@ func (s *service) VolumeMigrate(ctx context.Context, req *csimgr.VolumeMigrateRe } migrateType := req.GetType() - fields := log.Fields{ + fields := csmlog.Fields{ "RequestID": reqID, "SymmetrixID": symID, "RemoteSymID": sourceScParams[path.Join(s.opts.ReplicationPrefix, RemoteSymIDParam)], @@ -158,6 +160,7 @@ func (s *service) VolumeMigrate(ctx context.Context, req *csimgr.VolumeMigrateRe } func nonReplToRepl(ctx context.Context, params map[string]string, _ map[string]string, storageGroupName, applicationPrefix, serviceLevel, storagePoolID, symID string, s *service, vol *types.Volume) error { + log := log.WithContext(ctx) var replicationEnabled string var remoteSymID string var localRDFGrpNo string @@ -241,7 +244,7 @@ func nonReplToRepl(ctx context.Context, params map[string]string, _ map[string]s log.Debugf("RDF: Found Rdf enabled") // remote storage group name is kept same as local storage group name // Check if volume is already added in SG, else add it - log.Debug("StorageGroupName", storageGroupName, "localSGID", localProtectionGroupID, "remoteSGID", remoteProtectionGroupID) + log.Debug("StorageGroupName: " + storageGroupName + " localSGID: " + localProtectionGroupID + " remoteSGID: " + remoteProtectionGroupID) sg, err := pmaxClient.GetStorageGroup(ctx, symID, storageGroupName) if err != nil || sg == nil { log.Debug(fmt.Sprintf("Unable to find storage group: %s", storageGroupName)) @@ -282,6 +285,7 @@ func nonReplToRepl(ctx context.Context, params map[string]string, _ map[string]s } func replToNonRepl(ctx context.Context, params map[string]string, sourceScParams map[string]string, _, _, _, _, symID string, s *service, vol *types.Volume) error { + log := log.WithContext(ctx) pmaxClient, err := s.GetPowerMaxClient(symID) if err != nil { log.Error(err.Error()) @@ -319,6 +323,7 @@ func versionUpgrade(_ context.Context, _ map[string]string, _ map[string]string, } func (s *service) ArrayMigrate(ctx context.Context, req *csimgr.ArrayMigrateRequest) (*csimgr.ArrayMigrateResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { diff --git a/service/migration_test.go b/service/migration_test.go index 3af98598..2a74cfdc 100644 --- a/service/migration_test.go +++ b/service/migration_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. +Copyright © 2025-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,8 +26,8 @@ import ( "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" csimgr "github.com/dell/dell-csi-extensions/migration" types "github.com/dell/gopowermax/v2/types/v100" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" "google.golang.org/grpc/metadata" ) @@ -50,7 +50,7 @@ func TestVolumeMigrate(t *testing.T) { c.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.NextFreeRDFGroup{LocalRdfGroup: []int{42}, RemoteRdfGroup: []int{42}}, nil) c.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), gomock.Any()).AnyTimes().Return(&types.RDFDirList{RdfDirs: []string{}}, nil) c.EXPECT().ExecuteCreateRDFGroup(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) - c.EXPECT().GetProtectedStorageGroup(gomock.Any(), "0001", gomock.Any()).AnyTimes().Return(nil, errors.New("error")) + c.EXPECT().GetProtectedStorageGroup(gomock.Any(), "0001", gomock.Any()).AnyTimes().Return(nil, errors.New("cannot be found")) c.EXPECT().GetStorageGroupIDList(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("error")) symmetrix.Initialize([]string{"0001"}, c) diff --git a/service/mock-data/enhanced-vol.json b/service/mock-data/enhanced-vol.json new file mode 100644 index 00000000..c59f9d7f --- /dev/null +++ b/service/mock-data/enhanced-vol.json @@ -0,0 +1,27 @@ +{ + "volumes": [ + { + "id": "00001", + "type": "TDEV", + "system": { + "id": "000197900048" + }, + "identifier": "Vol00001", + "storage_groups": [ + { + "id": "csi-TST-Optimized-SRP_1-SG" + }, + { + "id": "SG_FCSCSI_DL366064" + } + ], + "num_of_masking_views": 0, + "masking_views": [], + "cap_cyl": 54614, + "srp": { + "id": "SRP_1" + }, + "volume_host_paths": [] + } + ] +} diff --git a/service/mock-data/symmetrix48.json b/service/mock-data/symmetrix48.json new file mode 100644 index 00000000..fcf56147 --- /dev/null +++ b/service/mock-data/symmetrix48.json @@ -0,0 +1,11 @@ +{ + "symmetrixId": "000197900048", + "device_count": 1045, + "ucode": "5978.441.441", + "model": "PowerMax_2000", + "local": true, + "all_flash": true, + "disk_count": 8, + "cache_size_mb": 203776, + "data_encryption": "Disabled" +} diff --git a/service/mock-data/symmetrix86.json b/service/mock-data/symmetrix86.json new file mode 100644 index 00000000..dc5eb9d3 --- /dev/null +++ b/service/mock-data/symmetrix86.json @@ -0,0 +1,11 @@ +{ + "symmetrixId": "000197900086", + "device_count": 1045, + "ucode": "5978.441.441", + "model": "PowerMax_2000", + "local": true, + "all_flash": true, + "disk_count": 8, + "cache_size_mb": 203776, + "data_encryption": "Disabled" +} diff --git a/service/mock-data/symmetrixList.json b/service/mock-data/symmetrixList.json index b4d440d0..37916e40 100644 --- a/service/mock-data/symmetrixList.json +++ b/service/mock-data/symmetrixList.json @@ -1,7 +1,7 @@ { - "symmetrixId": [ - "000197802104", - "000197900046", - "000197900047" - ] + "symmetrixId": [ + "000197802104", + "000197900046", + "000197900047" + ] } diff --git a/service/mount.go b/service/mount.go index 3f6f15bb..1bf691a6 100644 --- a/service/mount.go +++ b/service/mount.go @@ -22,9 +22,9 @@ import ( "strings" "time" - csi "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/dell/csmlog" "github.com/dell/gofsutil" - log "github.com/sirupsen/logrus" + csi "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -65,8 +65,7 @@ func GetDevice(path string) (*Device, error) { if unitTestEmulateBlockDevice { // For unit testing only, emulate a block device on windows dm = dm | os.ModeDevice - } - if dm&os.ModeDevice == 0 { + } else if dm&os.ModeDevice == 0 { // Execute only if not in unit test return nil, fmt.Errorf( "%s is not a block device", path) } @@ -146,7 +145,7 @@ func publishVolume( // MOUNT only =================================================================================================== // Path to mount device to - f := log.Fields{ + f := csmlog.Fields{ "id": id, "volumePath": sysDevice.FullPath, "device": sysDevice.RealDev, @@ -155,6 +154,7 @@ func publishVolume( "privateMount": privTgt, } ctx := context.WithValue(context.Background(), gofsutil.ContextKey("RequestID"), reqID) + log := log.WithContext(ctx) // Check if device is already mounted devMnts, err := getDevMounts(sysDevice) @@ -278,7 +278,7 @@ func publishVolume( rwo = "ro" } if rwo != "" && !contains(m.Opts, rwo) { - log.WithFields(f).Printf("mount %#v rwo %s\n", m, rwo) + log.WithFields(f).Infof("mount %#v rwo %s", m, rwo) return status.Error(codes.Internal, "volume previously published with different mount options") } @@ -322,12 +322,17 @@ func publishVolume( // cleanupPrivateTarget unmounts and removes the private directory for the retry so clean start next time. func cleanupPrivateTarget(reqID, privTgt string) { - log.WithField("CSIRequestID", reqID).WithField("privTgt", privTgt).Info("Cleaning up private target") + fields := map[string]interface{}{ + "CSIRequestID": reqID, + "privTgt": privTgt, + } + log := log.WithFields(fields) + log.Info("Cleaning up private target") if privErr := gofsutil.Unmount(context.Background(), privTgt); privErr != nil { - log.WithField("CSIRequestID", reqID).Printf("Error unmounting privTgt %s: %s", privTgt, privErr) + log.Errorf("Error unmounting privTgt %s: %s", privTgt, privErr) } if privErr := removeWithRetry(privTgt); privErr != nil { - log.WithField("CSIRequestID", reqID).Printf("Error removing privTgt %s: %s", privTgt, privErr) + log.Errorf("Error removing privTgt %s: %s", privTgt, privErr) } } @@ -407,16 +412,14 @@ func mkfile(path string) (bool, error) { if os.IsNotExist(err) { file, err := os.OpenFile(path, os.O_CREATE, 0o600) // #nosec G304 if err != nil { - log.WithField("path", path).WithError( - err).Error("Unable to create file") + log.Errorf("Unable to create file: %s with error: %s", path, err.Error()) } else { file.Close() // #nosec G20 - log.WithField("path", path).Debug("created file") + log.Debugf("created file: %s", path) return true, nil } } else { - log.WithField("path", path).WithError( - err).Error("Unable to create file") + log.Errorf("Unable to create file: %s error: %s", path, err.Error()) } return false, err } @@ -433,15 +436,13 @@ func mkdir(path string) (bool, error) { if err != nil { if os.IsNotExist(err) { if err := os.Mkdir(path, 0o750); err != nil { - log.WithField("dir", path).WithError( - err).Error("Unable to create dir") + log.Errorf("Unable to create directory: %s with error: %s", path, err.Error()) } else { - log.WithField("path", path).Debug("created directory") + log.Debugf("created directory: %s", path) return true, nil } } else { - log.WithField("path", path).WithError( - err).Error("Unable to create dir") + log.Errorf("Unable to create directory: %s error: %s", path, err.Error()) } return false, err } @@ -463,6 +464,7 @@ func unpublishVolume( lastUnmounted := false ctx := context.Background() + log := log.WithContext(ctx) id := req.GetVolumeId() target := req.GetTargetPath() @@ -482,7 +484,7 @@ func unpublishVolume( // Path to mount device to privTgt := getPrivateMountPoint(privDir, id) - f := log.Fields{ + f := csmlog.Fields{ "device": sysDevice.RealDev, "privTgt": privTgt, "CSIRequestID": reqID, @@ -539,6 +541,7 @@ func unmountPrivMount( dev *Device, target string, ) (bool, error) { + log := log.WithContext(ctx) mnts, err := getDevMounts(dev) if err != nil { return false, err @@ -568,7 +571,7 @@ func unmountPrivMount( } } - log.WithField("directory", target).Debug("removing directory") + log.Debugf("Removing directory: %s", target) err := removeWithRetry(target) if err != nil { log.Error("error removing private mount target: " + err.Error()) diff --git a/service/node.go b/service/node.go index 8f1bb7b9..d5bd2dd1 100644 --- a/service/node.go +++ b/service/node.go @@ -28,6 +28,7 @@ import ( "sync" "time" + "github.com/dell/csmlog" "github.com/dell/gonvme" "github.com/dell/csi-powermax/v2/pkg/file" @@ -36,13 +37,12 @@ import ( pmax "github.com/dell/gopowermax/v2" - "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/coreos/go-systemd/v22/dbus" "github.com/dell/gobrick" csictx "github.com/dell/gocsi/context" gofsutil "github.com/dell/gofsutil" "github.com/dell/goiscsi" - log "github.com/sirupsen/logrus" + "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/coreos/go-systemd/v22/dbus" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" @@ -118,6 +118,7 @@ func (s *service) NodeStageVolume( req *csi.NodeStageVolumeRequest) ( *csi.NodeStageVolumeResponse, error, ) { + log := log.WithContext(ctx) privTgt := req.GetStagingTargetPath() if privTgt == "" { return nil, status.Error(codes.InvalidArgument, "Target Path is required") @@ -199,7 +200,7 @@ func (s *service) NodeStageVolume( targetIdentifiers = publishContext[PortIdentifiers] } - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "DeviceID": devID, "ID": req.VolumeId, @@ -245,7 +246,7 @@ func (s *service) NodeStageVolume( log.Error("Remote device WWN required to be in PublishContext") return nil, status.Error(codes.InvalidArgument, "Remote device WWN required to be in PublishContext") } - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "DeviceID": remoteVolID, "ID": req.VolumeId, @@ -292,7 +293,7 @@ func (s *service) NodeStageVolume( f["RemoteWWN"] = remDeviceWWN } - log.WithFields(f).WithField("devPath", devicePath).Info("NodeStageVolume completed") + log.WithFields(f).Infof("NodeStageVolume completed for devicePath: %s", devicePath) return &csi.NodeStageVolumeResponse{}, nil } @@ -322,6 +323,7 @@ type NVMeTCPTargetInfo struct { } func (s *service) connectDevice(ctx context.Context, data publishContextData) (string, error) { + log := log.WithContext(ctx) logFields := getLogFields(ctx) var err error // The volumeLUNAddress is hex. @@ -415,6 +417,7 @@ func (s *service) connectRDMDevice(ctx context.Context, func (s *service) connectNVMeTCPDevice(ctx context.Context, data publishContextData, ) (gobrick.Device, error) { + log := log.WithContext(ctx) logFields := getLogFields(ctx) var targets []gobrick.NVMeTargetInfo for _, t := range data.nvmetcpTargets { @@ -440,6 +443,7 @@ func (s *service) NodeUnstageVolume( req *csi.NodeUnstageVolumeRequest) ( *csi.NodeUnstageVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -603,7 +607,7 @@ func (s *service) disconnectVolume(reqID, symID, devID, volumeWWN string) error var err error symlinkPath, _, _ := gofsutil.WWNToDevicePathX(context.Background(), volumeWWN) for i := 1; i <= maxDisconnectRetries; i++ { - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "DeviceID": devID, "Retry": i, @@ -638,7 +642,7 @@ func (s *service) disconnectVolume(reqID, symID, devID, volumeWWN string) error } devicePathComponents := strings.Split(devicePath, "/") deviceName = devicePathComponents[len(devicePathComponents)-1] - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "DeviceID": devID, "DeviceName": deviceName, @@ -689,6 +693,7 @@ func (s *service) NodePublishVolume( req *csi.NodePublishVolumeRequest) ( *csi.NodePublishVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -756,7 +761,7 @@ func (s *service) NodePublishVolume( } else { targetIdentifiers = publishContext[PortIdentifiers] } - log.WithField("CSIRequestID", reqID).Infof("node publishing volume: %s lun: %s", deviceWWN, volumeLUNAddress) + log.Infof("node publishing volume: %s lun: %s", deviceWWN, volumeLUNAddress) var symlinkPath string var devicePath string @@ -776,7 +781,7 @@ func (s *service) NodePublishVolume( } } - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "DeviceID": devID, "DevicePath": devicePath, @@ -803,6 +808,7 @@ func (s *service) NodeUnpublishVolume( req *csi.NodeUnpublishVolumeRequest) ( *csi.NodeUnpublishVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string var err error headers, ok := metadata.FromIncomingContext(ctx) @@ -846,7 +852,7 @@ func (s *service) NodeUnpublishVolume( // Get the VolumeID and parse it id := req.GetVolumeId() - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "DevicePath": devicePath, "ID": id, @@ -917,6 +923,7 @@ func (s *service) getTargetMount(target string) (gofsutil.Info, error) { } func (s *service) nodeProbe(ctx context.Context) error { + log := log.WithContext(ctx) log.Debug("Entering nodeProbe") defer log.Debug("Exiting nodeProbe") if s.opts.NodeName == "" { @@ -943,6 +950,7 @@ func (s *service) nodeProbe(ctx context.Context) error { } func (s *service) nodeProbeBySymID(ctx context.Context, symID string) error { + log := log.WithContext(ctx) log.Debugf("Entering nodeProbeBySymID for array %s", symID) defer log.Debugf("Exiting nodeProbe for array %s", symID) @@ -1154,6 +1162,7 @@ func (s *service) reachableEndPoint(endpoint string) bool { } func (s *service) createTopologyMap(ctx context.Context, nodeName string) map[string]string { + log := log.WithContext(ctx) topology := map[string]string{} iscsiArrays := make([]string, 0) nvmeTCPArrays := make([]string, 0) @@ -1337,6 +1346,7 @@ func (s *service) NodeGetInfo( _ *csi.NodeGetInfoRequest) ( *csi.NodeGetInfoResponse, error, ) { + log := log.WithContext(ctx) // Get the Node ID if s.opts.NodeName == "" { log.Error("Unable to get Node Name from the environment") @@ -1403,6 +1413,7 @@ func (s *service) NodeGetInfo( func (s *service) NodeGetVolumeStats( ctx context.Context, req *csi.NodeGetVolumeStatsRequest, ) (*csi.NodeGetVolumeStatsResponse, error) { + log := log.WithContext(ctx) var reqID string headers, ok := metadata.FromIncomingContext(ctx) if ok { @@ -1432,7 +1443,7 @@ func (s *service) NodeGetVolumeStats( return nil, err } - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "VolumePath": volPath, "ID": id, @@ -1466,7 +1477,7 @@ func (s *service) NodeGetVolumeStats( // remove the namespace from the volName as the mount paths will not have it volName = strings.Join(strings.Split(volName, "-")[:2], "-") isMounted, err := isVolumeMounted(ctx, volName, volPath) - log.Debug("---- isMounted ----", isMounted) + log.Debugf("---- isMounted ---- %t", isMounted) if err != nil { abnormal = true msg = fmt.Sprintf("Error getting mount info for volume %s", id) @@ -1581,6 +1592,7 @@ func isVolumeMounted(ctx context.Context, volName string, target string) (bool, // // returns an error if unable to perform node startup tasks without error func (s *service) nodeStartup(ctx context.Context) error { + log := log.WithContext(ctx) if s.nodeIsInitialized { return nil } @@ -1689,11 +1701,12 @@ func isValidHostID(hostID string) bool { // It returns the number of initiators for the host that were found. // Do not mix both FC and iSCSI initiators in a single call. func (s *service) verifyAndUpdateInitiatorsInADiffHost(ctx context.Context, symID string, nodeInitiators []string, hostID string, pmaxClient pmax.Pmax) ([]string, error) { + log := log.WithContext(ctx) validInitiators := make([]string, 0) var errormsg string initList, err := pmaxClient.GetInitiatorList(ctx, symID, "", false, false) if err != nil { - log.Warning("Failed to fetch initiator list for the SYM :" + symID) + log.Warn("Failed to fetch initiator list for the SYM :" + symID) return validInitiators, err } hostUpdated := false @@ -1707,7 +1720,7 @@ func (s *service) verifyAndUpdateInitiatorsInADiffHost(ctx context.Context, symI log.Infof("Checking initiator %s against host %s", initiatorID, hostID) initiator, err := pmaxClient.GetInitiatorByID(ctx, symID, initiatorID) if err != nil { - log.Warning("Failed to fetch initiator details for initiator: " + initiatorID) + log.Warn("Failed to fetch initiator details for initiator: " + initiatorID) continue } if initiator.Host != "" { @@ -1719,20 +1732,20 @@ func (s *service) verifyAndUpdateInitiatorsInADiffHost(ctx context.Context, symI _, err := pmaxClient.UpdateHostName(ctx, symID, initiator.Host, hostID) if err != nil { errormsg = fmt.Sprintf("Failed to change host name from %s to %s: %s", initiator.Host, hostID, err) - log.Warning(errormsg) + log.Warn(errormsg) continue } hostUpdated = true } else { errormsg = fmt.Sprintf("Skipping Updating Host %s for initiator: %s as updated host already present on: %s", initiator.Host, initiatorID, symID) - log.Warning(errormsg) + log.Warn(errormsg) continue } } else { errormsg = fmt.Sprintf("initiator: %s is already a part of a different host: %s on: %s", initiatorID, initiator.Host, symID) - log.Warning(errormsg) + log.Warn(errormsg) continue } } @@ -1769,6 +1782,7 @@ func (s *service) verifyAndUpdateInitiatorsInADiffHost(ctx context.Context, symI func (s *service) nodeHostSetup(ctx context.Context, portWWNs []string, IQNs []string, NQNs []string, symmetrixIDs []string) error { s.mutex.Lock() defer s.mutex.Unlock() + log := log.WithContext(ctx) log.Info("**************************nodeHostSetup executing...*******************************") defer log.Info("**************************nodeHostSetup completed...*******************************") @@ -1799,7 +1813,7 @@ func (s *service) nodeHostSetup(ctx context.Context, portWWNs []string, IQNs []s if s.opts.IsVsphereEnabled { err := s.getHostForVsphere(ctx, symID, pmaxClient) if err != nil { - log.Warningf("Host/HostGroup %s was not initialized on sym %s, err: %s", s.opts.VSphereHostName, symID, err.Error()) + log.Warnf("Host/HostGroup %s was not initialized on sym %s, err: %s", s.opts.VSphereHostName, symID, err.Error()) } else { s.arrayTransportProtocolMap[symID] = Vsphere } @@ -1913,6 +1927,7 @@ func (s *service) getHostForVsphere(ctx context.Context, array string, pmaxClien } func (s *service) setupArrayForFC(ctx context.Context, array string, portWWNs []string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) hostName, _, mvName := s.GetFCHostSGAndMVIDFromNodeID(s.opts.NodeName) log.Infof("setting up array %s for Fibrechannel, host name: %s masking view: %s", array, hostName, mvName) _, err := s.createOrUpdateFCHost(ctx, array, hostName, portWWNs, pmaxClient) @@ -1921,6 +1936,7 @@ func (s *service) setupArrayForFC(ctx context.Context, array string, portWWNs [] // setupArrayForIscsi is called to set up a node for iscsi operation. func (s *service) setupArrayForIscsi(ctx context.Context, array string, IQNs []string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) hostName, _, mvName := s.GetISCSIHostSGAndMVIDFromNodeID(s.opts.NodeName) log.Infof("setting up array %s for Iscsi, host name: %s masking view ID: %s %v", array, hostName, mvName, IQNs) @@ -1932,15 +1948,16 @@ func (s *service) setupArrayForIscsi(ctx context.Context, array string, IQNs []s } _, err = s.getAndConfigureMaskingViewTargets(ctx, array, mvName, IQNs, pmaxClient) if err != nil && !(strings.Contains(err.Error(), "Masking View") && strings.Contains(err.Error(), "cannot be found")) { + log.Error(err.Error()) return err } - log.Warning(err) return nil } // setupArrayForIscsi is called to set up a node for iscsi operation. func (s *service) setupArrayForNVMeTCP(ctx context.Context, array string, NQNs []string, pmaxClient pmax.Pmax) error { hostName, _, mvName := s.GetNVMETCPHostSGAndMVIDFromNodeID(s.opts.NodeName) + log := log.WithContext(ctx) log.Infof("setting up array %s for NVMeTCP, host name: %s masking view ID: %s %v", array, hostName, mvName, NQNs) // Discover targets on the host @@ -1964,13 +1981,14 @@ func (s *service) setupArrayForNVMeTCP(ctx context.Context, array string, NQNs [ // Create or update the NVMe Host and Initiators _, err = s.getAndConfigureMaskingViewTargetsNVMeTCP(ctx, array, mvName, pmaxClient) if err != nil && !(strings.Contains(err.Error(), "Masking View") && strings.Contains(err.Error(), "cannot be found")) { + log.Warn(err.Error()) return err } - log.Warning(err) return nil } func (s *service) updateNQNWithHostID(ctx context.Context, symID string, NQNs []string, pmaxClient pmax.Pmax) ([]string, error) { + log := log.WithContext(ctx) updatesHostNQNs := make([]string, 0) // Process the NQN to append hostId hostInitiators, err := pmaxClient.GetInitiatorList(ctx, symID, "", false, false) @@ -2004,6 +2022,7 @@ func (s *service) updateNQNWithHostID(ctx context.Context, symID string, NQNs [] // getAndConfigureMaskingViewTargets - Returns a list of ISCSITargets for a given masking view // also update the node database with CHAP authentication (if required) and perform discovery/login func (s *service) getAndConfigureMaskingViewTargets(ctx context.Context, array, mvName string, IQNs []string, pmaxClient pmax.Pmax) ([]goiscsi.ISCSITarget, error) { + log := log.WithContext(ctx) // Check the masking view goISCSITargets := make([]goiscsi.ISCSITarget, 0) view, err := pmaxClient.GetMaskingViewByID(ctx, array, mvName) @@ -2040,6 +2059,7 @@ func (s *service) getAndConfigureMaskingViewTargets(ctx context.Context, array, // getAndConfigureMaskingViewTargets - Returns a list of NVMeTargets for a given masking view // also update the node database with CHAP authentication (if required) and perform discovery/login func (s *service) getAndConfigureMaskingViewTargetsNVMeTCP(ctx context.Context, array, mvName string, pmaxClient pmax.Pmax) ([]gonvme.NVMeTarget, error) { + log := log.WithContext(ctx) // Check the masking view goNVMeTargets := make([]gonvme.NVMeTarget, 0) view, err := pmaxClient.GetMaskingViewByID(ctx, array, mvName) @@ -2069,6 +2089,7 @@ func (s *service) getAndConfigureMaskingViewTargetsNVMeTCP(ctx context.Context, // setupNVMeTCPTargetDiscovery is called to discover NVMe targets from the host/node func (s *service) setupNVMeTCPTargetDiscovery(ctx context.Context, array string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) var combinedErrors []string atLeastOneLoggedIn := false var totalTargetsDiscovered int @@ -2112,8 +2133,8 @@ func (s *service) loginIntoISCSITargets(array string, targets []maskingViewTarge var combinedErrors []string atLeastOneLoggedIn := false var totalTargetsDiscovered int - var err error + var err error for _, tgt := range targets { if s.opts.EnableCHAP { err = s.iscsiClient.PerformLogin(tgt.target) @@ -2158,11 +2179,12 @@ func (s *service) GetLoggedInArrays(array string) (isLoggedIn bool, ok bool) { return isLoggedIn, ok } -// loginIntoNVMeTargets - for a given array id and list of masking view targets +// loginIntoNVMeTCPTargets - for a given array id and list of masking view targets // also update the logged in arrays cache func (s *service) loginIntoNVMeTCPTargets(array string, targets []maskingViewNVMeTargetInfo) error { - var err error - loggedInAll := true + var combinedErrors []string + atLeastOneLoggedIn := false + var totalTargetsDiscovered int for _, tgt := range targets { // Attempt target discovery from host log.Debugf("Discovering NVMe targets on %s", tgt.target.Portal) @@ -2170,8 +2192,8 @@ func (s *service) loginIntoNVMeTCPTargets(array string, targets []maskingViewNVM if discoveryError != nil { log.Errorf("Failed to discover the NVMe target: %s. Error: %s", tgt.target.PortID, discoveryError.Error()) - err = discoveryError - loggedInAll = false + combinedErrors = append(combinedErrors, fmt.Sprintf("target: %s, Error: %s", + tgt.target.Portal, discoveryError.Error())) } else { nvmeTgts, ok := s.nvmeTargets.Load(array) if !ok { @@ -2180,14 +2202,20 @@ func (s *service) loginIntoNVMeTCPTargets(array string, targets []maskingViewNVM s.nvmeTargets.Store(array, append(nvmeTgts.([]string), tgt.target.TargetNqn)) log.Infof("Successfully logged into target: %s on portal :%s", tgt.target.PortID, tgt.target.Portal) + atLeastOneLoggedIn = true + totalTargetsDiscovered++ } } - // If we successfully logged into all targets, then marked the array as logged in - if loggedInAll { + // If we successfully logged into at least one target, then mark the array as logged in + if atLeastOneLoggedIn { s.UpdateLoggedInNVMeArrays(array, true) + } else { + return fmt.Errorf("failed to discover NVMe targets on any of the portals. Errors: %s", + strings.Join(combinedErrors, "; ")) } - return err + log.Infof("Discovered %d NVMe targets out of %d on array %s", totalTargetsDiscovered, len(targets), array) + return nil } func (s *service) UpdateLoggedInNVMeArrays(array string, value bool) { @@ -2224,7 +2252,7 @@ func (s *service) setCHAPCredentials(array string, targets []maskingViewTargetIn log.Debugf("Setting CHAP credentials for targets: %v", targets) err := s.iscsiClient.SetCHAPCredentials(targets[i].target, chapUserName, s.opts.CHAPPassword) if err != nil { - log.Error(err) + log.Error(err.Error()) // If we were able to set credentials for some targets successfully // even then we won't be updating the cache return err @@ -2301,6 +2329,7 @@ func (s *service) ensureISCSIDaemonStarted() error { } func (s *service) ensureLoggedIntoEveryArray(ctx context.Context, _ bool) error { + log := log.WithContext(ctx) arrays := &types.SymmetrixIDList{} // Get the list of arrays @@ -2356,6 +2385,7 @@ func (s *service) ensureLoggedIntoEveryArray(ctx context.Context, _ bool) error } func (s *service) performNVMETCPLoginOnSymID(ctx context.Context, array string, mvName string, pmaxClient pmax.Pmax) (err error) { + log := log.WithContext(ctx) mvTargets, ok := symToMaskingViewTargets.Load(array) if ok { err = s.loginIntoNVMeTCPTargets(array, mvTargets.([]maskingViewNVMeTargetInfo)) @@ -2382,6 +2412,7 @@ func (s *service) performNVMETCPLoginOnSymID(ctx context.Context, array string, } func (s *service) performIscsiLoginOnSymID(ctx context.Context, array string, IQNs []string, mvName string, pmaxClient pmax.Pmax) (err error) { + log := log.WithContext(ctx) // Try to get the masking view targets from the cache mvTargets, ok := symToMaskingViewTargets.Load(array) if ok { @@ -2497,6 +2528,7 @@ func (s *service) getNVMeTCPTargetsForMaskingView(ctx context.Context, array str } func (s *service) createOrUpdateFCHost(ctx context.Context, array string, nodeName string, portWWNs []string, pmaxClient pmax.Pmax) (*types.Host, error) { + log := log.WithContext(ctx) log.Info(fmt.Sprintf("Processing FC Host array: %s, nodeName: %s, initiators: %v", array, nodeName, portWWNs)) if array == "" { return &types.Host{}, fmt.Errorf("createOrUpdateHost: No array specified") @@ -2551,6 +2583,7 @@ func (s *service) createOrUpdateFCHost(ctx context.Context, array string, nodeNa } func (s *service) createOrUpdateIscsiHost(ctx context.Context, array string, nodeName string, IQNs []string, pmaxClient pmax.Pmax) (*types.Host, error) { + log := log.WithContext(ctx) log.Debug(fmt.Sprintf("Processing Iscsi Host array: %s, nodeName: %s, initiators: %v", array, nodeName, IQNs)) if array == "" { return &types.Host{}, fmt.Errorf("createOrUpdateHost: No array specified") @@ -2586,6 +2619,7 @@ func (s *service) createOrUpdateIscsiHost(ctx context.Context, array string, nod } func (s *service) createOrUpdateNVMeTCPHost(ctx context.Context, array string, nodeName string, NQNs []string, pmaxClient pmax.Pmax) (*types.Host, error) { + log := log.WithContext(ctx) log.Debug(fmt.Sprintf("Processing NVMeTCP Host array: %s, nodeName: %s, initiators: %v", array, nodeName, NQNs)) if array == "" { return &types.Host{}, fmt.Errorf("createOrUpdateHost: No array specified") @@ -2624,6 +2658,7 @@ func (s *service) createOrUpdateNVMeTCPHost(ctx context.Context, array string, n // retryableCreateHost func (s *service) retryableCreateHost(ctx context.Context, array string, nodeName string, hostInitiators []string, _ *types.HostFlags, pmaxClient pmax.Pmax) (*types.Host, error) { + log := log.WithContext(ctx) var err error var host *types.Host deadline := time.Now().Add(time.Duration(s.GetPmaxTimeoutSeconds()) * time.Second) @@ -2652,6 +2687,7 @@ func (s *service) retryableCreateHost(ctx context.Context, array string, nodeNam // retryableUpdateHostInitiators wraps UpdateHostInitiators in a retry loop func (s *service) retryableUpdateHostInitiators(ctx context.Context, array string, host *types.Host, initiators []string, pmaxClient pmax.Pmax) (*types.Host, error) { + log := log.WithContext(ctx) var err error var updatedHost *types.Host deadline := time.Now().Add(time.Duration(s.GetPmaxTimeoutSeconds()) * time.Second) @@ -2686,6 +2722,7 @@ func (s *service) NodeExpandVolume( req *csi.NodeExpandVolumeRequest) ( *csi.NodeExpandVolumeResponse, error, ) { + log := log.WithContext(ctx) var reqID string var err error headers, ok := metadata.FromIncomingContext(ctx) @@ -2814,7 +2851,7 @@ func (s *service) NodeExpandVolume( size := req.GetCapacityRange().GetRequiredBytes() - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "VolumeName": volName, "VolumePath": volumePath, @@ -2873,6 +2910,7 @@ func (s *service) NodeExpandVolume( // Gets the iscsi target iqn values that can be used for rescanning. func (s *service) getISCSITargets(ctx context.Context, symID string, pmaxClient pmax.Pmax) ([]ISCSITargetInfo, error) { + log := log.WithContext(ctx) var targets []ISCSITargetInfo var ips interface{} var ok bool @@ -2904,6 +2942,7 @@ func (s *service) getISCSITargets(ctx context.Context, symID string, pmaxClient // Gets the iscsi target iqn values that can be used for rescanning. func (s *service) getNVMeTCPTargets(ctx context.Context, symID string, pmaxClient pmax.Pmax) ([]NVMeTCPTargetInfo, error) { + log := log.WithContext(ctx) var targets []NVMeTCPTargetInfo var ips interface{} var ok bool @@ -2938,6 +2977,7 @@ func (s *service) getNVMeTCPTargets(ctx context.Context, symID string, pmaxClien // If the target is ISCSI, it also updates ISCSI node database if CHAP // authentication was requested func (s *service) getArrayTargets(ctx context.Context, targetIdentifiers string, symID string, pmaxClient pmax.Pmax) ([]ISCSITargetInfo, []FCTargetInfo, []NVMeTCPTargetInfo, bool, bool) { + log := log.WithContext(ctx) iscsiTargets := make([]ISCSITargetInfo, 0) fcTargets := make([]FCTargetInfo, 0) nvmeTargets := make([]NVMeTCPTargetInfo, 0) @@ -2975,6 +3015,7 @@ func (s *service) getArrayTargets(ctx context.Context, targetIdentifiers string, } func (s *service) getAndConfigureArrayNVMeTCPTargets(ctx context.Context, arrayTargets []string, symID string, pmaxClient pmax.Pmax) []NVMeTCPTargetInfo { + log := log.WithContext(ctx) log.Debugf("Entering getAndConfigureArrayNVMeTCPTargets for symID: %s, arrayTargets: %+v", symID, arrayTargets) nvmetcpTargets := make([]NVMeTCPTargetInfo, 0) @@ -3048,6 +3089,7 @@ func (s *service) getAndConfigureArrayNVMeTCPTargets(ctx context.Context, arrayT log.Infof("There is no cached info, build it") // There is no cached information _, _, mvName := s.GetNVMETCPHostSGAndMVIDFromNodeID(s.opts.NodeName) + log.Debugf("mvName: %s", mvName) // Get the Masking View Targets and configure CHAP if required // This call updates the cache as well @@ -3078,6 +3120,7 @@ func (s *service) getAndConfigureArrayNVMeTCPTargets(ctx context.Context, arrayT } func (s *service) getAndConfigureArrayISCSITargets(ctx context.Context, arrayTargets []string, symID string, pmaxClient pmax.Pmax) []ISCSITargetInfo { + log := log.WithContext(ctx) iscsiTargets := make([]ISCSITargetInfo, 0) allTargets, _ := s.getISCSITargets(ctx, symID, pmaxClient) IQNs, err := s.iscsiClient.GetInitiators("") @@ -3158,6 +3201,9 @@ func (s *service) getAndConfigureArrayISCSITargets(ctx context.Context, arrayTar } // There is no cached information _, _, mvName := s.GetISCSIHostSGAndMVIDFromNodeID(s.opts.NodeName) + + log.Debugf("mvName: %s", mvName) + // Get the Masking View Targets and configure CHAP if required // This call updates the cache as well goISCSITargets, err := s.getAndConfigureMaskingViewTargets(ctx, symID, mvName, IQNs, pmaxClient) diff --git a/service/node_connectivity_checker.go b/service/node_connectivity_checker.go index 0a4396e6..c9844658 100644 --- a/service/node_connectivity_checker.go +++ b/service/node_connectivity_checker.go @@ -26,8 +26,8 @@ import ( "sync" "time" + "github.com/dell/csmlog" "github.com/gorilla/mux" - log "github.com/sirupsen/logrus" ) func (s *service) newProbeStatus() { @@ -38,6 +38,7 @@ func (s *service) newProbeStatus() { // startAPIService reads nodes to array status periodically func (s *service) startAPIService(ctx context.Context) { + log := log.WithContext(ctx) if !s.opts.IsPodmonEnabled { log.Info("podmon is not enabled") return @@ -49,6 +50,7 @@ func (s *service) startAPIService(ctx context.Context) { // apiRouter serves http requests func (s *service) apiRouter(_ context.Context) { + log := csmlog.GetLogger() log.Infof("starting http server on port %s", s.opts.PodmonPort) // create a new mux router router := mux.NewRouter() @@ -151,6 +153,7 @@ func (s *service) getArrayConnectivityStatus(w http.ResponseWriter, r *http.Requ // startNodeToArrayConnectivityCheck starts connectivityTest as one goroutine for each array func (s *service) startNodeToArrayConnectivityCheck(ctx context.Context) { + log := log.WithContext(ctx) log.Debug("startNodeToArrayConnectivityCheck called") s.probeStatus = new(sync.Map) pMaxArrays := s.retryableGetSymmetrixIDList() @@ -164,6 +167,7 @@ func (s *service) startNodeToArrayConnectivityCheck(ctx context.Context) { // testConnectivityAndUpdateStatus runs probe to test connectivity from node to array // updates probeStatus map[array]ArrayConnectivityStatus func (s *service) testConnectivityAndUpdateStatus(ctx context.Context, symID string, timeout time.Duration) { + log := log.WithContext(ctx) defer func() { if err := recover(); err != nil { log.Errorf("panic occurred in testConnectivityAndUpdateStatus: %s", err) diff --git a/service/node_test.go b/service/node_test.go index 8020c409..dd6aae54 100644 --- a/service/node_test.go +++ b/service/node_test.go @@ -26,19 +26,19 @@ import ( "sync" "testing" - "github.com/container-storage-interface/spec/lib/go/csi" "github.com/dell/csi-powermax/v2/k8smock" "github.com/dell/csi-powermax/v2/pkg/symmetrix" "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" "github.com/dell/gofsutil" "github.com/dell/goiscsi" + "github.com/container-storage-interface/spec/lib/go/csi" gonvme "github.com/dell/gonvme" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - gomock "github.com/golang/mock/gomock" + "github.com/golang/mock/gomock" + gmock "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - gmock "go.uber.org/mock/gomock" ) func TestGetNVMeTCPTargets(t *testing.T) { @@ -605,7 +605,7 @@ func TestGetAndConfigureISCSITargets(t *testing.T) { }, }, nil) c.EXPECT().GetMaskingViewByID(gmock.All(), "array1", "csi-mv--").AnyTimes().Return(&types.MaskingView{ - MaskingViewID: "csi-mv--", + MaskingViewID: "csi-mv", PortGroupID: "portgroup1", }, nil) diff --git a/service/pending.go b/service/pending.go index ff291eae..fc4ba10b 100644 --- a/service/pending.go +++ b/service/pending.go @@ -18,7 +18,6 @@ import ( "sync" "time" - log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/service/replication.go b/service/replication.go index 0689ce4b..5d424975 100644 --- a/service/replication.go +++ b/service/replication.go @@ -22,7 +22,6 @@ import ( pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -40,6 +39,7 @@ const ( QueryAsync = "Asynchronous" QuerySync = "Synchronous" QueryMetro = "Active" + DefaultRDF = 1 ) func getQueryMode(mode string) string { @@ -94,6 +94,7 @@ func LocalRDFPortsNotAdded(createRDFPayload *types.RDFGroupCreate, localSymID st // 5. Choose all Ports associated with the remoteSite Provided // 6. Use all the details above to create a RDFg func (s *service) GetOrCreateRDFGroup(ctx context.Context, localSymID string, remoteSymID string, repMode string, namespace string, pmaxClient pmax.Pmax) (string, string, error) { + log := log.WithContext(ctx) createRDFgPayload := new(types.RDFGroupCreate) proceedWithCreate := false rdfLabel := "" @@ -107,36 +108,57 @@ func (s *service) GetOrCreateRDFGroup(ctx context.Context, localSymID string, re QueryRemSymID: remoteSymID, }) if err != nil { - log.Errorf("Failed to fetch RDF pre existing group, Error (%s)", err.Error()) - return "", "", err + if strings.Contains(err.Error(), "No SRDF Groups found for Array") { + log.Warnf("Failed to find any RDF groups on array: %s, will attempt to create", localSymID) + } else { + log.Errorf("Failed to fetch RDF pre existing group, Error (%s)", err.Error()) + return "", "", err + } } - for _, rDFGID := range rDFGList.RDFGroupIDs { - if strings.Compare(rdfLabel, rDFGID.Label) == 0 { - log.Debugf("found pre existing label for given array pair and RDF mode: %+v", rDFGID) - rDFG, err := pmaxClient.GetRDFGroupByID(ctx, localSymID, strconv.Itoa(rDFGID.RDFGNumber)) - if err != nil { - log.Errorf("Failed to fetch RDF pre existing group, Error (%s)", err.Error()) - return "", "", err + if rDFGList != nil { + for _, rDFGID := range rDFGList.RDFGroupIDs { + if strings.Compare(rdfLabel, rDFGID.Label) == 0 { + log.Debugf("found pre existing label for given array pair and RDF mode: %+v", rDFGID) + rDFG, err := pmaxClient.GetRDFGroupByID(ctx, localSymID, strconv.Itoa(rDFGID.RDFGNumber)) + if err != nil { + log.Errorf("Failed to fetch RDF pre existing group, Error (%s)", err.Error()) + return "", "", err + } + log.Debugf("found pre-existing RDF group with label: %s", rDFGID.Label) + return strconv.Itoa(rDFG.RdfgNumber), strconv.Itoa(rDFG.RemoteRdfgNumber), nil } - log.Debugf("found pre-existing RDF group with label: %s", rDFGID.Label) - return strconv.Itoa(rDFG.RdfgNumber), strconv.Itoa(rDFG.RemoteRdfgNumber), nil } } // Create new RDFG pair, no pre-existing pair for symIDs and rep mode nextFreeRDFG, err := pmaxClient.GetFreeLocalAndRemoteRDFg(ctx, localSymID, "") + localRDFG := 0 + remoteRDFG := 0 if err != nil { - log.Error(fmt.Sprintf("Failed to fetch free RDF groups, Error (%s)", err.Error())) - return "", "", err + if strings.Contains(err.Error(), "No SRDF Groups found for Array") { + log.Warnf("Failed to fetch free RDF groups on array: %s, will use default group number: %d", localSymID, DefaultRDF) + localRDFG = DefaultRDF + } else { + log.Error(fmt.Sprintf("Failed to fetch free RDF groups, Error (%s)", err.Error())) + return "", "", err + } + } else { + localRDFG = nextFreeRDFG.LocalRdfGroup[0] } - localRDFG := nextFreeRDFG.LocalRdfGroup[0] + // Create new RDFG pair, no pre-existing pair for symIDs and rep mode nextFreeRDFG, err = pmaxClient.GetFreeLocalAndRemoteRDFg(ctx, remoteSymID, "") if err != nil { - log.Error(fmt.Sprintf("Failed to fetch free RDF groups, Error (%s)", err.Error())) - return "", "", err + if strings.Contains(err.Error(), "No SRDF Groups found for Array") { + log.Warnf("Failed to fetch free RDF groups on array: %s, will use default group number: %d", remoteSymID, DefaultRDF) + remoteRDFG = DefaultRDF + } else { + log.Error(fmt.Sprintf("Failed to fetch free RDF groups, Error (%s)", err.Error())) + return "", "", err + } + } else { + remoteRDFG = nextFreeRDFG.LocalRdfGroup[0] } - remoteRDFG := nextFreeRDFG.LocalRdfGroup[0] log.Infof("Fetched Local RDFg:(%d), remote RDFg:(%d)", localRDFG, remoteRDFG) // We are only bothered about ONLINE RDF dirs, so get only those @@ -210,6 +232,7 @@ func (s *service) GetOrCreateRDFGroup(ctx context.Context, localSymID string, re // GetRDFDevicePairInfo returns the RDF informtaion of a volume func (s *service) GetRDFDevicePairInfo(ctx context.Context, symID, rdfGrpNo, localVolID string, pmaxClient pmax.Pmax) (*types.RDFDevicePair, error) { + log := log.WithContext(ctx) rdfPair, err := pmaxClient.GetRDFDevicePairInfo(ctx, symID, rdfGrpNo, localVolID) if err != nil { log.Error(fmt.Sprintf("Failed to fetch rdf pair information for (%s) - Error (%s)", localVolID, err.Error())) @@ -221,6 +244,7 @@ func (s *service) GetRDFDevicePairInfo(ctx context.Context, symID, rdfGrpNo, loc // ProtectStorageGroup protects a local SG based on the given RDF Information // This will create a remote storage group, RDF pairs and add the volumes in their respective SG func (s *service) ProtectStorageGroup(ctx context.Context, symID, remoteSymID, storageGroupName, remoteStorageGroupName, remoteServiceLevel, rdfGrpNo, rdfMode, localVolID, reqID string, bias bool, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) lockHandle := fmt.Sprintf("%s%s", storageGroupName, symID) lockNum := RequestLock(lockHandle, reqID) defer ReleaseLock(lockHandle, reqID, lockNum) @@ -263,6 +287,7 @@ func (s *service) ProtectStorageGroup(ctx context.Context, symID, remoteSymID, s // As CreateSGReplica needs that no storage group should be present on remote sym. // So we delete the remote SG only if it has zero volumes. func (s *service) verifyAndDeleteRemoteStorageGroup(ctx context.Context, remoteSymID, remoteStorageGroupName string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) sg, err := pmaxClient.GetStorageGroup(ctx, remoteSymID, remoteStorageGroupName) if err != nil || sg == nil { log.Debug("Can not found Remote storage group, proceed") @@ -292,6 +317,7 @@ func (s *service) GetRemoteVolumeID(ctx context.Context, symID, rdfGrpNo, localV // VerifyProtectedGroupDirection returns the direction of protected SG func (s *service) VerifyProtectedGroupDirection(ctx context.Context, symID, localProtectionGroupID, localRdfGrpNo string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) sgRDFInfo, err := pmaxClient.GetStorageGroupRDFInfo(ctx, symID, localProtectionGroupID, localRdfGrpNo) if err != nil { log.Errorf("GetStorageGroupRDFInfo failed:(%s)", err.Error()) @@ -311,7 +337,7 @@ func GetRDFInfoFromSGID(storageGroupID string) (namespace string, rDFGno string, compLength := len(sgComponents) if compLength < 5 || !strings.Contains(storageGroupID, CsiRepSGPrefix) { err = fmt.Errorf("The protected SGID %s is not formed correctly", storageGroupID) - return + return namespace, rDFGno, repMode, err } if compLength > 5 { // this is ASYNC/SYNC rep sg, it will have namespace @@ -319,7 +345,7 @@ func GetRDFInfoFromSGID(storageGroupID string) (namespace string, rDFGno string, } rDFGno = sgComponents[compLength-2] repMode = sgComponents[compLength-1] - return + return namespace, rDFGno, repMode, err } func isValidStateForFailOver(state string) bool { @@ -365,6 +391,7 @@ func getStateAndSRDFPersonality(psg *types.StorageGroupRDFG) (string, bool, bool func (s *service) Failover(ctx context.Context, symID, sgName, rdfGrpNo string, pmaxClient pmax.Pmax, toLocal, unplanned, withoutSwap bool, ) (bool, *types.StorageGroupRDFG, error) { + log := log.WithContext(ctx) psg, err := pmaxClient.GetStorageGroupRDFInfo(ctx, symID, sgName, rdfGrpNo) if err != nil { errorMsg := fmt.Sprintf("Failed to fetch replication state for SG (%s) - Error (%s)", sgName, err.Error()) @@ -400,7 +427,7 @@ func (s *service) Failover(ctx context.Context, symID, sgName, rdfGrpNo string, return false, nil, nil } else if state == FailedOver { // Idempotent operation, return success - log.Warningf("SG Name: %s, state: %s already in the desired state", sgName, state) + log.Warnf("SG Name: %s, state: %s already in the desired state", sgName, state) return true, psg, nil } // We try a best effort failover & if it doesn't succeed, then return a failed precondition @@ -435,12 +462,12 @@ func (s *service) Failover(ctx context.Context, symID, sgName, rdfGrpNo string, if state == Consistent { // Already reprotected at site // log a warning but don't throw an error - log.Warningf("SG name: %s, state: %s, volumes already protected at the desired site. Nothing to do here", + log.Warnf("SG name: %s, state: %s, volumes already protected at the desired site. Nothing to do here", sgName, state) return true, psg, nil } else if state == Suspended { // idempotent call - log.Warningf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) + log.Warnf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) return true, psg, nil } // We don't know what to do here @@ -488,7 +515,7 @@ func (s *service) Failover(ctx context.Context, symID, sgName, rdfGrpNo string, } else { if (isR1 && toLocal) || (!isR1 && !toLocal) { // Nothing to do here - log.Warningf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) + log.Warnf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) return true, psg, nil } err = pmaxClient.ExecuteReplicationActionOnSG(ctx, symID, FailOver, sgName, rdfGrpNo, true, true, false) @@ -507,6 +534,7 @@ func (s *service) Failover(ctx context.Context, symID, sgName, rdfGrpNo string, func (s *service) Failback(ctx context.Context, symID, sgName, rdfGrpNo string, pmaxClient pmax.Pmax, toLocal bool, ) (bool, *types.StorageGroupRDFG, error) { + log := log.WithContext(ctx) psg, err := pmaxClient.GetStorageGroupRDFInfo(ctx, symID, sgName, rdfGrpNo) if err != nil { errorMsg := fmt.Sprintf("Failed to fetch replication state for SG (%s) - Error (%s)", sgName, err.Error()) @@ -533,12 +561,12 @@ func (s *service) Failback(ctx context.Context, symID, sgName, rdfGrpNo string, // Both these scenarios are valid if state == Consistent || state == Synchronized { // Already in desired state - log.Warningf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) + log.Warnf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) return true, psg, nil } if state != FailedOver { // Log a warning and do a best effort failback - log.Warningf("SG name: %s, state: %s, incorrect state for performing failback as it may fail", sgName, state) + log.Warnf("SG name: %s, state: %s, incorrect state for performing failback as it may fail", sgName, state) } err := pmaxClient.ExecuteReplicationActionOnSG(ctx, symID, FailBack, sgName, rdfGrpNo, false, true, false) if err != nil { @@ -567,6 +595,7 @@ func (s *service) Failback(ctx context.Context, symID, sgName, rdfGrpNo string, func (s *service) Reprotect(ctx context.Context, symID, sgName, rdfGrpNo string, pmaxClient pmax.Pmax, toLocal bool, ) (bool, *types.StorageGroupRDFG, error) { + log := log.WithContext(ctx) psg, err := pmaxClient.GetStorageGroupRDFInfo(ctx, symID, sgName, rdfGrpNo) if err != nil { errorMsg := fmt.Sprintf("Failed to fetch replication state for SG (%s) - Error (%s)", sgName, err.Error()) @@ -593,13 +622,13 @@ func (s *service) Reprotect(ctx context.Context, symID, sgName, rdfGrpNo string, // These are the valid states for running a reprotect if state == Consistent || state == Synchronized { // Nothing to do, idempotent call - log.Warningf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) + log.Warnf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) return true, psg, nil } if state == Suspended || state == Split { log.Infof("SG name: %s, state: %s, Attempting to resume replication", sgName, state) } else { - log.Warningf("SG name: %s, state: %s, incorrect state for performing Reprotect as it may fail", + log.Warnf("SG name: %s, state: %s, incorrect state for performing Reprotect as it may fail", sgName, state) } err := pmaxClient.ExecuteReplicationActionOnSG(ctx, symID, Resume, sgName, rdfGrpNo, @@ -630,6 +659,7 @@ func (s *service) Reprotect(ctx context.Context, symID, sgName, rdfGrpNo string, func (s *service) Swap(ctx context.Context, symID, sgName, rdfGrpNo string, pmaxClient pmax.Pmax, toLocal bool, ) (bool, *types.StorageGroupRDFG, error) { + log := log.WithContext(ctx) psg, err := pmaxClient.GetStorageGroupRDFInfo(ctx, symID, sgName, rdfGrpNo) if err != nil { errorMsg := fmt.Sprintf("Failed to fetch replication state for SG (%s) - Error (%s)", sgName, err.Error()) @@ -669,7 +699,7 @@ func (s *service) Swap(ctx context.Context, symID, sgName, rdfGrpNo string, pmax // Swap & R1 // if state is suspended, then it is an idempotent call if state == Suspended { - log.Warningf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) + log.Warnf("SG name: %s, state: %s, idempotent operation. Nothing to do here", sgName, state) return true, psg, nil } // Any other state, we don't know what to do @@ -683,6 +713,7 @@ func (s *service) Swap(ctx context.Context, symID, sgName, rdfGrpNo string, pmax } func suspend(ctx context.Context, symID, sgName, rdfGrpNo string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) inDesiredState, err := validateRDFState(ctx, symID, Suspend, sgName, rdfGrpNo, pmaxClient) if err != nil { return err @@ -704,6 +735,7 @@ func (s *service) Suspend(ctx context.Context, symID, sgName, rdfGrpNo string, p } func establish(ctx context.Context, symID, sgName, rdfGrpNo string, bias bool, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) inDesiredState, err := validateRDFState(ctx, symID, Establish, sgName, rdfGrpNo, pmaxClient) if err != nil { return err @@ -726,6 +758,7 @@ func (s *service) Establish(ctx context.Context, symID, sgName, rdfGrpNo string, // Resume validates current state of replication & executes 'Resume' on storage group replication link func (s *service) Resume(ctx context.Context, symID, sgName, rdfGrpNo string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) inDesiredState, err := validateRDFState(ctx, symID, Resume, sgName, rdfGrpNo, pmaxClient) if err != nil { return err @@ -743,6 +776,7 @@ func (s *service) Resume(ctx context.Context, symID, sgName, rdfGrpNo string, pm // ValidateRDFState checks if the given action is permissible on the protected storage group based on its current state func validateRDFState(ctx context.Context, symID, action, sgName, rdfGrpNo string, pmaxClient pmax.Pmax) (bool, error) { + log := log.WithContext(ctx) // validate appropriateness of current link state to the action psg, err := pmaxClient.GetStorageGroupRDFInfo(ctx, symID, sgName, rdfGrpNo) if err != nil { @@ -781,6 +815,7 @@ func validateRDFState(ctx context.Context, symID, action, sgName, rdfGrpNo strin } func (s *service) addVolumesToProtectedStorageGroup(ctx context.Context, reqID, symID, localProtectionGroupID, remoteSymID, remoteProtectionGroupID string, force bool, volID string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) lockHandle := fmt.Sprintf("%s%s", localProtectionGroupID, symID) lockNum := RequestLock(lockHandle, reqID) defer ReleaseLock(lockHandle, reqID, lockNum) diff --git a/service/replication_test.go b/service/replication_test.go index 427096a0..e5f151e5 100644 --- a/service/replication_test.go +++ b/service/replication_test.go @@ -16,13 +16,17 @@ package service import ( "errors" + "fmt" + "reflect" "testing" + "github.com/dell/csi-powermax/v2/pkg/symmetrix" + "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "go.uber.org/mock/gomock" "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -173,3 +177,326 @@ func TestGetQueryMode(t *testing.T) { }) } } + +func TestService_GetOrCreateRDFGroup(t *testing.T) { + tests := []struct { + name string + localSymID string + remoteSymID string + repMode string + namespace string + pmaxClient *mocks.MockPmaxClient + expectedLocalRDFG string + expectedRemoteRDFG string + expectedErr error + initializeArray string + }{ + { + name: "success_return_existing_matching_srdf_group", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(&types.RDFGroupList{ + RDFGroupCount: 2, + RDFGroupIDs: []types.RDFGroupIDL{ + { + RDFGNumber: 2, + Label: "CPns1", + RemoteSymID: "remote-sym-id", + GroupType: "Dynamic", + }, + { + RDFGNumber: 3, + Label: "CPns2", + RemoteSymID: "remote-sym-id", + GroupType: "Dynamic", + }, + }, + }, nil) + + mockPmaxClient.EXPECT().GetRDFGroupByID(gomock.Any(), "local-sym-id", "2").Return(&types.RDFGroup{ + RdfgNumber: 2, + RemoteRdfgNumber: 20, + }, nil) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "2", + expectedRemoteRDFG: "20", + expectedErr: nil, + }, + { + name: "eror_getting_existing_srdf_group", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("error getting RDF group list")) + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting RDF group list"), + }, + { + name: "error_when_GetRDFGroupByID_called", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(&types.RDFGroupList{ + RDFGroupCount: 2, + RDFGroupIDs: []types.RDFGroupIDL{ + { + RDFGNumber: 2, + Label: "CPns1", + RemoteSymID: "remote-sym-id", + GroupType: "Dynamic", + }, + }, + }, nil) + + mockPmaxClient.EXPECT().GetRDFGroupByID(gomock.Any(), "local-sym-id", "2").Return(nil, errors.New("error getting RDFGroup by ID")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting RDFGroup by ID"), + }, + { + name: "success_no_SRDF_groups_found_for_array_returns_default", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + + mockPmaxClient.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), "local-sym-id").Return(&types.RDFDirList{RdfDirs: []string{"OR-1C"}}, nil) + mockPmaxClient.EXPECT().GetLocalOnlineRDFPorts(gomock.Any(), "OR-1C", "local-sym-id").Return(&types.RDFPortList{RdfPorts: []string{}}, nil) + mockPmaxClient.EXPECT().ExecuteCreateRDFGroup(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "1", + expectedRemoteRDFG: "1", + expectedErr: nil, + }, + { + name: "error_when_GetFreeLocalAndRemoteRDFg_called_for_local_sym", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("error getting local and remote RDFg for local sym")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting local and remote RDFg for local sym"), + }, + { + name: "error_when_GetFreeLocalAndRemoteRDFg_called_for_remote_sym", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(nil, errors.New("error getting local and remote RDFg for remote sym")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting local and remote RDFg for remote sym"), + }, + { + name: "error_getting_local_ONLINE_RDF_Directors", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + + mockPmaxClient.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), "local-sym-id").Return(nil, errors.New("error getting local ONLINE RDF Directors")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting local ONLINE RDF Directors"), + }, + { + name: "error_getting_local_ONLINE_RDF_Ports", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + + mockPmaxClient.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), "local-sym-id").Return(&types.RDFDirList{RdfDirs: []string{"OR-1C"}}, nil) + mockPmaxClient.EXPECT().GetLocalOnlineRDFPorts(gomock.Any(), "OR-1C", "local-sym-id").Return(nil, errors.New("error getting local ONLINE RDF ports")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting local ONLINE RDF ports"), + }, + { + name: "error_getting_remote_RDF_ports", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + + mockPmaxClient.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), "local-sym-id").Return(&types.RDFDirList{RdfDirs: []string{"OR-1C"}}, nil) + mockPmaxClient.EXPECT().GetLocalOnlineRDFPorts(gomock.Any(), "OR-1C", "local-sym-id").Return(&types.RDFPortList{RdfPorts: []string{"3"}}, nil) + mockPmaxClient.EXPECT().GetRemoteRDFPortOnSAN(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error getting remote RDF ports")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error getting remote RDF ports"), + }, + { + name: "error_when_rdf_label_length_is_greater_than_10", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "namespace_with_a_long_name", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("rdfLabel: CPnamespace_with_a_long_name for mode: ASYNC exceeds 10 char limit, rename the namespace within 7 char or use pre-existing RDFG via storage class"), + }, + { + name: "error_during_create_rdf_group", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(nil, errors.New("No SRDF Groups found for Array")) + + mockPmaxClient.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), "local-sym-id").Return(&types.RDFDirList{RdfDirs: []string{"OR-1C"}}, nil) + mockPmaxClient.EXPECT().GetLocalOnlineRDFPorts(gomock.Any(), "OR-1C", "local-sym-id").Return(&types.RDFPortList{RdfPorts: []string{}}, nil) + mockPmaxClient.EXPECT().ExecuteCreateRDFGroup(gomock.Any(), "local-sym-id", gomock.Any()).Return(errors.New("error during create rdf group")) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "", + expectedRemoteRDFG: "", + expectedErr: fmt.Errorf("error during create rdf group"), + }, + { + name: "success_no_SRDF_groups_found_for_array_but_free_group_nums_exists", + localSymID: "local-sym-id", + remoteSymID: "remote-sym-id", + repMode: "ASYNC", + namespace: "ns1", + pmaxClient: func() *mocks.MockPmaxClient { + mockPmaxClient := mocks.NewMockPmaxClient(gomock.NewController(t)) + mockPmaxClient.EXPECT().GetRDFGroupList(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil, errors.New("No SRDF Groups found for Array")) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "local-sym-id", "").Return(&types.NextFreeRDFGroup{ + LocalRdfGroup: []int{10}, + }, nil) + mockPmaxClient.EXPECT().GetFreeLocalAndRemoteRDFg(gomock.Any(), "remote-sym-id", "").Return(&types.NextFreeRDFGroup{ + LocalRdfGroup: []int{2}, + }, nil) + + mockPmaxClient.EXPECT().GetLocalOnlineRDFDirs(gomock.Any(), "local-sym-id").Return(&types.RDFDirList{RdfDirs: []string{"OR-1C"}}, nil) + mockPmaxClient.EXPECT().GetLocalOnlineRDFPorts(gomock.Any(), "OR-1C", "local-sym-id").Return(&types.RDFPortList{RdfPorts: []string{"4"}}, nil) + mockPmaxClient.EXPECT().GetRemoteRDFPortOnSAN(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return( + &types.RemoteRDFPortDetails{ + RemotePorts: []types.RDFPortDetails{ + { + SymmID: "remote-sym-id", + }, + }, + }, nil) + + mockPmaxClient.EXPECT().GetLocalRDFPortDetails(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.RDFPortDetails{}, nil) + mockPmaxClient.EXPECT().ExecuteCreateRDFGroup(gomock.Any(), "local-sym-id", gomock.Any()).Return(nil) + + return mockPmaxClient + }(), + initializeArray: "local-sym-id", + expectedLocalRDFG: "10", + expectedRemoteRDFG: "2", + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &service{ + opts: Opts{ + ClusterPrefix: "CP", + }, + } + _ = symmetrix.Initialize([]string{tt.initializeArray}, tt.pmaxClient) + defer symmetrix.RemoveClient(tt.initializeArray) + + localRDFG, remoteRDFG, err := s.GetOrCreateRDFGroup(context.Background(), tt.localSymID, tt.remoteSymID, tt.repMode, tt.namespace, tt.pmaxClient) + if localRDFG != tt.expectedLocalRDFG { + t.Errorf("GetOrCreateRDFGroup() localRDFG = %v, want %v", localRDFG, tt.expectedLocalRDFG) + } + if remoteRDFG != tt.expectedRemoteRDFG { + t.Errorf("GetOrCreateRDFGroup() remoteRDFG = %v, want %v", remoteRDFG, tt.expectedRemoteRDFG) + } + if !reflect.DeepEqual(err, tt.expectedErr) { + t.Errorf("GetOrCreateRDFGroup() error = %v, want %v", err, tt.expectedErr) + } + }) + } +} diff --git a/service/service.go b/service/service.go index 1d660680..1855ed92 100644 --- a/service/service.go +++ b/service/service.go @@ -1,5 +1,5 @@ /* - Copyright © 2021-2025 Dell Inc. or its subsidiaries. All Rights Reserved. + Copyright © 2021-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,24 +26,25 @@ import ( "sync" "time" + "github.com/dell/csmlog" "github.com/dell/gonvme" "github.com/dell/csi-powermax/v2/k8sutils" "github.com/dell/dell-csi-extensions/podmon" "github.com/fsnotify/fsnotify" + "github.com/sirupsen/logrus" "github.com/spf13/viper" "github.com/dell/csi-powermax/v2/pkg/symmetrix" "google.golang.org/grpc" - "github.com/container-storage-interface/spec/lib/go/csi" "github.com/dell/gocsi" csictx "github.com/dell/gocsi/context" "github.com/dell/goiscsi" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" + "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -72,22 +73,29 @@ const ( PortGroups = "X_CSI_POWERMAX_PORTGROUPS" Protocol = "X_CSI_TRANSPORT_PROTOCOL" // PmaxEndPoint = "X_CSI_POWERMAX_ENDPOINT" - ManagedArrays = "X_CSI_MANAGED_ARRAYS" - defaultCertFile = "tls.crt" + ManagedArrays = "X_CSI_MANAGED_ARRAYS" + defaultCertFile = "tls.crt" + defaultSgVolumeLimit = 4000 ) type contextKey string // specific string type used for context keys var inducedMockReverseProxy bool // for testing only +// Update when the manifest version changes. +var ManifestSemver string + // Manifest is the SP's manifest. var Manifest = map[string]string{ - "url": "http://github.com/dell/csi-powermax", - "semver": core.SemVer, - "commit": core.CommitSha32, + "semver": ManifestSemver, "formed": core.CommitTime.Format(time.RFC1123), } +var ( + log = csmlog.GetLogger() + sgVolumeLimit = defaultSgVolumeLimit +) + // Service is the CSI Mock service provider. type Service interface { csi.ControllerServer @@ -147,6 +155,8 @@ type Opts struct { PodmonPollingFreq string // indicates the polling frequency to check array connectivity TLSCertDir string StorageArrays map[string]StorageArrayConfig + dynamicSGEnabled bool + sgVolumeLimit int } // StorageArrayConfig represents the configuration of a storage array in the config file @@ -239,20 +249,21 @@ func updateDriverConfigParams(v *viper.Viper) { if v.IsSet(CSILogFormatParam) && logFormatFromConfig != "" { log.Infof("Read CSI_LOG_FORMAT: %s from configuration file", logFormatFromConfig) } - var formatter log.Formatter + var formatter logrus.Formatter + // Use text logger as default - formatter = &log.TextFormatter{ + formatter = &logrus.TextFormatter{ DisableColors: true, FullTimestamp: true, } if strings.EqualFold(logFormatFromConfig, "json") { - formatter = &log.JSONFormatter{ + formatter = &logrus.JSONFormatter{ TimestampFormat: time.RFC3339Nano, } } else if !strings.EqualFold(logFormatFromConfig, "text") && (logFormatFromConfig != "") { - log.Warningf("Unsupported CSI_LOG_FORMAT: %s supplied. Defaulting to text", logFormatFromConfig) + log.Warnf("Unsupported CSI_LOG_FORMAT: %s supplied. Defaulting to text", logFormatFromConfig) } - level := log.DebugLevel // Use debug as default + level := csmlog.DebugLevel // Use debug as default if v.IsSet(CSILogLevelParam) { logLevel := v.GetString(CSILogLevelParam) if logLevel != "" { @@ -260,23 +271,23 @@ func updateDriverConfigParams(v *viper.Viper) { log.Infof("Read CSI_LOG_LEVEL: %s from config file", logLevel) var err error - l, err := log.ParseLevel(logLevel) + l, err := csmlog.ParseLevel(logLevel) if err != nil { - log.WithError(err).Errorf("CSI_LOG_LEVEL %s value not recognized, error: %s, Setting to default: %s", + log.Errorf("CSI_LOG_LEVEL %s value not recognized, error: %s, Setting to default: %s", logLevel, err.Error(), level) } else { level = l } } } else { - log.Warning("Couldn't read CSI_LOG_LEVEL from config file. Using debug level as default") + log.Warn("Couldn't read CSI_LOG_LEVEL from config file. Using debug level as default") } setLogFormatAndLevel(formatter, level) // set X_CSI_LOG_LEVEL so that gocsi doesn't overwrite the loglevel set by us _ = os.Setenv(gocsi.EnvVarLogLevel, level.String()) } -func setLogFormatAndLevel(logFormat log.Formatter, level log.Level) { +func setLogFormatAndLevel(logFormat logrus.Formatter, level csmlog.Level) { log.SetFormatter(logFormat) log.Infof("Setting log level to %v", level) log.SetLevel(level) @@ -293,13 +304,13 @@ func setLogFormatAndLevel(logFormat log.Formatter, level log.Level) { // Otherwise, it processes each storage array, extracting labels and parameters. func GetStorageArrays(secretParams *viper.Viper, opts *Opts) { if secretParams.Get("storagearrays") == nil { - log.Println("No storage arrays declared.") + log.Info("No storage arrays declared.") return } storageArrays := secretParams.Get("storagearrays").([]interface{}) if len(storageArrays) == 0 { - log.Println("No storage array declared.") + log.Info("No storage array declared.") } else { for _, storageArray := range storageArrays { storageArrayMap := storageArray.(map[string]interface{}) @@ -322,6 +333,7 @@ func GetStorageArrays(secretParams *viper.Viper, opts *Opts) { func (s *service) BeforeServe( ctx context.Context, _ *gocsi.StoragePlugin, _ net.Listener, ) error { + log := log.WithContext(ctx) defer func() { fields := map[string]interface{}{ "endpoint": s.opts.Endpoint, @@ -377,7 +389,7 @@ func (s *service) BeforeServe( configFilePath, ok := csictx.LookupEnv(ctx, EnvConfigFilePath) if !ok { - log.Warningf("Unable to read X_CSI_POWERMAX_CONFIG_PATH from env. Continuing with default values") + log.Warnf("Unable to read X_CSI_POWERMAX_CONFIG_PATH from env. Continuing with default values") } paramsViper := viper.New() @@ -387,19 +399,19 @@ func (s *service) BeforeServe( err := paramsViper.ReadInConfig() // if unable to read configuration file, set defaults if err != nil { - log.WithError(err).Error("unable to read config file") - setLogFormatAndLevel(&log.TextFormatter{ + log.Errorf("Unable to read config file: %v", err) + setLogFormatAndLevel(&logrus.TextFormatter{ DisableColors: true, FullTimestamp: true, - }, log.DebugLevel) + }, csmlog.DebugLevel) // set X_CSI_LOG_LEVEL so that gocsi doesn't overwrite the loglevel set by us - _ = os.Setenv(gocsi.EnvVarLogLevel, log.DebugLevel.String()) + _ = os.Setenv(gocsi.EnvVarLogLevel, csmlog.DebugLevel.String()) } else { updateDriverConfigParams(paramsViper) } paramsViper.WatchConfig() paramsViper.OnConfigChange(func(e fsnotify.Event) { - log.Println("Received event for config file change:", e.Name) + log.Info("Received event for config file change: " + e.Name) updateDriverConfigParams(paramsViper) }) @@ -415,11 +427,11 @@ func (s *service) BeforeServe( // Reading Topology filters from the config file topoConfigFilePath, ok := csictx.LookupEnv(ctx, EnvTopoConfigFilePath) if !ok { - log.Warningf("Unable to read X_CSI_POWERMAX_TOPOLOGY_CONFIG_PATH from env. Continuing with default topology keys") + log.Warnf("Unable to read X_CSI_POWERMAX_TOPOLOGY_CONFIG_PATH from env. Continuing with default topology keys") } else { s.topologyConfig, err = ReadConfig(topoConfigFilePath) if err != nil { - log.Warningf("continuing with default topology keys") + log.Warnf("continuing with default topology keys") } else { log.Debug("processing topology config map") s.ParseConfig() @@ -471,7 +483,7 @@ func (s *service) BeforeServe( opts.User = User opts.Password = Password } else { - log.Println("No management servers found.") + log.Info("No management servers found.") } opts.StorageArrays = make(map[string]StorageArrayConfig) @@ -527,7 +539,7 @@ func (s *service) BeforeServe( if MaxVolumesPerNode, ok := csictx.LookupEnv(ctx, EnvMaxVolumesPerNode); ok { val, err := strconv.ParseInt(MaxVolumesPerNode, 10, 64) if err != nil { - log.Warningf("error while parsing env variable '%s', %s, defaulting to 0", EnvMaxVolumesPerNode, err) + log.Warnf("error while parsing env variable '%s', %s, defaulting to 0", EnvMaxVolumesPerNode, err) opts.MaxVolumesPerNode = 0 } else { opts.MaxVolumesPerNode = val @@ -585,8 +597,7 @@ func (s *service) BeforeServe( if v, ok := csictx.LookupEnv(ctx, n); ok { b, err := strconv.ParseBool(v) if err != nil { - log.WithField(n, v).Debug( - "invalid boolean value. defaulting to false") + log.Debugf("invalid boolean value (%s) for %s. defaulting to false", v, n) return false } return b @@ -702,6 +713,23 @@ func (s *service) BeforeServe( } } + if dynamicSGEnabled, ok := csictx.LookupEnv(ctx, EnvDynamicSGEnabled); ok && dynamicSGEnabled == "true" { + s.opts.dynamicSGEnabled = true + } + log.Infof("Dynamic SG enabled: %v", s.opts.dynamicSGEnabled) + + if s.opts.dynamicSGEnabled && s.isController() { + SgVolLimitStr, ok := csictx.LookupEnv(ctx, EnvSGVolumeLimit) + if ok { + sgVolLimit, err := strconv.Atoi(SgVolLimitStr) + if err != nil { + log.Errorf("unable to parse %s as duration: %s", EnvSGVolumeLimit, err.Error()) + } else { + sgVolumeLimit = sgVolLimit + } + } + log.Infof("%s set to %v", EnvSGVolumeLimit, sgVolumeLimit) + } return nil } @@ -736,7 +764,7 @@ func readNodesRules(connections []NodeConfig) map[string][]string { array = "" } if len(arrayHW) < 2 { - log.Warningf("incorrect config for %s skipping rule (%s)", nodeName, rule) + log.Warnf("incorrect config for %s skipping rule (%s)", nodeName, rule) continue } hws := strings.Split(arrayHW[1], "/") @@ -761,13 +789,13 @@ func ReadConfig(configPath string) (*TopologyConfig, error) { err := topoViper.ReadInConfig() // if unable to read configuration file, set defaults if err != nil { - log.WithError(err).Error("unable to read topology config file") + log.Errorf("unable to read topology config file: %s", err.Error()) return nil, err } var config TopologyConfig err = topoViper.Unmarshal(&config) if err != nil { - log.WithError(err).Error("unable to unmarshal topology config") + log.Errorf("unable to unmarshal topology config: %s", err.Error()) return nil, err } return &config, nil @@ -798,7 +826,7 @@ func (s *service) getProxySettingsFromEnv() (string, string, bool) { if sp, ok := csictx.LookupEnv(context.Background(), servicePortEnv); ok { servicePort = sp if serviceHost == "" || servicePort == "" { - log.Warning("Either ServiceHost and ServicePort is set to empty") + log.Warn("Either ServiceHost and ServicePort is set to empty") return "", "", false } return serviceHost, servicePort, true @@ -851,6 +879,7 @@ func (s *service) parseCommaSeperatedList(values string) ([]string, error) { } func (s *service) createPowerMaxClients(ctx context.Context) error { + log := log.WithContext(ctx) s.mutex.Lock() defer s.mutex.Unlock() endPoint := "" @@ -862,7 +891,7 @@ func (s *service) createPowerMaxClients(ctx context.Context) error { // Create our PowerMax API client, if needed if s.adminClient == nil { - applicationName := ApplicationName + "/" + "v" + core.SemVer + applicationName := ApplicationName + "/" + "v" + ManifestSemver tlsCertFile := filepath.Join(s.opts.TLSCertDir, defaultCertFile) c, err := pmax.NewClientWithArgs(endPoint, applicationName, s.opts.Insecure, !s.opts.DisableCerts, tlsCertFile) if err != nil { @@ -927,6 +956,14 @@ func (s *service) getCSIVolume(vol *types.Volume) *csi.Volume { return vi } +func (s *service) buildCSIVolume(vol *types.VolumeEnhanced) *csi.Volume { + vi := &csi.Volume{ + VolumeId: vol.ID, + CapacityBytes: int64(vol.CapCyl) * cylinderSizeInBytes, + } + return vi +} + func (s *service) getClusterPrefix() string { return s.opts.ClusterPrefix } @@ -938,17 +975,17 @@ func (s *service) getDriverName() string { return s.opts.DriverName } -func setLogFields(ctx context.Context, fields log.Fields) context.Context { +func setLogFields(ctx context.Context, fields csmlog.Fields) context.Context { if ctx == nil { ctx = context.Background() } return context.WithValue(ctx, contextKey(logFields), fields) } -func getLogFields(ctx context.Context) log.Fields { - fields, ok := ctx.Value(contextKey(logFields)).(log.Fields) +func getLogFields(ctx context.Context) csmlog.Fields { + fields, ok := ctx.Value(contextKey(logFields)).(csmlog.Fields) if !ok { - fields = log.Fields{} + fields = csmlog.Fields{} } csiReqID, ok := ctx.Value(csictx.RequestIDKey).(string) @@ -962,6 +999,7 @@ func getLogFields(ctx context.Context) log.Fields { // SetPollingFrequency reads the pollingFrequency from Env, sets default vale if ENV not found func (s *service) SetPollingFrequency(ctx context.Context) int64 { + log := log.WithContext(ctx) var pollingFrequency int64 s.pollingFrequencyMutex.Lock() defer s.pollingFrequencyMutex.Unlock() @@ -985,6 +1023,7 @@ func (s *service) GetPollingFrequency() int64 { } func setArrayConfigEnvs(ctx context.Context) error { + log := log.WithContext(ctx) log.Info("---------Inside setArrayConfigEnvs function----------") // set additional driver configs moved from envs. configFilePath, ok := csictx.LookupEnv(ctx, EnvArrayConfigPath) @@ -997,20 +1036,20 @@ func setArrayConfigEnvs(ctx context.Context) error { err := paramsViper.ReadInConfig() // if unable to read configuration file, set defaults if err != nil { - log.WithError(err).Error("unable to read array config file") - setLogFormatAndLevel(&log.TextFormatter{ + log.Errorf("unable to read array config file: %s", err.Error()) + setLogFormatAndLevel(&logrus.TextFormatter{ DisableColors: true, FullTimestamp: true, - }, log.DebugLevel) + }, csmlog.DebugLevel) } portgroups := paramsViper.GetString(PortGroups) if portgroups != "" { - log.Info("Read PortGroups from config file:", portgroups) + log.Info("Read PortGroups from config file: " + portgroups) _ = os.Setenv(PortGroups, portgroups) } protocol := paramsViper.GetString(Protocol) if protocol != "" { - log.Info("Read protocol from config file:", protocol) + log.Info("Read protocol from config file: " + protocol) _ = os.Setenv(Protocol, protocol) } endpoint := paramsViper.GetString(EnvEndpoint) @@ -1020,12 +1059,12 @@ func setArrayConfigEnvs(ctx context.Context) error { if strings.HasSuffix(endpoint, "/") { endpoint = strings.TrimRight(endpoint, "/") } - log.Info("Read endpoint from config file:", endpoint) + log.Info("Read endpoint from config file: " + endpoint) _ = os.Setenv(EnvEndpoint, endpoint) } managedArrays := paramsViper.GetString(ManagedArrays) if managedArrays != "" { - log.Info("Managed arrays from config file:", managedArrays) + log.Info("Managed arrays from config file: " + managedArrays) _ = os.Setenv(ManagedArrays, managedArrays) } diff --git a/service/service_test.go b/service/service_test.go index 1104ef07..5581173a 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -25,10 +25,9 @@ import ( "testing" "time" - "github.com/cucumber/godog" "github.com/dell/csi-powermax/v2/k8smock" + "github.com/cucumber/godog" gomock "github.com/golang/mock/gomock" - log "github.com/sirupsen/logrus" "github.com/spf13/viper" ) diff --git a/service/service_unit_test.go b/service/service_unit_test.go index 141b1d44..bec25822 100644 --- a/service/service_unit_test.go +++ b/service/service_unit_test.go @@ -29,22 +29,23 @@ import ( "testing" "time" - csi "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/coreos/go-systemd/v22/dbus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/dell/csi-powermax/v2/k8smock" "github.com/dell/csi-powermax/v2/k8sutils" "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" + "github.com/dell/csmlog" "github.com/dell/gocsi" csictx "github.com/dell/gocsi/context" pmax "github.com/dell/gopowermax/v2" - log "github.com/sirupsen/logrus" + csi "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/coreos/go-systemd/v22/dbus" + "github.com/golang/mock/gomock" "github.com/spf13/viper" - "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) const ( @@ -149,7 +150,7 @@ func TestBeforeServe(t *testing.T) { "X_CSI_MANAGED_ARRAYS=abc,def", "X_CSI_POWERMAX_SIDECAR_PROXY_PORT=8080", "X_CSI_K8S_CLUSTER_PREFIX=csi", - "X_CSI_POWERMAX_ENDPOINT=http://127.0.0.1:8080", + "X_CSI_POWERMAX_ENDPOINT=http://127.0.0.1:9104", "X_CSI_POWERMAX_PASSWORD=password", "X_CSI_MODE=controller", "X_CSI_MAX_VOLUMES_PER_NODE=10", @@ -184,28 +185,12 @@ func TestBeforeServe(t *testing.T) { }, expectedResult: nil, }, - { - name: "Error creating k8s utils", - ctx: context.WithValue(context.Background(), interface{}("os.Environ"), []string{ - "X_CSI_K8S_CLUSTER_PREFIX=csi", - "X_CSI_MANAGED_ARRAYS=abc,def", - "X_CSI_POWERMAX_ENDPOINT=http://127.0.0.1:8080", - "X_CSI_POWERMAX_PASSWORD=password", - "X_CSI_MODE=controller", - }), - adminClient: func() pmax.Pmax { - return mocks.NewMockPmaxClient(gomock.NewController(t)) - }(), - plugin: nil, - listener: &net.TCPListener{}, - expectedResult: errors.New("error creating k8sClient unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined"), - }, { name: "Error creating PowerMax client", ctx: context.WithValue(context.Background(), interface{}("os.Environ"), []string{ "X_CSI_K8S_CLUSTER_PREFIX=csi", "X_CSI_MANAGED_ARRAYS=abc,def", - "X_CSI_POWERMAX_ENDPOINT=http://127.0.0.1:8080", + "X_CSI_POWERMAX_ENDPOINT=http://127.0.0.1:9104", "X_CSI_POWERMAX_PASSWORD=password", "X_CSI_MODE=controller", "X_CSI_POWERMAX_SIDECAR_PROXY_PORT=2222", @@ -653,7 +638,7 @@ func TestGobrickInitialization(t *testing.T) { } func TestSetGetLogFields(t *testing.T) { - fields := log.Fields{ + fields := csmlog.Fields{ "RequestID": "123", "DeviceID": "12345", } diff --git a/service/snap.go b/service/snap.go index 93de716f..c9dbde34 100644 --- a/service/snap.go +++ b/service/snap.go @@ -25,7 +25,6 @@ import ( pmax "github.com/dell/gopowermax/v2" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -129,7 +128,7 @@ func (scw *snapCleanupWorker) requestCleanup(req *snapCleanupRequest) { for i := range scw.Queue { if scw.Queue[i].snapshotID == req.snapshotID && scw.Queue[i].symmetrixID == req.symmetrixID { // Found! - log.Warningf("Snapshot ID: %s already present in the deletion queue", req.snapshotID) + log.Warnf("Snapshot ID: %s already present in the deletion queue", req.snapshotID) return } } @@ -157,6 +156,7 @@ func (scw *snapCleanupWorker) removeItem() *snapCleanupRequest { // UnlinkTargets unlinks all the target devices from the snapshot func (s *service) UnlinkTargets(ctx context.Context, symID, srcDevID string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) // Get all the snapshot relation on the volume SrcSession, _, err := s.GetSnapSessions(ctx, symID, srcDevID, pmaxClient) if err != nil { @@ -185,6 +185,7 @@ func RemoveReplicationCapability(symID string) { // This function checks if the PowerMax array has the SnapVX license. // It returns an error if the array does not meet the expectations. func (s *service) IsSnapshotLicensed(ctx context.Context, symID string, pmaxClient pmax.Pmax) (err error) { + log := log.WithContext(ctx) if _, err := pmaxClient.IsAllowedArray(symID); err != nil { return err } @@ -233,6 +234,7 @@ func (s *service) IsSnapshotLicensed(ctx context.Context, symID string, pmaxClie // snapID: It can be empty to terminate all the snapshots on a source volume or terminates the // spefified snapshot func (s *service) UnlinkAndTerminate(ctx context.Context, symID, deviceID, snapID string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) var noOfSnapsOnSrc int // Get all the snapshot relation on the volume SrcSessions, TgtSession, err := s.GetSnapSessions(ctx, symID, deviceID, pmaxClient) @@ -303,7 +305,7 @@ func (s *service) UnlinkAndTerminate(ctx context.Context, symID, deviceID, snapI if s.isSourceTaggedToDelete(vol.VolumeIdentifier) { err = s.MarkVolumeForDeletion(ctx, symID, vol, pmaxClient) if err != nil { - log.Error("MarkVolumeForDeletion failed with error - ", err.Error()) + log.Error("MarkVolumeForDeletion failed with error - " + err.Error()) } } } @@ -315,8 +317,9 @@ func (s *service) UnlinkAndTerminate(ctx context.Context, symID, deviceID, snapI // unlink in one go. A value of 0 means, it unlinks all the targets else specified // by number of targets in maxUnlinkCount func (s *service) UnlinkSnapshot(ctx context.Context, symID string, snapSession *SnapSession, maxUnlinkCount int, pmaxClient pmax.Pmax) (err error) { + log := log.WithContext(ctx) if snapSession.Target == nil { - return + return err } var counter int for _, target := range snapSession.Target { @@ -350,6 +353,7 @@ func (s *service) UnlinkSnapshot(ctx context.Context, symID string, snapSession // The caller of this function should take a lock on the source device // before making a call to this function func (s *service) TerminateSnapshot(ctx context.Context, symID string, srcDev string, snapID string, pmaxClient pmax.Pmax) (err error) { + log := log.WithContext(ctx) // Ensure that the snapshot is not already deleted by a simultaneous operation snap, err := pmaxClient.GetSnapshotInfo(ctx, symID, srcDev, snapID) if err != nil || snap.VolumeSnapshotSource == nil { @@ -366,6 +370,7 @@ func (s *service) TerminateSnapshot(ctx context.Context, symID string, srcDev st // RemoveSnapshot deletes a snapshot func (s *service) RemoveSnapshot(ctx context.Context, symID string, srcDev string, snapID string, Generation int64, pmaxClient pmax.Pmax) (err error) { + log := log.WithContext(ctx) log.Info(fmt.Sprintf("Deleting snapshot (%s) with generation (%d)", snapID, Generation)) sourceVolumes := []types.VolumeList{} @@ -388,10 +393,11 @@ func (s *service) IsVolumeInSnapSession(ctx context.Context, symID, deviceID str // GetSnapSessions return snapshot source and target sessions func (s *service) GetSnapSessions(ctx context.Context, symID, deviceID string, pmaxClient pmax.Pmax) (srcSession []SnapSession, tgtSession *SnapSession, err error) { + log := log.WithContext(ctx) snapInfo, err := pmaxClient.GetVolumeSnapInfo(ctx, symID, deviceID) if err != nil { log.Errorf("GetVolumeSnapInfo failed for (%s): (%s)", deviceID, err.Error()) - return + return srcSession, tgtSession, err } log.Debugf("For Volume (%s), Snap Info: %v", deviceID, snapInfo) for _, volumeSnapshotSource := range snapInfo.VolumeSnapshotSource { @@ -418,7 +424,7 @@ func (s *service) GetSnapSessions(ctx context.Context, symID, deviceID string, p pVolInfo, err = pmaxClient.GetPrivVolumeByID(ctx, symID, deviceID) if err != nil { log.Errorf("GetPrivVolumeByID failed for (%s): (%s)", deviceID, err.Error()) - return + return srcSession, tgtSession, err } log.Debugf("For Volume (%s), Priv Vol Info: %v", deviceID, pVolInfo) // Ensure that this indeed is a target device @@ -440,7 +446,7 @@ func (s *service) GetSnapSessions(ctx context.Context, symID, deviceID string, p } } } - return + return srcSession, tgtSession, err } // LinkVolumeToSnapshot helps CreateVolume call to link the newly created @@ -471,43 +477,9 @@ func (s *service) LinkVolumeToSnapshot(ctx context.Context, symID, srcDevID, tgt return nil } -// LinkVolumeToVolume attaches the newly created volume -// to a temporary snapshot created from the source volume -func (s *service) LinkVolumeToVolume(ctx context.Context, symID string, vol *types.Volume, tgtDevID, snapID string, reqID string, isCopy bool, pmaxClient pmax.Pmax) error { - // Create a snapshot from the Source - // Set max 1 hr lifetime for the temporary snapshot - log.Debugf("Creating snapshot %s on %s and linking it to %s", snapID, vol.VolumeID, tgtDevID) - var TTL int64 = 1 - snapInfo, err := s.CreateSnapshotFromVolume(ctx, symID, vol, snapID, TTL, reqID, pmaxClient) - if err != nil { - if strings.Contains(err.Error(), "The maximum number of sessions has been exceeded for the specified Source device") { - return status.Errorf(codes.FailedPrecondition, "Failed to create snapshot: %s", err.Error()) - } - return err - } - // If Auth is enabled, snap name will come back with a prefix like tn1-snapID - snapID = snapInfo.SnapshotName - // Link the Target to the created snapshot - err = s.LinkVolumeToSnapshot(ctx, symID, vol.VolumeID, tgtDevID, snapID, reqID, isCopy, pmaxClient) - if err != nil { - if strings.Contains(err.Error(), errDesiredState) { - log.Infof("Link of %s on %s is in desired state", vol.VolumeID, tgtDevID) - } else { - return err - } - } - // Push the temporary snapshot created for cleanup - var cleanReq snapCleanupRequest - cleanReq.snapshotID = snapInfo.SnapshotName - cleanReq.symmetrixID = symID - cleanReq.volumeID = vol.VolumeID - cleanReq.requestID = reqID - s.snapCleaner.requestCleanup(&cleanReq) - return nil -} - // CreateSnapshotFromVolume creates a snapshot on a source volume func (s *service) CreateSnapshotFromVolume(ctx context.Context, symID string, vol *types.Volume, snapID string, TTL int64, reqID string, pmaxClient pmax.Pmax) (snapshot *types.VolumeSnapshot, err error) { + log := log.WithContext(ctx) log.Debugf("Creating snapshot %s on %s", snapID, vol.VolumeID) lockHandle := fmt.Sprintf("%s%s", vol.VolumeID, symID) lockNum := requestLockFunc(lockHandle, reqID) @@ -597,6 +569,7 @@ func (s *service) startSnapCleanupWorker() error { // snapCleanupThread - Deletes temporary snapshots and snapshots // that are pending but marked for deletion func snapCleanupThread(ctx context.Context, scw *snapCleanupWorker, s *service) { + log := log.WithContext(ctx) var tempSnapTag string var delSnapTag string @@ -700,14 +673,14 @@ func (s *service) isSourceTaggedToDelete(volName string) (ok bool) { volComponents := strings.Split(volName, "-") if len(volComponents) < 3 { ok = false - return + return ok } // Get the last substring containing DS tag ds := volComponents[len(volComponents)-1] if ds == delSrcTag { ok = true } - return + return ok } // findSnapIDFromSnapName returns the snapID from snapshot name in Device list @@ -715,11 +688,11 @@ func (s *service) findSnapIDFromSnapName(snapName string) (ok bool, snapID strin snapComponents := strings.Split(snapName, "-") if len(snapComponents) < 4 { ok = false - return + return ok, snapID } ok = true // Extract the snapshot name from name found in volume information // which is in devid-src/lnk-snapshotname-generation snapID = strings.Join(snapComponents[2:len(snapComponents)-1], "-") - return + return ok, snapID } diff --git a/service/snap_test.go b/service/snap_test.go index 420d6711..f0597fa5 100644 --- a/service/snap_test.go +++ b/service/snap_test.go @@ -15,15 +15,10 @@ package service import ( - "context" "reflect" "sync" "testing" "time" - - "github.com/dell/csi-powermax/v2/pkg/symmetrix/mocks" - types "github.com/dell/gopowermax/v2/types/v100" - gmock "go.uber.org/mock/gomock" ) func Test_snapCleanupQueue_Swap(t *testing.T) { @@ -350,77 +345,3 @@ func Test_service_startSnapCleanupWorker(t *testing.T) { }) } } - -// This test simulates a successful linkVolumeToVolume call when Auth is enabled -func TestLinkVolumeToVolumeWithAuth(t *testing.T) { - // Create a service - s := &service{} - - // Create a mock Pmax client - mockPmaxClient := mocks.NewMockPmaxClient(gmock.NewController(t)) - sysID := "sym-123" - - // Creating fields for the expected method calls - vol := &types.Volume{ - VolumeID: "123", - } - - volumeList := []types.VolumeList{ - { - Name: vol.VolumeID, - }, - } - tgtDevID := "tgt-dev-123" - - tgtVolumeList := []types.VolumeList{ - { - Name: tgtDevID, - }, - } - - snapID := "snap-123" - - // snapID with the tenent prefix from Auth - authSnapID := "tn1-snap-123" - - reqID := "req-123" - - isCopy := true - - var TTL int64 = 1 - - var generation int64 - - // Set up the mock Pmax client with request/release lock functions and snap cleaner - requestLockFunc = func(_, _ string) int { - return 0 - } - - releaseLockFunc = func(_, _ string, _ int) {} - - s.snapCleaner = &snapCleanupWorker{ - PollingInterval: time.Second * 1, - Queue: snapCleanupQueue{}, - } - - // Set up the mock Pmax client with expected method calls - mockPmaxClient.EXPECT().CreateSnapshot(context.Background(), sysID, snapID, volumeList, TTL).Return(nil) - - mockPmaxClient.EXPECT().GetSnapshotInfo(context.Background(), sysID, vol.VolumeID, snapID).Return(&types.VolumeSnapshot{ - SnapshotName: authSnapID, - }, nil) - - // Returning snapID with the tenent prefix from Auth simulates what happens when Auth is enabled and LinkVolumeToVolume is called - mockPmaxClient.EXPECT().GetSnapshotInfo(context.Background(), sysID, vol.VolumeID, authSnapID).Return(&types.VolumeSnapshot{ - SnapshotName: authSnapID, - }, nil) - - mockPmaxClient.EXPECT().ModifySnapshotS(context.Background(), sysID, volumeList, tgtVolumeList, authSnapID, Link, "", generation, isCopy).Return(nil) - - // Call the LinkVolumeToVolume function - err := s.LinkVolumeToVolume(context.Background(), sysID, vol, tgtDevID, snapID, reqID, isCopy, mockPmaxClient) - // Assert that the error is nil - if err != nil { - t.Errorf("Expected nil error, but got: %v", err) - } -} diff --git a/service/step_defs_test.go b/service/step_defs_test.go index 7f83fff9..39d70b9f 100644 --- a/service/step_defs_test.go +++ b/service/step_defs_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. +Copyright © 2021-2026 Dell Inc. or its subsidiaries. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -48,8 +48,6 @@ import ( migrext "github.com/dell/dell-csi-extensions/migration" "github.com/dell/gocsi" - csi "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/cucumber/godog" podmon "github.com/dell/dell-csi-extensions/podmon" csiext "github.com/dell/dell-csi-extensions/replication" "github.com/dell/gofsutil" @@ -57,7 +55,8 @@ import ( pmax "github.com/dell/gopowermax/v2" mock "github.com/dell/gopowermax/v2/mock" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" + csi "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/cucumber/godog" "golang.org/x/net/context" "google.golang.org/grpc/metadata" @@ -71,10 +70,10 @@ const ( altVolumeName = "vol2" goodNodeID = "node1" altNodeID = "7E012974-3651-4DCB-9954-25975A3C3CDF" - datafile = "test/tmp/datafile" - datafile2 = "test/tmp/datafile2" - datadir = "test/tmp/datadir" - datadir2 = "test/tmp/datadir2" + datafile = "/tmp/test/tmp/datafile" + datafile2 = "/tmp/test/tmp/datafile2" + datadir = "/tmp/test/tmp/datadir" + datadir2 = "/tmp/test/tmp/datadir2" volume1 = "CSIXX-Int409498632-000197900046-00501" volume2 = "CSIXX-Int409498632-000197900046-00502" volumeWithRemote1 = "CSIXX-Int409498632-000197900046:000197900046-00501:00502" @@ -83,19 +82,19 @@ const ( altPublishBlockDevice = "sdd" nodePublishMultipathDevice = "dm-0" nodePublishDeviceDir = "test/dev" - nodePublishBlockDevicePath = "test/dev/sdc" - nodePublishMultipathPath = "test/dev/dm-0" - altPublishBlockDevicePath = "test/dev/sdd" - nodePublishSymlinkDir = "test/dev/disk/by-id" - nodePublishPathSymlinkDir = "test/dev/disk/by-path" - nodePublishPrivateDir = "test/tmp" + nodePublishBlockDevicePath = "/tmp/test/dev/sdc" + nodePublishMultipathPath = "/tmp/test/dev/dm-0" + altPublishBlockDevicePath = "/tmp/test/dev/sdd" + nodePublishSymlinkDir = "/tmp/test/dev/disk/by-id" + nodePublishPathSymlinkDir = "/tmp/test/dev/disk/by-path" + nodePublishPrivateDir = "/tmp/test/tmp" nodePublishWWN = "60000970000197900046533030300501" nodePublishAltWWN = "60000970000197900046533030300502" remoteNodePublishWWN = "60000970000197900046533030300501" remoteNodePublishAltWWN = "60000970000197900046533030300502" nodePublishLUNID = "3" remoteNodePublishLUNID = "4" - iSCSIEtcDir = "test/etc/iscsi" + iSCSIEtcDir = "/tmp/test/etc/iscsi" iSCSIEtcFile = "initiatorname.iscsi" goodSnapID = "444-444" altSnapID = "555-555" @@ -117,17 +116,24 @@ const ( MaxRetries = 10 Namespace = "namespace-test" kubeconfig = "/etc/kubernetes/admin.conf" + imageVersion = "1.0.0" ) var allBlockDevices = [2]string{nodePublishBlockDevicePath, altPublishBlockDevicePath} +var ( + tempNodePublishBlockDevicePath = nodePublishBlockDevicePath + tempAltPublishBlockDevicePath = altPublishBlockDevicePath + nodePublishAltBlockDevPath = "scinib" +) + type feature struct { nGoRoutines int lastTime time.Time server *httptest.Server service *service err error // return from the preceeding call - // replace this with the Unispher client + // replace this with the Unisphere client adminClient pmax.Pmax symmetrixID string remoteSymID string @@ -258,7 +264,7 @@ func (f *feature) aPowerMaxService() error { SetInduceOverloadError(false) SetInducePendingError(false) inducedMockReverseProxy = true - gofsutil.GOFSWWNPath = "test/dev/disk/by-id/wwn-0x" + gofsutil.GOFSWWNPath = "/tmp/test/dev/disk/by-id/wwn-0x" nodePublishSleepTime = 5 * time.Millisecond removeDeviceSleepTime = 5 * time.Millisecond targetMountRecheckSleepTime = 30 * time.Millisecond @@ -425,7 +431,7 @@ func (f *feature) aPowerMaxService() error { f.server = httptest.NewServer(handler) } f.service.opts.Endpoint = f.server.URL - log.Printf("server url: %s", f.server.URL) + log.Infof("server url: %s", f.server.URL) // initialize the admin client if f.service.adminClient == nil { @@ -472,6 +478,9 @@ func (f *feature) aPowerMaxService() error { symIDs := f.service.retryableGetSymmetrixIDList() f.service.NewDeletionWorker(f.service.opts.ClusterPrefix, symIDs.SymmetrixIDs) f.errType = "" + + // Configure ManifestSemver + ManifestSemver = imageVersion return nil } @@ -508,7 +517,7 @@ func (f *feature) getService() *service { mock.AddPortGroupWithPortID("portgroup3", NvmeTCPTransportProtocol, []string{defaultNVMEDirPort}) mock.AddPortGroupWithPortID("portgroup4", FcTransportProtocol, []string{defaultFCDirPort}) - opts.ManagedArrays = []string{"000197900046", "000197900047", "000000000013"} + opts.ManagedArrays = []string{"000197900046", "000197900047", "000000000013", "000197900048"} opts.NodeFullName, _ = os.Hostname() opts.EnableSnapshotCGDelete = true opts.EnableListVolumesSnapshots = true @@ -559,11 +568,10 @@ func (f *feature) iCallGetPluginInfo() error { func (f *feature) aValidGetPluginInfoResponseIsReturned() error { rep := f.getPluginInfoResponse - url := rep.GetManifest()["url"] - if rep.GetName() == "" || rep.GetVendorVersion() == "" || url == "" { + if rep.GetName() == "" || rep.GetVendorVersion() == "" { return errors.New("Expected GetPluginInfo to return name and version") } - log.Printf("Name %s Version %s URL %s", rep.GetName(), rep.GetVendorVersion(), url) + log.Infof("Name %s Version %s", rep.GetName(), rep.GetVendorVersion()) return nil } @@ -623,7 +631,7 @@ func (f *feature) iCallGetReplicationCapabilities() error { header := metadata.New(map[string]string{"csi.requestid": "1"}) ctx := metadata.NewIncomingContext(context.Background(), header) req := new(csiext.GetReplicationCapabilityRequest) - log.Printf("Mode %s", f.service.mode) + log.Infof("Mode %s", f.service.mode) f.getRepCapabilitiesResponse, f.err = f.service.GetReplicationCapabilities(ctx, req) if f.err != nil { return f.err @@ -857,10 +865,33 @@ func (f *feature) iCallCreateVolume(name string) error { f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { - log.Printf("CreateVolume called failed: %s", f.err.Error()) + log.Infof("CreateVolume called failed: %s", f.err.Error()) + } + if f.createVolumeResponse != nil { + log.Infof("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) + f.volumeID = f.createVolumeResponse.GetVolume().VolumeId + f.volumeNameToID[name] = f.volumeID + } + return nil +} + +func (f *feature) iCallCreateVolumeEnhanced(name string) error { + header := metadata.New(map[string]string{"csi.requestid": "1"}) + ctx := metadata.NewIncomingContext(context.Background(), header) + if f.createVolumeRequest == nil { + req := f.getTypicalCreateVolumeRequest() + req.Parameters[SymmetrixIDParam] = "000197900048" + f.createVolumeRequest = req + } + req := f.createVolumeRequest + req.Name = name + + f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) + if f.err != nil { + log.Infof("CreateVolume called failed: %s", f.err.Error()) } if f.createVolumeResponse != nil { - log.Printf("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) + log.Infof("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) f.volumeID = f.createVolumeResponse.GetVolume().VolumeId f.volumeNameToID[name] = f.volumeID } @@ -886,16 +917,16 @@ func (f *feature) iCallCreateVolumeWithNamespace(name string, namespace string) } f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { - log.Printf("CreateVolume called failed: %s\n", f.err.Error()) + log.Infof("CreateVolume called failed: %s", f.err.Error()) } if f.createVolumeResponse != nil { - log.Printf("vol id %s\n", f.createVolumeResponse.GetVolume().VolumeId) + log.Infof("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) f.volumeID = f.createVolumeResponse.GetVolume().VolumeId f.volumeNameToID[name] = f.volumeID } vol, _, _, _, _, err := f.service.parseCsiID(f.volumeID) if err != nil { - log.Printf("volID: %s malformed. Error: %s", vol, f.err.Error()) + log.Infof("volID: %s malformed. Error: %s", vol, f.err.Error()) } // get the namespace from volume name and validate @@ -927,10 +958,10 @@ func (f *feature) iCallRDFEnabledCreateVolume(volName, namespace, mode string, r } f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { - log.Printf("CreateVolume called failed: %s", f.err.Error()) + log.Infof("CreateVolume called failed: %s", f.err.Error()) } if f.createVolumeResponse != nil { - log.Printf("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) + log.Infof("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) f.volumeID = f.createVolumeResponse.GetVolume().VolumeId f.volumeNameToID[volName] = f.volumeID } @@ -973,6 +1004,42 @@ func (f *feature) aValidCreateVolumeResponseIsReturned() error { return nil } +func (f *feature) aValidCreateVolumeEnhancedResponseIsReturned() error { + if f.err != nil { + return f.err + } + if f.createVolumeResponse == nil || f.createVolumeResponse.Volume == nil { + return errors.New("Expected a valid createVolumeResponse") + } + // Verify the Volume context + params := f.createVolumeRequest.Parameters + volumeContext := f.createVolumeResponse.GetVolume().VolumeContext + fmt.Printf("volume:\n%#v\n", volumeContext) + if params[StoragePoolParam] != volumeContext[StoragePoolParam] { + return errors.New("StoragePoolParam in response should match the request") + } + if serviceLevel, ok := params[ServiceLevelParam]; ok { + if serviceLevel != volumeContext[ServiceLevelParam] { + return errors.New("ServiceLevelParam in response should match the request") + } + } else { + if volumeContext[StoragePoolParam] != "Optimized" { + return errors.New("ServiceLevelParam in response should be Optimized") + } + } + // Verify the RDF info + if params[RepEnabledParam] == "true" { + if volumeContext[RemoteSymIDParam] != params[RemoteSymIDParam] { + return errors.New("RemoteSymIDParam in response should match the request") + } + } + f.volumeIDList = append(f.volumeIDList, f.createVolumeResponse.Volume.VolumeId) + fmt.Printf("Service Level %s SRP %s\n", + f.createVolumeResponse.Volume.VolumeContext[ServiceLevelParam], + f.createVolumeResponse.Volume.VolumeContext[StoragePoolParam]) + return nil +} + func (f *feature) iSpecifyAccessibilityRequirements() error { req := new(csi.CreateVolumeRequest) params := make(map[string]string) @@ -1079,10 +1146,10 @@ func (f *feature) iCallCreateVolumeSize(name string, size int64) error { f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { - log.Printf("CreateVolumeSize called failed: %s", f.err.Error()) + log.Infof("CreateVolumeSize called failed: %s", f.err.Error()) } if f.createVolumeResponse != nil { - log.Printf("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) + log.Infof("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) f.volumeID = f.createVolumeResponse.GetVolume().VolumeId f.volumeNameToID[name] = f.volumeID } @@ -1099,7 +1166,7 @@ func (f *feature) iChangeTheStoragePool(_ string) error { } func (f *feature) iInduceError(errtype string) error { - log.Printf("set induce error %s", errtype) + log.Infof("set induce error %s", errtype) f.errType = errtype switch errtype { case "InvalidSymID": @@ -1783,10 +1850,10 @@ func (f *feature) iCallPublishVolumeWithTo(accessMode, nodeID string) error { req = f.getControllerPublishVolumeRequest(accessMode, nodeID) f.publishVolumeRequest = req } - log.Printf("Calling controllerPublishVolume") + log.Infof("Calling controllerPublishVolume") f.publishVolumeResponse, f.err = f.service.ControllerPublishVolume(ctx, req) if f.err != nil { - log.Printf("PublishVolume call failed: %s", f.err.Error()) + log.Infof("PublishVolume call failed: %s", f.err.Error()) } f.publishVolumeRequest = nil return nil @@ -1902,10 +1969,10 @@ func (f *feature) iCallUnpublishVolumeFrom(nodeID string) error { req = f.getControllerUnpublishVolumeRequest(nodeID) f.unpublishVolumeRequest = req } - log.Printf("Calling controllerUnpublishVolume: %s", req.VolumeId) + log.Infof("Calling controllerUnpublishVolume: %s", req.VolumeId) f.unpublishVolumeResponse, f.err = f.service.ControllerUnpublishVolume(ctx, req) if f.err != nil { - log.Printf("UnpublishVolume call failed: %s", f.err.Error()) + log.Infof("UnpublishVolume call failed: %s", f.err.Error()) } return nil } @@ -1921,6 +1988,7 @@ func (f *feature) iCallNodeGetInfo() error { header := metadata.New(map[string]string{"csi.requestid": "1"}) ctx := metadata.NewIncomingContext(context.Background(), header) req := new(csi.NodeGetInfoRequest) + f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(ctx, req) return nil } @@ -1953,7 +2021,7 @@ func (f *feature) iCallNodeGetInfoWithInvalidVolumeLimit(volumeLimit int64) erro f.service.opts.MaxVolumesPerNode = volumeLimit f.nodeGetInfoResponse, f.err = f.service.NodeGetInfo(context.Background(), req) if f.err != nil { - log.Printf("NodeGetInfo call failed: %s\n", f.err.Error()) + log.Infof("NodeGetInfo call failed: %s", f.err.Error()) return nil } return nil @@ -2023,10 +2091,10 @@ func (f *feature) iCallDeleteVolumeWith(arg1 string) error { if inducedErrors.invalidVolumeID { req.VolumeId = "000-00" } - log.Printf("Calling DeleteVolume") + log.Infof("Calling DeleteVolume") f.deleteVolumeResponse, f.err = f.service.DeleteVolume(ctx, req) if f.err != nil { - log.Printf("DeleteVolume called failed: %s", f.err.Error()) + log.Infof("DeleteVolume called failed: %s", f.err.Error()) } return nil } @@ -2081,7 +2149,7 @@ func (f *feature) iCallGetCapacityWithStoragePool(srpID string) error { req.Parameters[StoragePoolParam], req.Parameters[SymmetrixIDParam]) f.getCapacityResponse, f.err = f.service.GetCapacity(ctx, req) if f.err != nil { - log.Printf("GetCapacity call failed: %s", f.err.Error()) + log.Infof("GetCapacity call failed: %s", f.err.Error()) return nil } return nil @@ -2096,7 +2164,7 @@ func (f *feature) iCallGetCapacityWithoutSymmetrixID() error { req.Parameters = parameters f.getCapacityResponse, f.err = f.service.GetCapacity(ctx, req) if f.err != nil { - log.Printf("GetCapacity call failed: %s", f.err.Error()) + log.Infof("GetCapacity call failed: %s", f.err.Error()) return nil } return nil @@ -2109,7 +2177,7 @@ func (f *feature) iCallGetCapacityWithoutParameters() error { req.Parameters = nil f.getCapacityResponse, f.err = f.service.GetCapacity(ctx, req) if f.err != nil { - log.Printf("GetCapacity call failed: %s", f.err.Error()) + log.Infof("GetCapacity call failed: %s", f.err.Error()) return nil } return nil @@ -2124,7 +2192,7 @@ func (f *feature) iCallGetCapacityWithInvalidCapabilities() error { req.Parameters = parameters f.getCapacityResponse, f.err = f.service.GetCapacity(ctx, req) if f.err != nil { - log.Printf("GetCapacity call failed: %s", f.err.Error()) + log.Infof("GetCapacity call failed: %s", f.err.Error()) return nil } return nil @@ -2148,10 +2216,10 @@ func (f *feature) iCallControllerGetCapabilities() error { header := metadata.New(map[string]string{"csi.requestid": "1"}) ctx := metadata.NewIncomingContext(context.Background(), header) req := new(csi.ControllerGetCapabilitiesRequest) - log.Printf("Calling ControllerGetCapabilities") + log.Infof("Calling ControllerGetCapabilities") f.controllerGetCapabilitiesResponse, f.err = f.service.ControllerGetCapabilities(ctx, req) if f.err != nil { - log.Printf("ControllerGetCapabilities call failed: %s", f.err.Error()) + log.Infof("ControllerGetCapabilities call failed: %s", f.err.Error()) return f.err } return nil @@ -2222,12 +2290,12 @@ func (f *feature) iCallListVolumesWith(dt *messages.PickleStepArgument_PickleTab req = f.getControllerListVolumesRequest(maxEntries, startingToken) f.listVolumesRequest = req } - log.Printf("Calling ListVolumes with req=%+v", f.listVolumesRequest) + log.Infof("Calling ListVolumes with req=%+v", f.listVolumesRequest) f.listVolumesResponse, f.err = f.service.ListVolumes(ctx, req) if f.err != nil { - log.Printf("ListVolume called failed: %s", f.err.Error()) + log.Infof("ListVolume called failed: %s", f.err.Error()) } else if f.listVolumesResponse == nil { - log.Printf("Received null response from ListVolumes") + log.Infof("Received null response from ListVolumes") } else { f.listVolumesNextTokenCache = f.listVolumesResponse.NextToken } @@ -2324,7 +2392,7 @@ func (f *feature) iCallValidateVolumeCapabilitiesWithVoltypeAccessFstype(voltype } req.VolumeContext = attributes - log.Printf("Calling ValidateVolumeCapabilities") + log.Infof("Calling ValidateVolumeCapabilities") f.validateVolumeCapabilitiesResponse, f.err = f.service.ValidateVolumeCapabilities(ctx, req) if f.err != nil || f.validateVolumeCapabilitiesResponse == nil { return nil @@ -2469,10 +2537,14 @@ func (f *feature) aControllerPublishedVolume() error { _, err = os.Stat(dev) if err != nil { fmt.Printf("stat error: %s\n", err.Error()) - cmd := exec.Command("mknod", dev, "b", "0", "0") - output, err := cmd.CombinedOutput() + _, err = os.Create(dev) if err != nil { - fmt.Printf("A error creating device node: %s\n", string(output)) + fmt.Printf("A error creating device node: %s\n", err) + } + + // Optionally set permissions to mimic a device node + if err := os.Chmod(dev, 0o750); err != nil { + fmt.Printf("Failed to set permissions on mock device file: %s\n", err) } } } @@ -2531,11 +2603,14 @@ func (f *feature) aControllerPublishedMultipathVolume() error { // Make the block device and alternate _, err = os.Stat(nodePublishMultipathPath) if err != nil { - fmt.Printf("stat error: %s\n", err.Error()) - cmd := exec.Command("mknod", nodePublishMultipathPath, "b", "0", "7") - output, err := cmd.CombinedOutput() + _, err = os.Create(nodePublishMultipathPath) if err != nil { - fmt.Printf("B error creating device node: %s\n", string(output)) + fmt.Printf("A error creating device node: %s\n", err) + } + + // Optionally set permissions to mimic a device node + if err := os.Chmod(nodePublishMultipathPath, 0o750); err != nil { + fmt.Printf("Failed to set permissions on mock device file: %s\n", err) } } @@ -2552,8 +2627,8 @@ func (f *feature) aControllerPublishedMultipathVolume() error { } } // Make the gofsutil entry - gofsutil.GOFSWWNPath = "test/dev/disk/by-id/dm-uuid-mpath-3" - gofsutil.MultipathDevDiskByIDPrefix = "test/dev/disk/by-id/dm-uuid-mpath-3" + gofsutil.GOFSWWNPath = "/tmp/test/dev/disk/by-id/dm-uuid-mpath-3" + gofsutil.MultipathDevDiskByIDPrefix = "/tmp/test/dev/disk/by-id/dm-uuid-mpath-3" gofsutil.GOFSMockWWNToDevice[symlinkString] = nodePublishMultipathPath return nil } @@ -2727,8 +2802,24 @@ func (f *feature) iCallNodePublishVolume() error { _ = f.getNodePublishVolumeRequest() req = f.nodePublishVolumeRequest } + + file, err := os.Create(nodePublishAltBlockDevPath) + if err != nil { + fmt.Printf("Couldn't create block dev: %s\n", nodePublishAltBlockDevPath) + fmt.Printf("couldn't create file: %s: %v\n", nodePublishAltBlockDevPath, err) + } + + defer func() { + file.Close() + }() + + // Optionally set permissions to mimic a device node + if err := os.Chmod(nodePublishAltBlockDevPath, 0o750); err != nil { + fmt.Printf("Failed to set permissions on mock device file %s: %v\n", nodePublishAltBlockDevPath, err) + } + fmt.Printf("Calling NodePublishVolume\n") - _, err := f.service.NodePublishVolume(ctx, req) + _, err = f.service.NodePublishVolume(ctx, req) if err != nil { fmt.Printf("NodePublishVolume failed: %s\n", err.Error()) if f.err == nil { @@ -2959,7 +3050,7 @@ func (f *feature) iCallNodeUnstageVolume() error { req.VolumeId = "badVolumeID" } req.StagingTargetPath = f.nodePublishVolumeRequest.StagingTargetPath - log.Printf("iCallNodeUnstageVolume %s %s", req.VolumeId, req.StagingTargetPath) + log.Infof("iCallNodeUnstageVolume %s %s", req.VolumeId, req.StagingTargetPath) _, f.err = f.service.NodeUnstageVolume(ctx, req) return nil } @@ -2982,7 +3073,7 @@ func (f *feature) iCallNodeUnstageVolumeWithSimulator() error { req.VolumeId = "badVolumeID" } req.StagingTargetPath = f.nodePublishVolumeRequest.StagingTargetPath - log.Printf("iCallNodeUnstageVolume %s %s", req.VolumeId, req.StagingTargetPath) + log.Infof("iCallNodeUnstageVolume %s %s", req.VolumeId, req.StagingTargetPath) _, f.err = f.service.NodeUnstageVolume(ctx, req) }) return nil @@ -3758,7 +3849,7 @@ func (f *feature) aDevicePathLun(device, lun string) error { value := nodePublishDeviceDir + "/" + device gofsutil.GOFSMockTargetIPLUNToDevice[key] = value gofsutil.GOFSMockWWNToDevice[nodePublishWWN] = value - log.Printf("aDevicePath wwn %s dev %s", nodePublishWWN, value) + log.Infof("aDevicePath wwn %s dev %s", nodePublishWWN, value) return nil } @@ -3779,8 +3870,8 @@ func (f *feature) thereAreRemainingDeviceEntriesForLun(number int, _ string) err } func (f *feature) aNodeRootWithMultipathConfigFile() error { - os.MkdirAll("test/noderoot/etc", 0o777) - os.MkdirAll("test/root/etc", 0o777) + os.MkdirAll("/tmp/test/noderoot/etc", 0o777) + os.MkdirAll("/tmp/test/root/etc", 0o777) _, err := exec.Command("touch", "test/noderoot/etc/multipath.conf").CombinedOutput() return err } @@ -4224,9 +4315,11 @@ func (f *feature) iCallRequestAddVolumeToSGMVMv(nodeID, maskingViewName string) deviceID := deviceIDComponents[len(deviceIDComponents)-1] fmt.Printf("deviceID %s\n", deviceID) if maskingViewName != "" && maskingViewName != "default" { - f.addVolumeToSGMVResponse2, f.lockChan, f.err = f.service.sgSvc.requestAddVolumeToSGMV(context.Background(), f.sgID, maskingViewName, f.hostID, "0001", mock.DefaultSymmetrixID, mock.DefaultSymmetrixID, deviceID, accessMode) + f.addVolumeToSGMVResponse2, f.lockChan, f.err = f.service.sgSvc.requestAddVolumeToSGMV( + context.Background(), f.sgID, maskingViewName, f.hostID, "0001", mock.DefaultSymmetrixID, mock.DefaultSymmetrixID, deviceID, "fake", accessMode) } else { - f.addVolumeToSGMVResponse1, f.lockChan, f.err = f.service.sgSvc.requestAddVolumeToSGMV(context.Background(), f.sgID, f.mvID, f.hostID, "0001", mock.DefaultSymmetrixID, mock.DefaultSymmetrixID, deviceID, accessMode) + f.addVolumeToSGMVResponse1, f.lockChan, f.err = f.service.sgSvc.requestAddVolumeToSGMV( + context.Background(), f.sgID, f.mvID, f.hostID, "0001", mock.DefaultSymmetrixID, mock.DefaultSymmetrixID, deviceID, "fake", accessMode) } return nil } @@ -4739,7 +4832,7 @@ func (f *feature) IsSnapshotSource(ctx context.Context, symID string, devID stri srcSessions, _, err := s.GetSnapSessions(ctx, symID, devID, pmaxClient) if err != nil { - log.Error("Failed to determine volume as a snapshot source: Error - ", err.Error()) + log.Error("Failed to determine volume as a snapshot source: Error - " + err.Error()) if strings.Contains(err.Error(), "Volume is neither a source nor target") { err = nil } @@ -4856,10 +4949,10 @@ func (f *feature) iCallDeleteLocalVolumeWith(arg1 string) error { if inducedErrors.invalidVolumeID { req.VolumeHandle = "000-00" } - log.Printf("Calling DeleteLocalVolume") + log.Infof("Calling DeleteLocalVolume") f.deleteLocalVolumeResponse, f.err = f.service.DeleteLocalVolume(ctx, req) if f.err != nil { - log.Printf("DeleteLocalVolume called failed: %s", f.err.Error()) + log.Infof("DeleteLocalVolume called failed: %s", f.err.Error()) } return nil } @@ -4891,10 +4984,10 @@ func (f *feature) iCallControllerGetVolume() error { cmp[3] = "000xxxx00000" req.VolumeId = strings.Join(cmp, "-") } - log.Printf("Calling ControllerGetVolume") + log.Infof("Calling ControllerGetVolume") f.controllerGetVolumeResponse, f.err = f.service.ControllerGetVolume(ctx, req) if f.err != nil { - log.Printf("ControllerGetVolume call failed: %s", f.err.Error()) + log.Infof("ControllerGetVolume call failed: %s", f.err.Error()) } return nil } @@ -4933,7 +5026,7 @@ func (f *feature) iCallGetStorageProtectionGroupStatus(mode string) error { } _, f.err = f.service.GetStorageProtectionGroupStatus(ctx, req) if f.err != nil { - log.Printf("GetStorageProtectionGroupStatus call failed: %s", f.err.Error()) + log.Infof("GetStorageProtectionGroupStatus call failed: %s", f.err.Error()) } return nil } @@ -4956,7 +5049,7 @@ func (f *feature) iCallExecuteAction(action string) error { } _, f.err = f.service.ExecuteAction(ctx, req) if f.err != nil { - log.Printf("GetStorageProtectionGroupStatus call failed: %s", f.err.Error()) + log.Infof("GetStorageProtectionGroupStatus call failed: %s", f.err.Error()) } return nil } @@ -5002,10 +5095,10 @@ func (f *feature) iCallFileSystemCreateVolume(volName string) error { f.createVolumeResponse, f.err = f.service.CreateVolume(ctx, req) if f.err != nil { - log.Printf("CreateVolume called failed: %s", f.err.Error()) + log.Infof("CreateVolume called failed: %s", f.err.Error()) } if f.createVolumeResponse != nil { - log.Printf("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) + log.Infof("vol id %s", f.createVolumeResponse.GetVolume().VolumeId) f.volumeID = f.createVolumeResponse.GetVolume().VolumeId f.volumeNameToID[volName] = f.volumeID } @@ -5018,10 +5111,10 @@ func (f *feature) iCallFileSystemDeleteVolume() error { req := &csi.DeleteVolumeRequest{ VolumeId: f.volumeID, } - log.Printf("Calling fileSystem DeleteVolume %v", req) + log.Infof("Calling fileSystem DeleteVolume %v", req) f.deleteVolumeResponse, f.err = f.service.DeleteVolume(ctx, req) if f.err != nil { - log.Printf("fileSystem DeleteVolume called failed: %s", f.err.Error()) + log.Infof("fileSystem DeleteVolume called failed: %s", f.err.Error()) } return nil } @@ -5039,7 +5132,7 @@ func (f *feature) iCallValidateVolumeHostConnectivity() error { req := &podmon.ValidateVolumeHostConnectivityRequest{} f.validateVHCResp, f.err = f.service.ValidateVolumeHostConnectivity(ctx, req) if f.err != nil { - log.Printf("error in ValidateVolumeHostConnectivity: %s", f.err.Error()) + log.Infof("error in ValidateVolumeHostConnectivity: %s", f.err.Error()) } return nil } @@ -5074,7 +5167,7 @@ func (f *feature) iCallValidateVolumeHostConnectivityWithAndSymID(nodeID, symID } f.validateVHCResp, f.err = f.service.ValidateVolumeHostConnectivity(ctx, req) if f.err != nil { - log.Printf("error in ValidateVolumeHostConnectivity: %s", f.err.Error()) + log.Infof("error in ValidateVolumeHostConnectivity: %s", f.err.Error()) } return nil } @@ -5092,7 +5185,13 @@ func (f *feature) iStartNodeAPIServer() { f.service.opts.PodmonPort = ":9028" fmt.Printf("Starting server at port %s\n", f.service.opts.PodmonPort) - go http.ListenAndServe(f.service.opts.PodmonPort, nil) // #nosec G114 + // http.ListenAndServe(f.service.opts.PodmonPort, nil) // #nosec G114 + go listenAndServe(f.service.opts.PodmonPort) +} + +func listenAndServe(port string) { + err := http.ListenAndServe(port, nil) // #nosec G114 + fmt.Println("Error with listen and serve: ", err) } func (f *feature) iCallQueryArrayStatus(url string, statusType string) { @@ -5256,7 +5355,9 @@ func FeatureContext(s *godog.ScenarioContext) { s.Step(`^the Controller has no connection$`, f.theControllerHasNoConnection) s.Step(`^there is a Node Probe Lsmod error$`, f.thereIsANodeProbeLsmodError) s.Step(`^I call CreateVolume "([^"]*)"$`, f.iCallCreateVolume) + s.Step(`^I call CreateVolumeEnhanced "([^"]*)"$`, f.iCallCreateVolumeEnhanced) s.Step(`^a valid CreateVolumeResponse is returned$`, f.aValidCreateVolumeResponseIsReturned) + s.Step(`^a valid CreateVolumeEnhancedResponse is returned$`, f.aValidCreateVolumeEnhancedResponseIsReturned) s.Step(`^I specify AccessibilityRequirements$`, f.iSpecifyAccessibilityRequirements) s.Step(`^I specify MULTINODEWRITER$`, f.iSpecifyMULTINODEWRITER) s.Step(`^I specify a BadCapacity$`, f.iSpecifyABadCapacity) diff --git a/service/storage_group_svc.go b/service/storage_group_svc.go index 17b7233e..6bd4fb99 100644 --- a/service/storage_group_svc.go +++ b/service/storage_group_svc.go @@ -22,11 +22,11 @@ import ( "time" "github.com/dell/csi-powermax/v2/pkg/symmetrix" + "github.com/dell/csmlog" pmax "github.com/dell/gopowermax/v2" - csi "github.com/container-storage-interface/spec/lib/go/csi" types "github.com/dell/gopowermax/v2/types/v100" - log "github.com/sirupsen/logrus" + csi "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -60,6 +60,7 @@ type addVolumeToSGMVRequest struct { symID string clientSymID string devID string + volumeName string accessMode *csi.VolumeCapability_AccessMode respChan chan addVolumeToSGMVResponse } @@ -85,6 +86,7 @@ type removeVolumeFromSGMVRequest struct { symID string clientSymID string devID string + volumeName string respChan chan removeVolumeFromSGMVResponse } @@ -145,7 +147,7 @@ func (g *storageGroupSvc) getUpdateStorageGroupState(symID, tgtStorageGroupID st } // requestAddVolumeToSGMV requests that a volume be added to a specific storage group and masking view. -func (g *storageGroupSvc) requestAddVolumeToSGMV(_ context.Context, tgtStorageGroupID, tgtMaskingViewID, hostID, reqID, clientSymID, symID, devID string, accessMode *csi.VolumeCapability_AccessMode) (chan addVolumeToSGMVResponse, chan bool, error) { +func (g *storageGroupSvc) requestAddVolumeToSGMV(_ context.Context, tgtStorageGroupID, tgtMaskingViewID, hostID, reqID, clientSymID, symID, devID, volumeName string, accessMode *csi.VolumeCapability_AccessMode) (chan addVolumeToSGMVResponse, chan bool, error) { responseChan := make(chan addVolumeToSGMVResponse, 1) sgStateP, key := g.getUpdateStorageGroupState(symID, tgtStorageGroupID) @@ -159,6 +161,7 @@ func (g *storageGroupSvc) requestAddVolumeToSGMV(_ context.Context, tgtStorageGr devID: devID, accessMode: accessMode, respChan: responseChan, + volumeName: volumeName, } log.Infof("Adding %v to queue: %s", addVolumeRequest, key) sgStateP.addVolumeToSGMVQueue <- addVolumeRequest @@ -190,6 +193,7 @@ func (g *storageGroupSvc) requestRemoveVolumeFromSGMV(_ context.Context, tgtStor // runAddVolumesToSGMV gets the applicable number of requests, calls addVolumesToSGMV to process them, // handles any errors by calling handleAddVolumeToSGMV error, and makes sure each volume gets a return response to the error channel func (g *storageGroupSvc) runAddVolumesToSGMV(ctx context.Context, symID, tgtStorageGroupID string, _ pmax.Pmax) { + log := log.WithContext(ctx) // Get the key and defer releasing the lock key := getKey(symID, tgtStorageGroupID) sgStateInterface, ok := g.updateSGStateMap.Load(key) @@ -247,6 +251,7 @@ func (g *storageGroupSvc) runAddVolumesToSGMV(ctx context.Context, symID, tgtSto // handleAddVolumeToSGMVError handles case where a volume received error and may need to be retried func (g *storageGroupSvc) handleAddVolumeToSGMVError(ctx context.Context, req *addVolumeToSGMVRequest) { + log := log.WithContext(ctx) pmaxClient, err := symmetrix.GetPowerMaxClient(req.clientSymID) if err != nil { log.Error(err.Error()) @@ -292,6 +297,7 @@ func (g *storageGroupSvc) handleAddVolumeToSGMVError(ctx context.Context, req *a // released when we return to the caller, there is nothing more for him to do other than // continue polling until his request completes or it's his turn to run the service again. func (g *storageGroupSvc) runRemoveVolumesFromSGMV(ctx context.Context, symID, tgtStorageGroupID string) { + log := log.WithContext(ctx) // Get the key and defer releasing the lock key := getKey(symID, tgtStorageGroupID) // Pull a request off the queue and process it @@ -330,6 +336,7 @@ func (g *storageGroupSvc) runRemoveVolumesFromSGMV(ctx context.Context, symID, t // handleRemoveVolumeFromSGMVError handles case where a volume received error and may need to be retried func (g *storageGroupSvc) handleRemoveVolumeFromSGMVError(ctx context.Context, req *removeVolumeFromSGMVRequest) { + log := log.WithContext(ctx) pmaxClient, err := symmetrix.GetPowerMaxClient(req.clientSymID) if err != nil { log.Error(err.Error()) @@ -367,6 +374,7 @@ func (g *storageGroupSvc) handleRemoveVolumeFromSGMVError(ctx context.Context, r // getRequestsForAddVolumesToSG gets zero to maxAddGroupSize requests for the sgState passed in and returns a request list and devID list func (g *storageGroupSvc) getRequestsForAddVolumesToSG(ctx context.Context, sgState *updateStorageGroupState) ([]*addVolumeToSGMVRequest, []string) { + log := log.WithContext(ctx) var initialReq *addVolumeToSGMVRequest devIDs := make([]string, 0) requests := make([]*addVolumeToSGMVRequest, 0) @@ -378,7 +386,7 @@ func (g *storageGroupSvc) getRequestsForAddVolumesToSG(ctx context.Context, sgSt initialReq = req } if req.symID == initialReq.symID && req.tgtStorageGroupID == initialReq.tgtStorageGroupID && req.tgtMaskingViewID == initialReq.tgtMaskingViewID { - addToSG, createMV, err := g.addVolumeToSGMVVolumeCheck(ctx, req.clientSymID, req.symID, req.devID, req.tgtStorageGroupID, req.tgtMaskingViewID, req.accessMode) + addToSG, createMV, err := g.addVolumeToSGMVVolumeCheck(ctx, req.clientSymID, req.symID, req.devID, req.tgtStorageGroupID, req.tgtMaskingViewID, req.volumeName, req.accessMode) if err != nil { // received an error response := addVolumeToSGMVResponse{ @@ -414,6 +422,7 @@ func (g *storageGroupSvc) getRequestsForAddVolumesToSG(ctx context.Context, sgSt // getRequestsForRemoveVolumesFromSG gets zero to maxRemoveGroupSize requests for the sgState passed in and returns a request list and devID list func (g *storageGroupSvc) getRequestsForRemoveVolumesFromSG(ctx context.Context, sgState *updateStorageGroupState) ([]*removeVolumeFromSGMVRequest, []string) { + log := log.WithContext(ctx) var initialReq *removeVolumeFromSGMVRequest devIDs := make([]string, 0) requests := make([]*removeVolumeFromSGMVRequest, 0) @@ -443,6 +452,7 @@ func (g *storageGroupSvc) getRequestsForRemoveVolumesFromSG(ctx context.Context, } func (g *storageGroupSvc) processSingleAddVolumeToSGMV(ctx context.Context, req *addVolumeToSGMVRequest) { + log := log.WithContext(ctx) log.Infof("Single addVolumesToSGMV: %v", req) devIDs := make([]string, 1) devIDs[0] = req.devID @@ -464,6 +474,7 @@ func (g *storageGroupSvc) processSingleAddVolumeToSGMV(ctx context.Context, req // Process a single request, sending the error (if any) back to the caller func (g *storageGroupSvc) processSingleRemoveVolumeFromSG(ctx context.Context, req *removeVolumeFromSGMVRequest) { + log := log.WithContext(ctx) log.Infof("Single removeVolumesFromSGMV: %v", req) devIDs := make([]string, 1) devIDs[0] = req.devID @@ -478,25 +489,32 @@ func (g *storageGroupSvc) processSingleRemoveVolumeFromSG(ctx context.Context, r // and that the access modes are compatible. // Returns bool addVolumeToSG, bool addVolumeToMV (i.e. create MV), and error // Can return true, true, nil -- add volume to SG; false, true, nil -- volume already in SG, check MV; false, false, nil -- done; false, false, err -- Error occured -func (g *storageGroupSvc) addVolumeToSGMVVolumeCheck(ctx context.Context, clientSymID, symID, devID, tgtStorageGroupID, tgtMaskingViewID string, am *csi.VolumeCapability_AccessMode) (bool, bool, error) { +func (g *storageGroupSvc) addVolumeToSGMVVolumeCheck(ctx context.Context, clientSymID, symID, devID, tgtStorageGroupID, tgtMaskingViewID, _ string, am *csi.VolumeCapability_AccessMode) (bool, bool, error) { + log := log.WithContext(ctx) pmaxClient, err := symmetrix.GetPowerMaxClient(clientSymID) if err != nil { log.Error(err.Error()) return false, false, err } + volumeAlreadyInTargetSG := false + volumeInTargetMaskingView := false + maskingViewIDs := make([]string, 0) + noOfHosts := 0 + var storageGroups []*types.StorageGroup + vol, err := pmaxClient.GetVolumeByID(ctx, symID, devID) if err != nil { - log.Error("Error retreiving volume: " + devID + ": " + err.Error()) + log.Error("Error retrieving volume: " + devID + ": " + err.Error()) return false, false, err } - volumeAlreadyInTargetSG := false + noOfHosts = vol.NumberOfFrontEndPaths currentSGIDs := vol.StorageGroupIDList for _, sgID := range currentSGIDs { if sgID == tgtStorageGroupID { volumeAlreadyInTargetSG = true } } - maskingViewIDs, storageGroups, err := g.svc.GetMaskingViewAndSGDetails(ctx, symID, currentSGIDs, pmaxClient) + maskingViewIDs, storageGroups, err = g.svc.GetMaskingViewAndSGDetails(ctx, symID, currentSGIDs, pmaxClient) if err != nil { log.Error("GetMaskingViewAndSGDetails Error: " + err.Error()) return false, false, err @@ -509,20 +527,22 @@ func (g *storageGroupSvc) addVolumeToSGMVVolumeCheck(ctx context.Context, client } } } - volumeInTargetMaskingView := false + // First check if our masking view is in the existsing list for _, mvID := range maskingViewIDs { if mvID == tgtMaskingViewID { volumeInTargetMaskingView = true } } + + // till here we have to remove for new API we don't this code block switch am.Mode { case csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER, csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER, csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY: // Check if the volume is already mapped to some host - if vol.NumberOfFrontEndPaths > 0 { + if noOfHosts > 0 { // make sure we got at least one masking view if len(maskingViewIDs) == 0 { // this is an error, we should have gotten at least one MV @@ -551,15 +571,16 @@ func (g *storageGroupSvc) addVolumeToSGMVVolumeCheck(ctx context.Context, client // addVolumesToSGMV adds volumes to StorageGroup and Masking View for a specific symID, tgtStorageGroupID, tgtMaskingViewID, hostID // devIDs can be of zero length, in which case we'll just ensure the masking view exists func (g *storageGroupSvc) addVolumesToSGMV(ctx context.Context, reqID, symID, tgtStorageGroupID, tgtMaskingViewID, hostID string, devIDs []string, pmaxClient pmax.Pmax) error { + log := log.WithContext(ctx) var maskingViewExists bool var err error - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "MaskingView": tgtMaskingViewID, "SymmetrixID": symID, "StorageGroup": tgtStorageGroupID, } - log.WithFields(f).Infof("addVolumesToSGMV processing: %s", devIDs) + tgtMaskingView := &types.MaskingView{} // Fetch the masking view tgtMaskingView, err = pmaxClient.GetMaskingViewByID(ctx, symID, tgtMaskingViewID) @@ -574,9 +595,10 @@ func (g *storageGroupSvc) addVolumesToSGMV(ctx context.Context, reqID, symID, tg if (strings.EqualFold(tgtMaskingView.HostID, hostID) || strings.EqualFold(tgtMaskingView.HostGroupID, hostID)) && strings.EqualFold(tgtMaskingView.StorageGroupID, tgtStorageGroupID) { // Add the volumes to masking view, if any to be added if len(devIDs) > 0 { + log.WithFields(f).Infof("addVolumesToSGMV processing: %s", devIDs) log.WithFields(f).Info("Calling AddVolumesToStorageGroup") start := time.Now() - err := pmaxClient.AddVolumesToStorageGroupS(ctx, symID, tgtStorageGroupID, true, devIDs...) + err = pmaxClient.AddVolumesToStorageGroupS(ctx, symID, tgtStorageGroupID, true, devIDs...) if err != nil { log.WithFields(f).Errorf("ControllerPublishVolume: Failed to add devices %s to storage group: %s", devIDs, err) return status.Error(codes.Internal, "Failed to add volume to storage group: "+err.Error()) @@ -644,15 +666,31 @@ func (g *storageGroupSvc) addVolumesToSGMV(ctx context.Context, reqID, symID, tg } // Add the volumes to storage group if len(devIDs) > 0 { - log.WithFields(f).Info("calling AddVolumesToStorageGroup") - start := time.Now() - err = pmaxClient.AddVolumesToStorageGroup(ctx, symID, tgtStorageGroupID, true, devIDs...) + // get volume first, here if exists then proceed else return + pmaxVolume, err := pmaxClient.GetVolumeByID(ctx, symID, devIDs[0]) if err != nil { - log.WithFields(f).Errorf("ControllerPublishVolume: Failed to add device - %s to storage group", devIDs) - return status.Error(codes.Internal, "Failed to add volume to storage group: "+err.Error()) + log.WithFields(f).Debug("Failed to get volume from array") + return status.Error(codes.Internal, "Failed to get volume from array: "+err.Error()) + } + var volFound bool + for _, v := range pmaxVolume.StorageGroupIDList { + if v == tgtStorageGroupID { + log.WithFields(f).Debugf("Volume already present in storage group:%v", tgtStorageGroupID) + volFound = true + } + } + + if !volFound { + log.WithFields(f).Info("calling AddVolumesToStorageGroup") + start := time.Now() + err = pmaxClient.AddVolumesToStorageGroup(ctx, symID, tgtStorageGroupID, true, devIDs...) + if err != nil { + log.WithFields(f).Errorf("ControllerPublishVolume: Failed to add device - %s to storage group", devIDs) + return status.Error(codes.Internal, "Failed to add volume to storage group: "+err.Error()) + } + dur := time.Now().Sub(start) + log.WithFields(f).Infof("AddVolumesToStorageGroup time %d %f", len(devIDs), dur.Seconds()) } - dur := time.Now().Sub(start) - log.WithFields(f).Infof("AddVolumesToStorageGroup time %d %f", len(devIDs), dur.Seconds()) } _, err = pmaxClient.CreateMaskingView(ctx, symID, tgtMaskingViewID, tgtStorageGroupID, hostID, true, portGroupID) if err != nil { @@ -664,13 +702,14 @@ func (g *storageGroupSvc) addVolumesToSGMV(ctx context.Context, reqID, symID, tg } func (g *storageGroupSvc) removeVolumesFromSGMV(ctx context.Context, clientSymID, tgtStorageGroupID, tgtMaskingViewID, reqID, symID string, devIDs []string) error { + log := log.WithContext(ctx) var err error pmaxClient, err := symmetrix.GetPowerMaxClient(clientSymID) if err != nil { log.Error(err.Error()) return err } - f := log.Fields{ + f := csmlog.Fields{ "CSIRequestID": reqID, "MaskingView": tgtMaskingViewID, "SymmetrixID": symID, diff --git a/service/string_slice.go b/service/string_slice.go index 7c14b720..5cb90f43 100644 --- a/service/string_slice.go +++ b/service/string_slice.go @@ -18,8 +18,6 @@ import ( "reflect" "regexp" "sort" - - log "github.com/sirupsen/logrus" ) // stringSlicesEqual returns true IFF two slices contain same members diff --git a/service/vsphere.go b/service/vsphere.go index 7953a110..69b1ef09 100644 --- a/service/vsphere.go +++ b/service/vsphere.go @@ -25,8 +25,6 @@ import ( "reflect" "strings" - log "github.com/sirupsen/logrus" - "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/mo" diff --git a/service/vsphere_test.go b/service/vsphere_test.go index 5f6bd791..9c8014d6 100644 --- a/service/vsphere_test.go +++ b/service/vsphere_test.go @@ -237,6 +237,7 @@ func TestRemoveLunDevice(t *testing.T) { Label: "test device", }, }) + mapSDI["lunUUID"] = &types.ScsiLun{ CanonicalName: "deviceNAA", } @@ -260,6 +261,7 @@ func TestRemoveLunDevice(t *testing.T) { Label: "test device", }, }) + mapSDI["lunUUID"] = &types.ScsiLun{ CanonicalName: "deviceNAA", } diff --git a/test/integration/README.md b/test/integration/README.md deleted file mode 100644 index 746b8d47..00000000 --- a/test/integration/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# CSI-PowerMax Integration Tests - -## How to run the integration tests -1. Update csi-powermax/env.sh with values for your Powermax system. -2. run `export PATH=$PATH:/usr/local/go/bin` -3. run `export KUBECONFIG=/root/.kube/config` -4. run `bash run.sh` - -### If you want to only run some test scenarios: - 1. Add a `tag` on the tests - 2. Update the `Tags` section in file _integration_test.go_ with the same tag - -### Notes on the replication test scenarios: -- Scenarios `SRDF Actions on a protected SG` and `Get Status of a protected SG` are not currently running due to timing issues in the tests themselves -- they both execute a failover on Unisphere and expect it to be done in less than a second, which is not realisitic. -- Scenarios `Create and delete replication volume with auto SRDF group` and `Create and delete replication volume with HostIOLimits` only pass with manual intervention. When the test gets stuck deleting a volume, the user needs to go to the remote array in Unisphere and manually remove the volume from any storage groups it is in and then delete it. diff --git a/test/integration/features/integration.feature b/test/integration/features/integration.feature deleted file mode 100644 index 37df499f..00000000 --- a/test/integration/features/integration.feature +++ /dev/null @@ -1,1026 +0,0 @@ -Feature: Powermax OS CSI interface - As a consumer of the CSI interface - I want to run a system test - So that I know the service functions correctly. - - @v1.0.0 - Scenario: Create and delete basic thick large volume - Given a Powermax service - And a basic block volume request "integration1" "65536" - And I use thick provisioning - When I call CreateVolume - And I receive a valid volume - # When I call ListVolume - # Then a valid ListVolumeResponse is returned - And when I call DeleteVolume - Then there are no errors - And there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Create and delete basic volume using Gold Service Level - Given a Powermax service - And a basic block volume request "integration1" "50" - And an alternate ServiceLevel "Gold" - When I call CreateVolume - And I receive a valid volume - # When I call ListVolume - # Then a valid ListVolumeResponse is returned - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.1.0 - Scenario Outline: Create and delete basic volume using various volume sizes - Given a Powermax service - And a basic block volume request "integration11" - When I call CreateVolume - And the volume size is - # When I call ListVolume - # Then a valid ListVolumeResponse is returned - And when I call DeleteVolume - Then the error message should contain - And all volumes are deleted successfully - - Examples: - | sizeMB | expectedGB | errormsg | - | "0" | "1.00" | "none" | - | "8192" | "8.00" | "none" | -# 2 TB - | "2097152" | "2048.00" | "none" | - | "100000000" | "0.00" | "The device size specified exceeds the maximum allowed" | - - @v1.0.0 -# This test checks an important DL scenario, that to delete a volume -# it must match not only the symmetrix ID and device ID, but also the volume name. - Scenario: Create volume, try to delete it with incorrect CSI VolumeID, then delete it. - Given a Powermax service - And a basic block volume request "integration1" "50" - And an alternate ServiceLevel "Gold" - When I call CreateVolume - # When I call ListVolume - # Then a valid ListVolumeResponse is returned - And I munge the CSI VolumeId - And when I call DeleteVolume - Then the volume is not deleted - And I unmunge the CSI VolumeId - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Idempotent create and delete basic volume - Given a Powermax service - And a basic block volume request "integration2" "50" - When I call CreateVolume - And I call CreateVolume - And I receive a valid volume - And when I call DeleteVolume - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Create and delete mount volume - Given a Powermax service - And a mount volume request "integration5" - When I call CreateVolume - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Create, publish, unpublish and delete basic volume - Given a Powermax service - And a basic block volume request "integration5" "100" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.1.0 - @xwip - Scenario: Create publish, node stage, node publish, node unpublish, node unstage, unpublish, delete basic volume - Given a Powermax service - And a mount volume request "integration6" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - @xwip - Scenario: Idempotent create publish, node publish, node unpublish, unpublish, delete basic volume - Given a Powermax service - And an idempotent test - And a mount volume request "integration5" - When I call CreateVolume - And there are no errors - And I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Create and delete basic volume to maximum capacity - Given a Powermax service - And max retries 1 - And a basic block volume request "integration4" "1048576" - When I call CreateVolume - And I receive a valid volume - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Create and delete basic volume beyond maximum capacity - Given a Powermax service - And max retries 1 - And a basic block volume request "integration4" "1073741824" - When I call CreateVolume - Then the error message should contain "OutOfRange desc = bad capacity" - - @v1.0.0 - Scenario: Create and delete basic volume of minimum capacity - Given a Powermax service - And max retries 1 - And a basic block volume request "integration4" "50" - When I call CreateVolume - And I receive a valid volume - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Create and delete basic volume below minimum capacity - Given a Powermax service - And max retries 1 - And a basic block volume request "integration4" "48" - When I call CreateVolume - And I receive a valid volume - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.0.0 - Scenario: Call GetCapacity - Given a Powermax service - And a get capacity request "SRP_1" - And I call GetCapacity - Then a valid GetCapacityResponse is returned - - Scenario: Create volume, create snapshot, create volume from snapshot, delete original volume, delete new volume - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And I call CreateSnapshot - And there are no errors - And I call ListVolume - And a valid ListVolumeResponse is returned - And I call ListSnapshot - And a valid ListSnapshotResponse is returned - And I call LinkVolumeToSnapshot - Then there are no errors - And I call ListVolume - And a valid ListVolumeResponse is returned - And I call DeleteSnapshot - And there are no errors - And when I call DeleteVolume - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - And I call ListVolume - - Scenario: Create volume, create snapshot, create many volumes from snap, delete original volume, delete new volumes - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - And I call CreateManyVolumesFromSnapshot - Then the error message should contain "too many volumes in the VTree" - And I call DeleteSnapshot - And when I call DeleteVolume - And when I call DeleteAllVolumes - - - Scenario: Create volume, idempotent create snapshot, delete volume - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And I call CreateSnapshot - And there are no errors - And I call CreateSnapshot - And there are no errors - And I call DeleteSnapshot - And there are no errors - And I call DeleteSnapshot - And there are no errors - And when I call DeleteVolume - Then there are no errors - - Scenario: Create multiple volumes, create snapshot of consistency group, delete volumes - Given a Powermax service - And a basic block volume request "integration1" "8" - When I call CreateVolume - And a basic block volume request "integration2" "8" - And I call CreateVolume - And a basic block volume request "integration3" "8" - And I call CreateVolume - And I call CreateSnapshotConsistencyGroup - And there are no errors - Then I call DeleteSnapshot - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - Scenario Outline: Create publish, node-publish, node-unpublish, unpublish, and delete basic volume - Given a Powermax service - And a capability with voltype access fstype - And a volume request "integration5" "8" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "nodeID" - And when I call NodeStageVolume "nodeID" - And when I call NodePublishVolume "nodeID" - And verify published volume with voltype access fstype - And when I call NodePublishVolume "nodeID" - And when I call NodeUnstageVolume "nodeID" - And when I call NodeUnpublishVolume "nodeID" - And when I call UnpublishVolume "nodeID" - And when I call DeleteVolume - Then the error message should contain - - Examples: - | voltype | access | fstype | errormsg | - | "mount" | "single-writer" | "xfs" | "none" | - | "mount" | "single-writer" | "ext4" | "none" | - | "mount" | "multi-writer" | "ext4" | "multi-writer not allowed" | - | "block" | "single-writer" | "none" | "none" | - | "block" | "multi-writer" | "none" | "none" | - | "block" | "single-writer" | "none" | "none" | - | "mount" | "single-node-single-writer" | "xfs" | "none" | - | "mount" | "single-node-single-writer" | "ext4" | "none" | - | "block" | "single-node-single-writer" | "none" | "none" | - - - Scenario: Create publish, unpublish, and delete basic volume - Given a Powermax service - And a basic block volume request "integration5" "8" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "nodeID" - And there are no errors - And when I call UnpublishVolume "nodeID" - And there are no errors - And when I call DeleteVolume - Then there are no errors - - Scenario: Multi-host create publish, unpublish, and delete basic volume - Given a Powermax service - And a basic block volume request "integration6" "8" - And access type is "multi-writer" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "nodeID" - And there are no errors - And when I call PublishVolume "ALT_GUID" - And there are no errors - And when I call UnpublishVolume "nodeID" - And there are no errors - And when I call UnpublishVolume "ALT_GUID" - And there are no errors - And when I call DeleteVolume - Then there are no errors - - Scenario: Create and delete basic 100000G volume - Given a Powermax service - And max retries 1 - And a basic block volume request "integration4" "100000" - When I call CreateVolume - And when I call DeleteVolume - Then the error message should contain "The capacity of the devices in this Storage Pool does not allow for this amount of thin capacity allocation" - - Scenario: Create and delete basic 96G volume - Given a Powermax service - And max retries 10 - And a basic block volume request "integration3" "96" - When I call CreateVolume - And when I call DeleteVolume - Then there are no errors - - @v1.0.0 - @xwip - Scenario Outline: Scalability test to create volumes, publish, node publish, node unpublish, unpublish, delete volumes in parallel - Given a Powermax service - When I create volumes in parallel - And there are no errors - And I publish volumes in parallel - And there are no errors - And I node stage volumes in parallel - And there are no errors - And I node publish volumes in parallel - And there are no errors - And I node unpublish volumes in parallel - And there are no errors - And I node unstage volumes in parallel - And there are no errors - And I unpublish volumes in parallel - And there are no errors - And when I delete volumes in parallel - Then there are no errors - And all volumes are deleted successfully - - Examples: - | numberOfVolumes | - | 1 | - | 2 | - | 5 | - | 8 | - # | 10 | - # | 20 | - # | 50 | - # | 100 | - # | 200 | - - @v1.1.0 - @xwip - Scenario Outline: Scalability test to create volumes, publish, node stage, node publish, node unpublish, node unstage, unpublish, delete volumes in parallel - Given a Powermax service - When I create volumes in parallel - And there are no errors - And I publish volumes in parallel - And there are no errors - And I node stage volumes in parallel - And there are no errors - And I node publish volumes in parallel - And there are no errors - And I node unpublish volumes in parallel - And there are no errors - And I node unstage volumes in parallel - And there are no errors - And I unpublish volumes in parallel - And there are no errors - And when I delete volumes in parallel - Then there are no errors - And all volumes are deleted successfully - - Examples: - | numberOfVolumes | - | 1 | - | 2 | - | 5 | - | 8 | - # | 10 | - # | 20 | - # | 50 | - # | 100 | - # | 200 | - - @v1.0.0 - Scenario Outline: Idempotent create volumes, publish, node stage, node publish, node unpublish, node unstage, unpublish, delete volumes in parallel - Given a Powermax service - When I create volumes in parallel - And there are no errors - When I create volumes in parallel - And there are no errors - And I publish volumes in parallel - And there are no errors - And I publish volumes in parallel - And there are no errors - And I node stage volumes in parallel - And there are no errors - And I node stage volumes in parallel - And there are no errors - And I node publish volumes in parallel - And there are no errors - And I node publish volumes in parallel - And there are no errors - And I node unpublish volumes in parallel - And there are no errors - And I node unpublish volumes in parallel - And there are no errors - And I node unstage volumes in parallel - And there are no errors - And I node unstage volumes in parallel - And there are no errors - And I unpublish volumes in parallel - And there are no errors - And I unpublish volumes in parallel - And there are no errors - And when I delete volumes in parallel - And there are no errors - And when I delete volumes in parallel - Then there are no errors - - Examples: - | numberOfVolumes | - | 1 | - #| 10 | - #| 20 | - - @v1.0.0 - Scenario Outline: Create publish, node-publish, node-unpublish, unpublish, and delete basic volume - Given a Powermax service - And a volume request with file system "integration5" fstype access voltype - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - - Examples: - | fstype | access | voltype | - | "xfs" | "single-writer" | "mount" | - | "ext4" | "single-writer" | "mount" | - - - @v1.2.0 - Scenario: Create volume, create snapshot, create volume from snapshot, create volume from volume - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - And I call LinkVolumeToSnapshot - Then there are no errors - And I call LinkVolumeToVolume - And there are no errors - Then I call DeleteSnapshot - And there are no errors - And when I call DeleteVolume - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Create 'n' snapshots from a volume in parallel - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I create 10 snapshots in parallel - And there are no errors - Then I call DeleteAllSnapshots - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Create 'n' volumes from snapshot in parallel - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - And I create 10 volumes from snapshot in parallel - And there are no errors - Then I call DeleteSnapshot - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Create 'n' volumes from volume in parallel - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I create 4 volumes from volume in parallel - And there are no errors - Then when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Delete snapshot - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - And I call DeleteSnapshot - And there are no errors - Then when I call DeleteVolume - And there are no errors - - - @v1.2.0 - Scenario: Delete 'n' snapshots in parallel - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I create 10 snapshots in parallel - And there are no errors - Then I call DeleteSnapshot in parallel - And there are no errors - Then when I call DeleteVolume - And there are no errors - - @v1.2.0 - Scenario: Delete Snapshot then the Volume having a snapshot - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I create 2 snapshots in parallel - And there are no errors - Then I call DeleteAllSnapshots - And there are no errors - Then when I call DeleteVolume - And there are no errors - - @v1.2.0 - Scenario: Deleting and creating a snapshot concurrently on source and target volumes - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - And I call LinkVolumeToSnapshot - And there are no errors - Then I call DeleteSnapshot and CreateSnapshot in parallel - And there are no errors - And I call DeleteSnapshot - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Creating a Snapshot on newly created Volume from Snasphot - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - Then I call LinkVolumeToSnapshot - And there are no errors - And I call CreateSnapshot on new volume - And there are no errors - Then I call DeleteAllSnapshots - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Deleting Target Volume then the Source Volume - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - Then I call LinkVolumeToSnapshot - And there are no errors - Then I call LinkVolumeToSnapshot - And there are no errors - And when I call DeleteLocalVolume - And there are no errors - Then I call DeleteSnapshot - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Creating a Volume from newly created Volume from Volume - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - Then I call LinkVolumeToVolume - And there are no errors - Then I call CreateVolume from new volume - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @v1.2.0 - Scenario: Checks if a soft deleted volume is hard deleted - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I create 2 snapshots in parallel - And there are no errors - And when I call DeleteVolume - And there are no errors - And I delete a snapshot - And there are no errors - Then I check if volume exist - And there are no errors - And I delete a snapshot - And there are no errors - Then I check if volume is deleted - And there are no errors - - @wip - @v1.4.0 - Scenario: Expand Mount Volume - Given a Powermax service - And a mount volume request "integration6" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call ExpandVolume to 100 cylinders - And there are no errors - And when I call NodeExpandVolume - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.4.0 - Scenario: Expand Mount Volume with Capacity Bytes not set - Given a Powermax service - And a mount volume request "integration6" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call ExpandVolume to 0 cylinders - Then the error message should contain "Invalid argument" - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @wip - @v1.4.0 - Scenario: Expand Raw Block Volume - Given a Powermax service - And a mount volume request "integration6" - And a basic block volume request "integration7" "100" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call ExpandVolume to 100 cylinders - And there are no errors - And when I call NodeExpandVolume - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v1.4.0 - Scenario Outline: Create and Delete 'n' Snapshots from 'n' Volumes in parallel and retry for failing snapshot request - Given a Powermax service - And a basic block volume request "integration1" "50" - When I create volumes in parallel - And there are no errors - And I create a snapshot per volume in parallel - And there are no errors - Then I call DeleteSnapshot in parallel - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - Examples: - | n | - | 10 | - | 20 | - | 35 | - - @v1.6.0 - Scenario: Create and delete replication volume - Given a Powermax service - And a basic block volume request "integration1" "100" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And I receive a valid volume - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v1.6.0 - Scenario: Idempotent create and delete replication volume - Given a Powermax service - And a basic block volume request "integration1" "100" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And I call CreateVolume - And I receive a valid volume - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And when I call DeleteVolume - And when I call DeleteVolume - Then there are no errors - And I call DeleteLocalVolume - Then there are no errors - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v1.6.0 - Scenario: Create, publish, unpublish and delete replication volume - Given a Powermax service - And a basic block volume request "integration1" "100" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v1.6.0 - Scenario: Create and delete replication mount volume - Given a Powermax service - And a mount volume request "integration5" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And there are no errors - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v1.6.0 - Scenario: SRDF Actions on a protected SG - Given a Powermax service - And a basic block volume request "integration1" "100" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And I receive a valid volume - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call ExecuteAction with action "Failover" - And there are no errors - And I call ExecuteAction with action "Failback" - And there are no errors - And I call ExecuteAction with action "Suspend" - And there are no errors - And I call ExecuteAction with action "Resume" - And there are no errors - And I call ExecuteAction with action "Resume" - And there are no errors - And I call ExecuteAction with action "Suspend" - And there are no errors - And I call ExecuteAction with action "Establish" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v1.6.0 - Scenario: Get Status of a protected SG - Given a Powermax service - And a basic block volume request "integration1" "100" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And I receive a valid volume - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call ExecuteAction with action "Failover" - And there are no errors - And I call GetStorageProtectionGroupStatus to get "FAILEDOVER" - And there are no errors - And I call ExecuteAction with action "Failback" - And there are no errors - And I call GetStorageProtectionGroupStatus to get "SYNCHRONIZED" - And there are no errors - And I call ExecuteAction with action "Suspend" - And there are no errors - And I call GetStorageProtectionGroupStatus to get "SUSPENDED" - And there are no errors - And I call ExecuteAction with action "Resume" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v2.2.0 - Scenario: Volume Health Monitoring method - Given a Powermax service - And a mount volume request "integration6" - When I call CreateVolume - And there are no errors - And when I call PublishVolume "Node1" - And there are no errors - And when I call NodeStageVolume "Node1" - And there are no errors - And when I call NodePublishVolume "Node1" - And there are no errors - And when I call ControllerGetVolume - And there are no errors - And when I call NodeGetVolumeStats - And there are no errors - And when I call NodeUnpublishVolume "Node1" - And there are no errors - And when I call NodeUnstageVolume "Node1" - And there are no errors - And when I call UnpublishVolume "Node1" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And all volumes are deleted successfully - - @v2.4.0 - Scenario: Create and delete replication volume with auto SRDF group - Given a Powermax service - And a basic block volume request "integration1" "100" - And adds auto SRDFG replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And I receive a valid volume - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And when I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @v2.7.0 - Scenario: Create and delete replication volume with HostIOLimits - Given a Powermax service - And I have SetHostIOLimits on the storage group - And a basic block volume request "integration1" "100" - And adds replication capability with mode "ASYNC" namespace "INT" - When I call CreateVolume - And I receive a valid volume - And I call CreateStorageProtectionGroup with mode "ASYNC" - And there are no errors - And I call CreateRemoteVolume with mode "ASYNC" - And there are no errors - And when I call DeleteVolume - Then there are no errors - And when I call DeleteLocalVolume - Then there are no errors - And all volumes are deleted successfully - And I call Delete LocalStorageProtectionGroup - And there are no errors - And I call Delete RemoteStorageProtectionGroup - And there are no errors - - @2.12.0 - Scenario: Creating a Volume from newly created Volume from Volume - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - Then I call LinkVolumeToVolume - And there are no errors - Then I call LinkVolumeToVolumeAgain - And there are no errors - And when I call DeleteAllVolumes - And there are no errors - - @2.12.0 - Scenario: Creating a Volume from newly created Spanshot - Given a Powermax service - And a basic block volume request "integration1" "50" - When I call CreateVolume - And there are no errors - And I call CreateSnapshot - And there are no errors - Then I call LinkVolumeToSnapshot - And there are no errors - Then I call LinkVolumeToSnapshotAgain - And there are no errors - And when I call DeleteAllVolumes - And there are no errors diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go deleted file mode 100644 index 098efa9b..00000000 --- a/test/integration/integration_test.go +++ /dev/null @@ -1,199 +0,0 @@ -/* -Copyright © 2021-2025 Dell Inc. or its subsidiaries. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package integration_test - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - log "github.com/sirupsen/logrus" - "google.golang.org/grpc/backoff" - "google.golang.org/grpc/credentials/insecure" - - csi "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/cucumber/godog" - "github.com/dell/csi-powermax/v2/provider" - "github.com/dell/csi-powermax/v2/service" - csiutils "github.com/dell/gocsi/utils/csi" - "google.golang.org/grpc" -) - -const ( - datafile = "/tmp/datafile" - datadir = "/tmp/datadir" - defaultTimeout = 5 * time.Minute -) - -var grpcClient *grpc.ClientConn - -func readTimeoutFromEnv() time.Duration { - contextTimeout := defaultTimeout - if timeoutStr := os.Getenv("X_CSI_UNISPHERE_TIMEOUT"); timeoutStr != "" { - if timeout, err := time.ParseDuration(timeoutStr); err == nil { - contextTimeout = timeout - } - } - return contextTimeout -} - -func TestMain(m *testing.M) { - var stop func() - // Block must be enabled for many tests to work - os.Setenv(service.EnvEnableBlock, "true") - ctx, cancel := context.WithTimeout(context.Background(), readTimeoutFromEnv()) - defer cancel() - fmt.Println("*** calling startServer ***") - var err error - grpcClient, stop, err = startServer(ctx) - if err != nil { - log.Errorf("Failed to create a grpc client: %s", err.Error()) - os.Exit(1) - } - fmt.Println("*** back from startServer ***") - - // Make the directory and file needed for NodePublish, these are: - // /tmp/datadir -- for file system mounts - // /tmp/datafile -- for block bind mounts - fmt.Printf("Checking %s\n", datadir) - var fileMode os.FileMode - fileMode = 0o777 - err = os.Mkdir(datadir, fileMode) - if err != nil && !os.IsExist(err) { - fmt.Printf("%s: %s\n", datadir, err) - } - fmt.Printf("Checking %s\n", datafile) - file, err := os.Create(datafile) - if err != nil && !os.IsExist(err) { - fmt.Printf("%s %s\n", datafile, err) - } - if file != nil { - file.Close() - } - - time.Sleep(10 * time.Second) - var exitVal int - if st := m.Run(); st > exitVal { - exitVal = st - } - write, err := os.Create("powermax_integration_testresults.xml") - runOptions := godog.Options{ - Output: write, - Format: "junit", - Paths: []string{"features"}, - Tags: "@v1.0.0,@v1.1.0,@v1.2.0,@v1.4.0,@v1.6.0,@v2.2.0,@v2.4.0,@v2.7.0,@2.12.0", - } - godogExit := godog.TestSuite{ - Name: "CSI Powermax Int Tests", - ScenarioInitializer: FeatureContext, - Options: &runOptions, - }.Run() - - if godogExit > exitVal { - exitVal = godogExit - } - stop() - os.Exit(exitVal) -} - -func TestIdentityGetPluginInfo(t *testing.T) { - ctx := context.Background() - fmt.Printf("testing GetPluginInfo\n") - client := csi.NewIdentityClient(grpcClient) - info, err := client.GetPluginInfo(ctx, &csi.GetPluginInfoRequest{}) - if err != nil { - fmt.Printf("GetPluginInfo %s:\n", err.Error()) - t.Error("GetPluginInfo failed") - } else { - fmt.Printf("testing GetPluginInfo passed: %s\n", info.GetName()) - } -} - -func TestNodeGetInfo(t *testing.T) { - ctx := context.Background() - fmt.Printf("testing NodeGetInfo\n") - client := csi.NewNodeClient(grpcClient) - info, err := client.NodeGetInfo(ctx, &csi.NodeGetInfoRequest{}) - if err != nil { - fmt.Printf("NodeGetInfo %s:\n", err.Error()) - t.Error("NodeGetInfo failed") - } else { - if info.AccessibleTopology == nil { - t.Error("NodeGetInfo failed, no topology keys found") - } - fmt.Printf("testing NodeGetInfo passed:\n %+v\n", info) - } -} - -func startServer(ctx context.Context) (*grpc.ClientConn, func(), error) { - // Create a new SP instance and serve it with a piped connection. - sp := provider.New() - lis, err := csiutils.GetCSIEndpointListener() - if err != nil { - fmt.Printf("couldn't open listener: %s\n", err.Error()) - return nil, nil, err - } - go func() { - fmt.Printf("starting server\n") - if err := sp.Serve(ctx, lis); err != nil { - fmt.Printf("http: Server closed") - } - }() - network, addr, err := csiutils.GetCSIEndpoint() - if err != nil { - return nil, nil, err - } - fmt.Printf("network %v addr %v\n", network, addr) - - clientOpts := []grpc.DialOption{ - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithConnectParams(grpc.ConnectParams{ - Backoff: backoff.DefaultConfig, - MinConnectTimeout: 10 * time.Second, - }), - grpc.WithBlock(), - } - - var conn *grpc.ClientConn - ready := make(chan bool) - address := "unix:" + addr - go func() { - // Create a client for the piped connection. - fmt.Printf("calling gprc.DialContext, ctx %v, addr %s, clientOpts %v\n", ctx, addr, clientOpts) - conn, err = grpc.Dial(address, clientOpts...) - close(ready) - }() - - ticker := time.NewTicker(10 * time.Second) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - log.Infof("Still connecting to %s", address) - case <-ready: - if err != nil { - return nil, nil, err - } - return conn, func() { - conn.Close() - sp.GracefulStop(ctx) - }, err - } - } -} diff --git a/test/integration/pool.yml b/test/integration/pool.yml deleted file mode 100644 index 30a7b8d0..00000000 --- a/test/integration/pool.yml +++ /dev/null @@ -1 +0,0 @@ -storagepool: viki_pool_HDD_20181031 diff --git a/test/integration/run.sh b/test/integration/run.sh deleted file mode 100644 index eeeb7123..00000000 --- a/test/integration/run.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Copyright © 2020-2025 Dell Inc. or its subsidiaries. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -#!/bin/bash -# This will run coverage analysis using the integration testing. -# The env.sh must point to a valid Unisphere deployment and the iscsi packages must be installed -# on this system. This will make real calls to Unisphere - -rm -f unix_sock -. ../../env.sh -rm -rf /dev/disk/csi-powermax/* -rm -rf datadir* -echo ENDPOINT $X_CSI_POWERMAX_ENDPOINT - -GOOS=linux CGO_ENABLED=0 GO111MODULE=on go test -v -coverprofile=c.linux.out -timeout 180m -coverpkg=../../service *test.go diff --git a/test/integration/step_defs_test.go b/test/integration/step_defs_test.go deleted file mode 100644 index 2458022f..00000000 --- a/test/integration/step_defs_test.go +++ /dev/null @@ -1,2296 +0,0 @@ -/* -Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package integration_test - -import ( - "context" - "errors" - "fmt" - "os" - "os/exec" - "path" - "strings" - "sync" - "time" - - "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/cucumber/godog" - "github.com/dell/csi-powermax/v2/pkg/symmetrix" - "github.com/dell/csi-powermax/v2/service" - csiext "github.com/dell/dell-csi-extensions/replication" - pmax "github.com/dell/gopowermax/v2" -) - -const ( - MaxRetries = 10 - RetrySleepTime = 10 * time.Second - ShortSleepTime = 3 * time.Second - SleepTime = 100 * time.Millisecond - ApplicationName = "CSI Driver Integration Tests" - HostLimitName = "HostLimitName" - HostIOLimitMBSec = "HostIOLimitMBSec" - HostIOLimitIOSec = "HostIOLimitIOSec" - DynamicDistribution = "DynamicDistribution" -) - -type feature struct { - errs []error - createVolumeRequest *csi.CreateVolumeRequest - createVolumeResponse *csi.CreateVolumeResponse - createRemoteVolumeResponse *csiext.CreateRemoteVolumeResponse - publishVolumeRequest *csi.ControllerPublishVolumeRequest - expandVolumeRequest *csi.ControllerExpandVolumeRequest - expandVolumeResponse *csi.ControllerExpandVolumeResponse - nodeExpandVolumeRequest *csi.NodeExpandVolumeRequest - nodeExpandVolumeResponse *csi.NodeExpandVolumeResponse - publishVolumeResponse *csi.ControllerPublishVolumeResponse - nodePublishVolumeRequest *csi.NodePublishVolumeRequest - listVolumesResponse *csi.ListVolumesResponse - listSnapshotsResponse *csi.ListSnapshotsResponse - capability *csi.VolumeCapability - capabilities []*csi.VolumeCapability - getCapacityRequest *csi.GetCapacityRequest - getCapacityResponse *csi.GetCapacityResponse - volID string - remotevolID string - snapshotID string - volIDList []string - maxRetryCount int - symID string - remotesymID string - srpID string - serviceLevel string - remoteServiceLevel string - localRdfGrpNo string - remoteRdfGrpNo string - localProtectedStorageGroup string - remoteProtectedStorageGroup string - pmaxClient pmax.Pmax - publishVolumeContextMap map[string]map[string]string - lockMutex sync.Mutex - idempotentTest bool - snapIDList []string - replicationPrefix string - replicationContextPrefix string - symmetrixIDParam string - remoteSRPID string - setIOLimits bool - autoSRDFEnabled bool -} - -func (f *feature) addError(err error) { - f.errs = append(f.errs, err) -} - -func (f *feature) aPowermaxService() error { - f.errs = make([]error, 0) - f.createVolumeRequest = nil - f.createVolumeResponse = nil - f.createRemoteVolumeResponse = nil - f.publishVolumeRequest = nil - f.publishVolumeResponse = nil - f.expandVolumeRequest = nil - f.expandVolumeResponse = nil - f.nodeExpandVolumeRequest = nil - f.nodeExpandVolumeResponse = nil - f.listVolumesResponse = nil - f.listSnapshotsResponse = nil - f.getCapacityRequest = nil - f.getCapacityResponse = nil - f.capability = nil - f.volID = "" - f.snapshotID = "" - f.localProtectedStorageGroup = "" - f.remoteProtectedStorageGroup = "" - f.symmetrixIDParam = "SYMID" - f.volIDList = f.volIDList[:0] - f.snapIDList = f.snapIDList[:0] - f.maxRetryCount = MaxRetries - f.publishVolumeContextMap = make(map[string]map[string]string) - f.idempotentTest = false - if f.pmaxClient == nil { - var err error - endpoint := os.Getenv("X_CSI_POWERMAX_ENDPOINT") - if endpoint == "" { - return fmt.Errorf("Cannot read X_CSI_POWERMAX_ENDPOINT") - } - f.pmaxClient, err = pmax.NewClientWithArgs(endpoint, ApplicationName, true, false, "") - if err != nil { - return fmt.Errorf("Cannot attach to pmax library: %s", err) - } - configConnect := &pmax.ConfigConnect{ - Endpoint: endpoint, - Username: os.Getenv("X_CSI_POWERMAX_USER"), - Password: os.Getenv("X_CSI_POWERMAX_PASSWORD"), - } - err = f.pmaxClient.Authenticate(context.Background(), configConnect) - if err != nil { - return err - } - } - f.symID = os.Getenv("SYMID") - f.srpID = os.Getenv("SRPID") - f.remoteSRPID = os.Getenv("REMOTESRPID") - f.remotesymID = os.Getenv("REMOTESYMID") - f.serviceLevel = os.Getenv("SERVICELEVEL") - f.remoteServiceLevel = os.Getenv("REMOTESERVICELEVEL") - f.localRdfGrpNo = os.Getenv("LOCALRDFGROUP") - f.remoteRdfGrpNo = os.Getenv("REMOTERDFGROUP") - f.replicationPrefix = os.Getenv("X_CSI_REPLICATION_PREFIX") - f.replicationContextPrefix = os.Getenv("X_CSI_REPLICATION_CONTEXT_PREFIX") - f.setIOLimits = false - f.autoSRDFEnabled = false - symIDList := []string{f.remotesymID} - // Add remote array to managed sym by csi driver - if _, err := symmetrix.GetPowerMax(f.remotesymID); err != nil { - err := symmetrix.Initialize(symIDList, f.pmaxClient) - if err != nil { - fmt.Printf("initialize remote array error: %s", err.Error()) - return err - } - } - return nil -} - -// A size of zero causes no capacity range to be specified. -func (f *feature) aBasicBlockVolumeRequest(_ string, size int64) error { - req := new(csi.CreateVolumeRequest) - params := make(map[string]string) - params[service.SymmetrixIDParam] = f.symID - params[service.ServiceLevelParam] = f.serviceLevel - params[service.StoragePoolParam] = f.srpID - params["thickprovisioning"] = "false" - params[service.ApplicationPrefixParam] = "INT" - params[service.CSIPVCNamespace] = "INT" - req.Parameters = params - now := time.Now() - req.Name = fmt.Sprintf("pmax-Int%d", now.Nanosecond()) - if size > 0 { - capacityRange := new(csi.CapacityRange) - capacityRange.RequiredBytes = size * 1024 * 1024 - req.CapacityRange = capacityRange - } - capability := new(csi.VolumeCapability) - block := new(csi.VolumeCapability_BlockVolume) - blockType := new(csi.VolumeCapability_Block) - blockType.Block = block - capability.AccessType = blockType - accessMode := new(csi.VolumeCapability_AccessMode) - accessMode.Mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER - capability.AccessMode = accessMode - f.capability = capability - capabilities := make([]*csi.VolumeCapability, 0) - capabilities = append(capabilities, capability) - req.VolumeCapabilities = capabilities - if f.setIOLimits { - req.Parameters[HostLimitName] = "HL1" - req.Parameters[HostIOLimitMBSec] = "1000" - req.Parameters[HostIOLimitIOSec] = "500" - req.Parameters[DynamicDistribution] = "Always" - } - f.createVolumeRequest = req - return nil -} - -func (f *feature) iUseThickProvisioning() error { - f.createVolumeRequest.Parameters[service.ThickVolumesParam] = "true" - f.createVolumeRequest.Parameters[service.StorageGroupParam] = "CSI-Integration-Thick" - return nil -} - -func (f *feature) anAlternateServiceLevel(serviceLevel string) error { - f.createVolumeRequest.Parameters[service.ServiceLevelParam] = serviceLevel - return nil -} - -func (f *feature) addsReplicationCapability(replicationMode string, namespace string) error { - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.RepEnabledParam)] = "true" - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.LocalRDFGroupParam)] = f.localRdfGrpNo - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.RemoteRDFGroupParam)] = f.remoteRdfGrpNo - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.RemoteSymIDParam)] = f.remotesymID - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.ReplicationModeParam)] = replicationMode - f.createVolumeRequest.Parameters[service.CSIPVCNamespace] = namespace - return nil -} - -func (f *feature) addsAutoSRDFReplicationCapability(replicationMode string, namespace string) error { - f.autoSRDFEnabled = true - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.RepEnabledParam)] = "true" - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.RemoteSymIDParam)] = f.remotesymID - f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.ReplicationModeParam)] = replicationMode - f.createVolumeRequest.Parameters[service.CSIPVCNamespace] = namespace - return nil -} - -func (f *feature) iCallCreateStorageProtectionGroup(replicationMode string) error { - req := new(csiext.CreateStorageProtectionGroupRequest) - params := make(map[string]string) - if !f.autoSRDFEnabled { - params[path.Join(f.replicationPrefix, service.LocalRDFGroupParam)] = f.localRdfGrpNo - params[path.Join(f.replicationPrefix, service.RemoteRDFGroupParam)] = f.remoteRdfGrpNo - } - params[path.Join(f.replicationPrefix, service.RemoteSymIDParam)] = f.remotesymID - params[path.Join(f.replicationPrefix, service.ReplicationModeParam)] = replicationMode - req.Parameters = params - req.VolumeHandle = f.volID - var err error - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - resp, err := client.CreateStorageProtectionGroup(ctx, req) - if err != nil { - fmt.Printf("CreateStorageProtectionGroup error %s:\n", err.Error()) - f.addError(err) - } - fmt.Printf("CreateStorageProtectionGroup succeeded \n") - if resp.LocalProtectionGroupAttributes[path.Join(f.replicationContextPrefix, f.symmetrixIDParam)] != f.symID { - fmt.Printf("CreateStorageProtectionGroup validation failed") - err := errors.New("validation failed for CreateStorageProtectionGroup, context prefix is missing") - f.addError(err) - } - fmt.Printf("CreateStorageProtectionGroup validation succeeded \n") - f.localProtectedStorageGroup = resp.LocalProtectionGroupId - f.remoteProtectedStorageGroup = resp.RemoteProtectionGroupId - return nil -} - -func (f *feature) iCallCreateRemoteVolume(replicationMode string) error { - req := new(csiext.CreateRemoteVolumeRequest) - params := make(map[string]string) - if !f.autoSRDFEnabled { - params[path.Join(f.replicationPrefix, service.LocalRDFGroupParam)] = f.localRdfGrpNo - params[path.Join(f.replicationPrefix, service.RemoteRDFGroupParam)] = f.remoteRdfGrpNo - } - params[path.Join(f.replicationPrefix, service.RemoteSymIDParam)] = f.remotesymID - params[path.Join(f.replicationPrefix, service.ReplicationModeParam)] = replicationMode - params[path.Join(f.replicationPrefix, service.RemoteServiceLevelParam)] = f.remoteServiceLevel - params[path.Join(f.replicationPrefix, service.RemoteSRPParam)] = f.remoteSRPID - req.Parameters = params - req.VolumeHandle = f.volID - var err error - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - f.createRemoteVolumeResponse, err = client.CreateRemoteVolume(ctx, req) - if err != nil { - fmt.Printf("CreateRemoteVolume error %s:\n", err.Error()) - f.addError(err) - } - remoteVolume := f.createRemoteVolumeResponse.RemoteVolume.GetVolumeId() - fmt.Printf("Remote Volume retrieved %s:\n", remoteVolume) - if f.createRemoteVolumeResponse.RemoteVolume.VolumeContext[path.Join(f.replicationContextPrefix, f.symmetrixIDParam)] != f.remotesymID { - fmt.Printf("CreateRemoteVolume validation failed \n") - err := errors.New("validation failed for CreateRemoteVolume, context prefix is missing") - f.addError(err) - } - fmt.Printf("CreateRemoteVolume validation succeeded\n") - f.volIDList = append(f.volIDList, remoteVolume) - return nil -} - -func (f *feature) iCallDeleteLocalStorageProtectionGroup() error { - req := new(csiext.DeleteStorageProtectionGroupRequest) - params := make(map[string]string) - params[path.Join(f.replicationContextPrefix, service.SymmetrixIDParam)] = f.symID - req.ProtectionGroupAttributes = params - req.ProtectionGroupId = f.localProtectedStorageGroup - var err error - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - _, err = client.DeleteStorageProtectionGroup(ctx, req) - if err != nil { - fmt.Printf("DeleteStorageProtectionGroup error %s:\n", err.Error()) - f.addError(err) - } - return nil -} - -func (f *feature) iCallDeleteRemoteStorageProtectionGroup() error { - req := new(csiext.DeleteStorageProtectionGroupRequest) - params := make(map[string]string) - params[path.Join(f.replicationContextPrefix, service.SymmetrixIDParam)] = f.remotesymID - req.ProtectionGroupAttributes = params - req.ProtectionGroupId = f.remoteProtectedStorageGroup - var err error - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - _, err = client.DeleteStorageProtectionGroup(ctx, req) - if err != nil { - fmt.Printf("DeleteStorageProtectionGroup error %s:\n", err.Error()) - f.addError(err) - } - return nil -} - -func (f *feature) iCallExecuteAction(action string) error { - req := new(csiext.ExecuteActionRequest) - params := make(map[string]string) - repMode := f.createVolumeRequest.Parameters[path.Join(f.replicationPrefix, service.ReplicationModeParam)] - params[path.Join(f.replicationContextPrefix, service.SymmetrixIDParam)] = f.symID - params[path.Join(f.replicationContextPrefix, service.LocalRDFGroupParam)] = f.localRdfGrpNo - params[path.Join(f.replicationContextPrefix, service.ReplicationModeParam)] = repMode - req.ProtectionGroupId = f.localProtectedStorageGroup - req.ProtectionGroupAttributes = params - actionType := &csiext.ExecuteActionRequest_Action{ - Action: &csiext.Action{}, - } - switch action { - case "Failover": - actionType.Action.ActionTypes = csiext.ActionTypes_FAILOVER_REMOTE - case "Failback": - actionType.Action.ActionTypes = csiext.ActionTypes_FAILBACK_LOCAL - case "Establish": - actionType.Action.ActionTypes = csiext.ActionTypes_ESTABLISH - case "Resume": - actionType.Action.ActionTypes = csiext.ActionTypes_RESUME - case "Suspend": - actionType.Action.ActionTypes = csiext.ActionTypes_SUSPEND - } - req.ActionTypes = actionType - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - _, err := client.ExecuteAction(ctx, req) - if err != nil { - fmt.Printf("ExecuteAction error %s:\n", err.Error()) - f.addError(err) - } - return nil -} - -func (f *feature) iCallGetStorageProtectionGroupStatus(expectedStatus string) error { - req := new(csiext.GetStorageProtectionGroupStatusRequest) - params := make(map[string]string) - params[path.Join(f.replicationContextPrefix, service.SymmetrixIDParam)] = f.symID - req.ProtectionGroupId = f.localProtectedStorageGroup - req.ProtectionGroupAttributes = params - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - resp, err := client.GetStorageProtectionGroupStatus(ctx, req) - if err != nil { - fmt.Printf("GetStorageProtectionGroupStatus error %s:\n", err.Error()) - f.addError(err) - } - currentStatus := resp.GetStatus().State.String() - if currentStatus != expectedStatus { - errmsg := fmt.Sprintf("GetStorageProtectionGroupStatus error, Expected %s != %s Current", expectedStatus, currentStatus) - fmt.Printf("%s", errmsg) - f.addError(errors.New(errmsg)) - } - return nil -} - -func (f *feature) accessTypeIs(arg1 string) error { - switch arg1 { - case "multi-writer": - f.createVolumeRequest.VolumeCapabilities[0].AccessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER - } - return nil -} - -func (f *feature) iCallCreateVolume() error { - var err error - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - f.createVolumeResponse, err = client.CreateVolume(ctx, f.createVolumeRequest) - if err != nil { - fmt.Printf("CreateVolume error %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("CreateVolume %s (%s) %s %s\n", f.createVolumeResponse.GetVolume().VolumeContext["Name"], - f.createVolumeResponse.GetVolume().VolumeId, f.createVolumeResponse.GetVolume().VolumeContext["CreationTime"], f.createVolumeResponse.GetVolume().VolumeContext["CapacityBytes"]) - f.volID = f.createVolumeResponse.GetVolume().VolumeId - f.volIDList = append(f.volIDList, f.createVolumeResponse.GetVolume().VolumeId) - } - return nil -} - -func (f *feature) createVolume(req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - var volResp *csi.CreateVolumeResponse - var err error - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - volResp, err = client.CreateVolume(ctx, req) - if err == nil || !strings.Contains(err.Error(), "Insufficient resources") { - // no need for retry - break - } - fmt.Printf("CreateVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return volResp, err -} - -func (f *feature) whenICallDeleteVolume() error { - err := f.deleteVolume(f.volID) - if err != nil { - fmt.Printf("DeleteVolume %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("DeleteVolume %s completed successfully\n", f.volID) - } - return nil -} - -func (f *feature) whenICallDeleteLocalVolume() error { - if f.createRemoteVolumeResponse == nil { - return nil - } - err := f.deleteLocalVolume(f.createRemoteVolumeResponse.RemoteVolume.VolumeId) - if err != nil { - fmt.Printf("DeleteLocalVolume %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("DeleteLocalVolume %s completed successfully\n", f.volID) - } - return nil -} - -func (f *feature) iReceiveAValidVolume() error { - if f.createVolumeResponse == nil { - return fmt.Errorf("Expected a Volume Resonse but there was none") - } - attributes := f.createVolumeResponse.Volume.VolumeContext - if attributes == nil { - return fmt.Errorf("Expected volume attributes but there were none") - } - serviceLevel := attributes["ServiceLevel"] - expectedServiceLevel := f.createVolumeRequest.Parameters[service.ServiceLevelParam] - if expectedServiceLevel != serviceLevel { - return fmt.Errorf("Expected ServiceLevel %s but ServiceLevel %s was returned", expectedServiceLevel, serviceLevel) - } - return nil -} - -func (f *feature) deleteVolume(id string) error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - delVolReq := new(csi.DeleteVolumeRequest) - delVolReq.VolumeId = id - var err error - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.DeleteVolume(ctx, delVolReq) - if err == nil { - // no need for retry - break - } - fmt.Printf("DeleteVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) deleteLocalVolume(id string) error { - ctx := context.Background() - client := csiext.NewReplicationClient(grpcClient) - delVolReq := new(csiext.DeleteLocalVolumeRequest) - delVolReq.VolumeHandle = id - var err error - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.DeleteLocalVolume(ctx, delVolReq) - if err == nil { - // no need for retry - break - } - fmt.Printf("DeleteLocalVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) thereAreNoErrors() error { - if len(f.errs) == 0 { - return nil - } - return f.errs[0] -} - -func (f *feature) theErrorMessageShouldContain(expected string) error { - // If arg1 is none, we expect no error, any error received is unexpected - if expected == "none" { - if len(f.errs) == 0 { - return nil - } - return fmt.Errorf("Unexpected error(s): %s", f.errs[0]) - } - // We expect an error... - if len(f.errs) == 0 { - return errors.New("there were no errors but we expected: " + expected) - } - err0 := f.errs[0] - if !strings.Contains(err0.Error(), expected) { - return fmt.Errorf("Error %s does not contain the expected message: %s", err0.Error(), expected) - } - return nil -} - -func (f *feature) aMountVolumeRequest(name string) error { - req := f.getMountVolumeRequest(name) - f.createVolumeRequest = req - return nil -} - -func (f *feature) getMountVolumeRequest(_ string) *csi.CreateVolumeRequest { - req := new(csi.CreateVolumeRequest) - params := make(map[string]string) - params[service.SymmetrixIDParam] = f.symID - params[service.ServiceLevelParam] = f.serviceLevel - params[service.CSIPVCNamespace] = "INT" - params[service.StoragePoolParam] = f.srpID - req.Parameters = params - now := time.Now() - req.Name = fmt.Sprintf("pmax-Int%d", now.Nanosecond()) - capacityRange := new(csi.CapacityRange) - capacityRange.RequiredBytes = 100 * 1024 * 1024 - req.CapacityRange = capacityRange - capability := new(csi.VolumeCapability) - mountVolume := new(csi.VolumeCapability_MountVolume) - mountVolume.FsType = "xfs" - mountVolume.MountFlags = make([]string, 0) - mount := new(csi.VolumeCapability_Mount) - mount.Mount = mountVolume - capability.AccessType = mount - accessMode := new(csi.VolumeCapability_AccessMode) - accessMode.Mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER - capability.AccessMode = accessMode - f.capability = capability - capabilities := make([]*csi.VolumeCapability, 0) - capabilities = append(capabilities, capability) - req.VolumeCapabilities = capabilities - f.createVolumeRequest = req - return req -} - -func (f *feature) aVolumeRequestFileSystem(name, fstype, access, voltype string) error { - req := f.getVolumeRequestFileSystem(name, fstype, access, voltype) - f.createVolumeRequest = req - return nil -} - -func (f *feature) getVolumeRequestFileSystem(_, fstype, access, voltype string) *csi.CreateVolumeRequest { - req := new(csi.CreateVolumeRequest) - params := make(map[string]string) - params[service.SymmetrixIDParam] = f.symID - params[service.ServiceLevelParam] = f.serviceLevel - params[service.StoragePoolParam] = f.srpID - switch voltype { - case "block": - params["thickprovisioning"] = "false" - params[service.ApplicationPrefixParam] = "INT" - } - req.Parameters = params - now := time.Now() - req.Name = fmt.Sprintf("pmax-Int%d", now.Nanosecond()) - capacityRange := new(csi.CapacityRange) - capacityRange.RequiredBytes = 100 * 1024 * 1024 - req.CapacityRange = capacityRange - capability := new(csi.VolumeCapability) - switch voltype { - case "mount": - mountVolume := new(csi.VolumeCapability_MountVolume) - switch fstype { - case "xfs": - mountVolume.FsType = "xfs" - case "ext4": - mountVolume.FsType = "ext4" - } - mountVolume.MountFlags = make([]string, 0) - mount := new(csi.VolumeCapability_Mount) - mount.Mount = mountVolume - capability.AccessType = mount - case "block": - block := new(csi.VolumeCapability_BlockVolume) - blockType := new(csi.VolumeCapability_Block) - blockType.Block = block - capability.AccessType = blockType - } - accessMode := new(csi.VolumeCapability_AccessMode) - switch access { - case "single-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER - case "multi-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER - case "multi-reader": - accessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY - case "multi-node-single-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER - } - capability.AccessMode = accessMode - f.capability = capability - capabilities := make([]*csi.VolumeCapability, 0) - capabilities = append(capabilities, capability) - req.VolumeCapabilities = capabilities - f.createVolumeRequest = req - return req -} - -func (f *feature) getControllerPublishVolumeRequest() *csi.ControllerPublishVolumeRequest { - req := new(csi.ControllerPublishVolumeRequest) - req.VolumeId = f.volID - req.NodeId = os.Getenv("X_CSI_POWERMAX_NODENAME") - req.Readonly = false - req.VolumeCapability = f.capability - f.publishVolumeRequest = req - return req -} - -func (f *feature) whenICallPublishVolume(nodeIDEnvVar string) error { - err := f.controllerPublishVolume(f.volID, nodeIDEnvVar) - if err != nil { - fmt.Printf("ControllerPublishVolume %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("ControllerPublishVolume completed successfully\n") - } - time.Sleep(SleepTime) - return nil -} - -func (f *feature) controllerPublishVolume(id string, _ string) error { - var resp *csi.ControllerPublishVolumeResponse - var err error - req := f.getControllerPublishVolumeRequest() - req.VolumeId = id - req.NodeId = os.Getenv("X_CSI_POWERMAX_NODENAME") - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - resp, err = client.ControllerPublishVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("Controller PublishVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - f.lockMutex.Lock() - defer f.lockMutex.Unlock() - f.publishVolumeContextMap[id] = resp.GetPublishContext() - f.publishVolumeResponse = resp - return err -} - -func (f *feature) whenICallUnpublishVolume(nodeIDEnvVar string) error { - err := f.controllerUnpublishVolume(f.publishVolumeRequest.VolumeId, nodeIDEnvVar) - if err != nil { - fmt.Printf("ControllerUnpublishVolume failed: %s\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("ControllerUnpublishVolume completed successfully\n") - } - time.Sleep(SleepTime) - return nil -} - -func (f *feature) controllerUnpublishVolume(id string, _ string) error { - var err error - req := new(csi.ControllerUnpublishVolumeRequest) - req.VolumeId = id - req.NodeId = os.Getenv("X_CSI_POWERMAX_NODENAME") - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.ControllerUnpublishVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("ControllerUnpublishVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) maxRetries(arg1 int) error { - f.maxRetryCount = arg1 - return nil -} - -func (f *feature) aCapabilityWithVoltypeAccessFstype(voltype, access, fstype string) error { - // Construct the volume capabilities - capability := new(csi.VolumeCapability) - switch voltype { - case "block": - blockVolume := new(csi.VolumeCapability_BlockVolume) - block := new(csi.VolumeCapability_Block) - block.Block = blockVolume - capability.AccessType = block - case "mount": - mountVolume := new(csi.VolumeCapability_MountVolume) - mountVolume.FsType = fstype - mountVolume.MountFlags = make([]string, 0) - mount := new(csi.VolumeCapability_Mount) - mount.Mount = mountVolume - capability.AccessType = mount - } - accessMode := new(csi.VolumeCapability_AccessMode) - switch access { - case "single-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER - case "multi-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER - case "multi-reader": - accessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY - case "multi-node-single-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER - case "single-node-single-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER - case "single-node-multi-writer": - accessMode.Mode = csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER - } - capability.AccessMode = accessMode - f.capabilities = make([]*csi.VolumeCapability, 0) - f.capabilities = append(f.capabilities, capability) - f.capability = capability - return nil -} - -func (f *feature) aVolumeRequest(name string, size int64) error { - req := new(csi.CreateVolumeRequest) - params := make(map[string]string) - params["storagepool"] = os.Getenv("STORAGE_POOL") - params["thickprovisioning"] = "true" - req.Parameters = params - req.Name = name - capacityRange := new(csi.CapacityRange) - capacityRange.RequiredBytes = size * 1024 - req.CapacityRange = capacityRange - req.VolumeCapabilities = f.capabilities - f.createVolumeRequest = req - return nil -} - -func (f *feature) getNodePublishVolumeRequest() *csi.NodePublishVolumeRequest { - req := new(csi.NodePublishVolumeRequest) - req.VolumeId = f.volID - req.Readonly = false - req.VolumeCapability = f.capability - req.PublishContext = f.publishVolumeResponse.PublishContext - block := f.capability.GetBlock() - if block != nil { - req.TargetPath = datafile - } - mount := f.capability.GetMount() - if mount != nil { - now := time.Now() - targetPath := fmt.Sprintf("%s-datadir-%d", f.volID, now.Nanosecond()) - var fileMode os.FileMode - fileMode = 0o777 - err := os.Mkdir(targetPath, fileMode) - if err != nil && !os.IsExist(err) { - fmt.Printf("%s: %s\n", req.TargetPath, err) - } - var cwd string - if cwd, err = os.Getwd(); err != nil { - panic("cannot get working directory") - } - req.TargetPath = cwd + "/" + targetPath - } - f.nodePublishVolumeRequest = req - return req -} - -func (f *feature) whenICallNodeStageVolume(_ string) error { - pub := f.nodePublishVolumeRequest - if pub == nil { - pub = f.getNodePublishVolumeRequest() - } - req := &csi.NodeStageVolumeRequest{} - req.VolumeId = f.volID - req.PublishContext = pub.PublishContext - req.StagingTargetPath = pub.TargetPath - req.VolumeCapability = f.capability - fmt.Printf("calling NodeStageVolume vol %s staging path %s\n", f.volID, f.nodePublishVolumeRequest.TargetPath) - client := csi.NewNodeClient(grpcClient) - ctx := context.Background() - _, err := client.NodeStageVolume(ctx, req) - return err -} - -func (f *feature) nodeStageVolume(id string, path string) error { - var err error - pub := f.nodePublishVolumeRequest - if pub == nil { - pub = f.getNodePublishVolumeRequest() - } - req := &csi.NodeStageVolumeRequest{} - req.VolumeId = id - f.lockMutex.Lock() - defer f.lockMutex.Unlock() - req.PublishContext = f.publishVolumeContextMap[id] - req.StagingTargetPath = path - req.VolumeCapability = f.capability - fmt.Printf("calling NodeStageVolume vol %s staging path %s\n", req.VolumeId, req.StagingTargetPath) - client := csi.NewNodeClient(grpcClient) - ctx := context.Background() - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.NodeStageVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("NodeStageVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) whenICallNodePublishVolume(_ string) error { - err := f.nodePublishVolume(f.volID, "") - if err != nil { - fmt.Printf("NodePublishVolume failed: %s\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("NodePublishVolume completed successfully\n") - } - time.Sleep(ShortSleepTime) - time.Sleep(30 * time.Second) - return nil -} - -func (f *feature) nodePublishVolume(id string, path string) error { - var err error - req := f.nodePublishVolumeRequest - if req == nil || !f.idempotentTest { - req = f.getNodePublishVolumeRequest() - } - if path != "" { - block := f.capability.GetBlock() - if block != nil { - req.TargetPath = path - } - mount := f.capability.GetMount() - if mount != nil { - req.TargetPath = path - } - } - req.VolumeId = id - f.lockMutex.Lock() - defer f.lockMutex.Unlock() - req.PublishContext = f.publishVolumeContextMap[id] - ctx := context.Background() - client := csi.NewNodeClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.NodePublishVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("NodePublishVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) whenICallNodeUnstageVolume(_ string) error { - req := &csi.NodeUnstageVolumeRequest{} - req.VolumeId = f.volID - req.StagingTargetPath = f.nodePublishVolumeRequest.TargetPath - fmt.Printf("calling NodeUnstageVolume vol %s targetpath %s\n", f.volID, f.nodePublishVolumeRequest.TargetPath) - client := csi.NewNodeClient(grpcClient) - ctx := context.Background() - _, err := client.NodeUnstageVolume(ctx, req) - return err -} - -func (f *feature) nodeUnstageVolume(id string, path string) error { - var err error - req := &csi.NodeUnstageVolumeRequest{} - req.VolumeId = id - req.StagingTargetPath = path - fmt.Printf("calling NodeUnstageVolume vol %s targetpath %s\n", id, path) - client := csi.NewNodeClient(grpcClient) - ctx := context.Background() - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.NodeUnstageVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("NodeUnstageVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) whenICallNodeUnpublishVolume(_ string) error { - fmt.Printf("calling nodeUnpublish vol %s targetPath %s\n", f.volID, f.nodePublishVolumeRequest.TargetPath) - err := f.nodeUnpublishVolume(f.volID, f.nodePublishVolumeRequest.TargetPath) - if err != nil { - fmt.Printf("NodeUnpublishVolume failed: %s\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("NodeUnpublishVolume completed successfully\n") - } - time.Sleep(ShortSleepTime) - return nil -} - -func (f *feature) nodeUnpublishVolume(id string, path string) error { - var err error - req := &csi.NodeUnpublishVolumeRequest{VolumeId: id, TargetPath: path} - ctx := context.Background() - client := csi.NewNodeClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.NodeUnpublishVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("NodeUnpublishVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) verifyPublishedVolumeWithVoltypeAccessFstype(voltype, _, fstype string) error { - if len(f.errs) > 0 { - fmt.Printf("Not verifying published volume because of previous error") - return nil - } - var cmd *exec.Cmd - if voltype == "mount" { - cmd = exec.Command("/bin/bash", "-c", "mount | grep /tmp/datadir") - } else if voltype == "block" { - cmd = exec.Command("/bin/bash", "-c", "mount | grep /tmp/datafile") - } else { - return fmt.Errorf("unexpected volume type") - } - stdout, err := cmd.CombinedOutput() - if err != nil { - return err - } - fmt.Printf("%s\n", stdout) - if voltype == "mount" { - // output: /dev/scinia on /tmp/datadir type xfs (rw,relatime,seclabel,attr2,inode64,noquota) - if !strings.Contains(string(stdout), "/dev/scini") { - return fmt.Errorf("Mount did not contain /dev/scini for scale-io") - } - if !strings.Contains(string(stdout), "/tmp/datadir") { - return fmt.Errorf("Mount did not contain /tmp/datadir for type mount") - } - if !strings.Contains(string(stdout), fmt.Sprintf("type %s", fstype)) { - return fmt.Errorf("Did not find expected fstype %s", fstype) - } - - } else if voltype == "block" { - // devtmpfs on /tmp/datafile type devtmpfs (rw,relatime,seclabel,size=8118448k,nr_inodes=2029612,mode=755) - if !strings.Contains(string(stdout), "devtmpfs on /tmp/datafile") { - return errors.New("Expected devtmpfs on /tmp/datafile for mounted block device") - } - } - return nil -} - -func (f *feature) iCallCreateSnapshot() error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - req := &csi.CreateSnapshotRequest{ - SourceVolumeId: f.volID, - Name: "snapshot-0eb5347a-0000-11e9-ab1c-005056a64ad3", - } - resp, err := client.CreateSnapshot(ctx, req) - if err != nil { - fmt.Printf("CreateSnapshot returned error: %s\n", err.Error()) - f.addError(err) - } else { - f.snapshotID = resp.Snapshot.SnapshotId - f.snapIDList = append(f.snapIDList, f.snapshotID) - fmt.Printf("createSnapshot: SnapshotId %s SourceVolumeId %s CreationTime %s\n", - resp.Snapshot.SnapshotId, resp.Snapshot.SourceVolumeId, resp.Snapshot.CreationTime.AsTime().Format(time.RFC3339Nano)) - } - time.Sleep(RetrySleepTime) - return nil -} - -func (f *feature) iCallDeleteSnapshot() error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - req := &csi.DeleteSnapshotRequest{ - SnapshotId: f.snapshotID, - } - _, err := client.DeleteSnapshot(ctx, req) - if err != nil { - fmt.Printf("DeleteSnapshot returned error: %s\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("DeleteSnapshot: SnapshotId %s\n", req.SnapshotId) - } - time.Sleep(RetrySleepTime) - return nil -} - -func (f *feature) ICallDeleteAllSnapshots() error { - for _, s := range f.snapIDList { - f.snapshotID = s - f.iCallDeleteSnapshot() - } - return nil -} - -func (f *feature) iCallCreateSnapshotConsistencyGroup() error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - var volumeIDList string - for i, v := range f.volIDList { - switch i { - case 0: - continue - case 1: - volumeIDList = v - default: - volumeIDList = volumeIDList + "," + v - } - } - req := &csi.CreateSnapshotRequest{ - SourceVolumeId: f.volIDList[0], - Name: "", - } - req.Parameters = make(map[string]string) - req.Parameters["VolumeIDList"] = volumeIDList - resp, err := client.CreateSnapshot(ctx, req) - if err != nil { - fmt.Printf("CreateSnapshot returned error: %s\n", err.Error()) - f.addError(err) - } else { - f.snapshotID = resp.Snapshot.SnapshotId - fmt.Printf("createSnapshot: SnapshotId %s SourceVolumeId %s CreationTime %s\n", - resp.Snapshot.SnapshotId, resp.Snapshot.SourceVolumeId, resp.Snapshot.CreationTime.AsTime().Format(time.RFC3339Nano)) - } - time.Sleep(SleepTime) - return nil -} - -func (f *feature) whenICallDeleteAllVolumes() error { - for _, v := range f.volIDList { - f.volID = v - f.whenICallDeleteVolume() - } - return nil -} - -func (f *feature) iCallLinkVolumeToSnapshot() error { - req := f.createVolumeRequest - req.Name = "volFromSnap-" + req.Name - source := &csi.VolumeContentSource_SnapshotSource{SnapshotId: f.snapshotID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Snapshot{Snapshot: source} - fmt.Printf("Calling CreateVolume with snapshot source") - - _ = f.createAVolume(req, "single CreateVolume from Snap") - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCallLinkVolumeToSnapshotAgain() error { - req := f.createVolumeRequest - source := &csi.VolumeContentSource_SnapshotSource{SnapshotId: f.snapshotID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Snapshot{Snapshot: source} - fmt.Printf("Calling CreateVolume with snapshot source") - - _ = f.createAVolume(req, "single CreateVolume from Snap") - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCallLinkVolumeToVolume() error { - req := f.createVolumeRequest - req.Name = "volFromVol-" + req.Name - source := &csi.VolumeContentSource_VolumeSource{VolumeId: f.volID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Volume{Volume: source} - fmt.Printf("Calling CreateVolume with volume source") - - _ = f.createAVolume(req, "single CreateVolume from Volume") - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCallLinkVolumeToVolumeAgain() error { - req := f.createVolumeRequest - source := &csi.VolumeContentSource_VolumeSource{VolumeId: f.volID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Volume{Volume: source} - fmt.Printf("Calling CreateVolume with volume source") - - _ = f.createAVolume(req, "single CreateVolume from Volume") - time.Sleep(SleepTime) - return nil -} - -func (f *feature) createAVolume(req *csi.CreateVolumeRequest, voltype string) error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - volResp, err := client.CreateVolume(ctx, req) - if err != nil { - fmt.Printf("CreateVolume %s request returned error: %s\n", voltype, err.Error()) - f.addError(err) - } else { - fmt.Printf("CreateVolume from snap %s (%s) %s\n", volResp.GetVolume().VolumeContext["Name"], - volResp.GetVolume().VolumeId, volResp.GetVolume().VolumeContext["CreationTime"]) - f.volIDList = append(f.volIDList, volResp.GetVolume().VolumeId) - } - return err -} - -func (f *feature) iCallCreateManyVolumesFromSnapshot() error { - for i := 1; i <= 130; i++ { - req := f.createVolumeRequest - req.Name = fmt.Sprintf("volFromSnap%d", i) - source := &csi.VolumeContentSource_SnapshotSource{SnapshotId: f.snapshotID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Snapshot{Snapshot: source} - fmt.Printf("Calling CreateVolume with snapshot source") - err := f.createAVolume(req, "single CreateVolume from Snap") - if err != nil { - fmt.Printf("Error on the %d th volume: %s\n", i, err.Error()) - break - } - } - return nil -} - -func (f *feature) iCallListVolume() error { - var err error - ctx := context.Background() - req := &csi.ListVolumesRequest{} - client := csi.NewControllerClient(grpcClient) - f.listVolumesResponse, err = client.ListVolumes(ctx, req) - if err != nil { - return err - } - return nil -} - -func (f *feature) aValidListVolumeResponseIsReturned() error { - resp := f.listVolumesResponse - entries := resp.GetEntries() - if entries == nil { - return errors.New("expected ListVolumeResponse.Entries but none") - } - for _, entry := range entries { - vol := entry.GetVolume() - if vol != nil { - id := vol.VolumeId - capacity := vol.CapacityBytes - name := vol.VolumeContext["Name"] - creation := vol.VolumeContext["CreationTime"] - fmt.Printf("Volume ID: %s Name: %s Capacity: %d CreationTime: %s\n", id, name, capacity, creation) - } - } - return nil -} - -func (f *feature) iCallListSnapshot() error { - var err error - ctx := context.Background() - req := &csi.ListSnapshotsRequest{} - client := csi.NewControllerClient(grpcClient) - f.listSnapshotsResponse, err = client.ListSnapshots(ctx, req) - if err != nil { - return err - } - return nil -} - -func (f *feature) aValidListSnapshotResponseIsReturned() error { - nextToken := f.listSnapshotsResponse.GetNextToken() - if nextToken != "" { - return errors.New("received NextToken on ListSnapshots but didn't expect one") - } - entries := f.listSnapshotsResponse.GetEntries() - for j := 0; j < len(entries); j++ { - entry := entries[j] - id := entry.GetSnapshot().SnapshotId - ts := entry.GetSnapshot().CreationTime.AsTime().Format(time.RFC3339Nano) - fmt.Printf("snapshot ID %s source ID %s timestamp %s\n", id, entry.GetSnapshot().SourceVolumeId, ts) - } - return nil -} - -func (f *feature) iCreateVolumesInParallel(nVols int) error { - idchan := make(chan string, nVols) - errchan := make(chan error, nVols) - t0 := time.Now() - // Send requests - for i := 0; i < nVols; i++ { - name := fmt.Sprintf("scale%d", i) - go func(name string, idchan chan string, errchan chan error) { - var resp *csi.CreateVolumeResponse - var err error - req := f.getMountVolumeRequest(name) - if req != nil { - resp, err = f.createVolume(req) - if resp != nil { - idchan <- resp.GetVolume().VolumeId - } else { - idchan <- "" - } - } - errchan <- err - }(name, idchan, errchan) - } - // Wait on complete, collecting ids and errors - nerrors := 0 - for i := 0; i < nVols; i++ { - var id string - var err error - id = <-idchan - if id != "" { - f.volIDList = append(f.volIDList, id) - } - err = <-errchan - if err != nil { - fmt.Printf("create volume received error: %s\n", err.Error()) - f.addError(err) - nerrors++ - } - } - t1 := time.Now() - if len(f.volIDList) > nVols { - f.volIDList = f.volIDList[0:nVols] - } - fmt.Printf("Create volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iPublishVolumesInParallel(nVols int) error { - nvols := len(f.volIDList) - done := make(chan bool, nvols) - errchan := make(chan error, nvols) - - // Send requests - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - go func(id string, done chan bool, errchan chan error) { - err := f.controllerPublishVolume(id, "nodeID") - done <- true - errchan <- err - }(id, done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Controller publish", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - - t1 := time.Now() - fmt.Printf("Controller publish volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(4 * SleepTime) - return nil -} - -// waitOnParallelResponses waits on the responses from threads and returns the number of errors and/or and error -func (f *feature) waitOnParallelResponses(method string, nVols int, _ []string, done chan bool, errchan chan error) (int, error) { - nerrors := 0 - for i := 0; i < nVols; i++ { - if f.volIDList[i] == "" { - continue - } - finished := <-done - if !finished { - return nerrors, errors.New("premature completion") - } - err := <-errchan - if err != nil { - fmt.Printf("%s received error: %s\n", method, err.Error()) - f.addError(err) - nerrors++ - } - } - return nerrors, nil -} - -func (f *feature) iNodeStageVolumesInParallel(nVols int) error { - nvols := len(f.volIDList) - // make a staging directory for each - for i := 0; i < nVols; i++ { - dataDirName := fmt.Sprintf("/tmp/stagedir%d", i) - fmt.Printf("Creating %s\n", dataDirName) - var fileMode os.FileMode - fileMode = 0o777 - err := os.Mkdir(dataDirName, fileMode) - if err != nil && !os.IsExist(err) { - fmt.Printf("%s: %s\n", dataDirName, err) - } - } - done := make(chan bool, nvols) - errchan := make(chan error, nvols) - - // Send requests - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - dataDirName := fmt.Sprintf("/tmp/stagedir%d", i) - go func(id string, dataDirName string, done chan bool, errchan chan error) { - err := f.nodeStageVolume(id, dataDirName) - done <- true - errchan <- err - }(id, dataDirName, done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Node stage", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - - t1 := time.Now() - fmt.Printf("Node stage volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(2 * SleepTime) - return nil -} - -func (f *feature) iNodePublishVolumesInParallel(nVols int) error { - nvols := len(f.volIDList) - // make a data directory for each - for i := 0; i < nVols; i++ { - dataDirName := fmt.Sprintf("/tmp/datadir%d", i) - fmt.Printf("Checking %s\n", dataDirName) - var fileMode os.FileMode - fileMode = 0o777 - err := os.Mkdir(dataDirName, fileMode) - if err != nil && !os.IsExist(err) { - fmt.Printf("%s: %s\n", dataDirName, err) - } - } - done := make(chan bool, nvols) - errchan := make(chan error, nvols) - - // Send requests - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - dataDirName := fmt.Sprintf("/tmp/datadir%d", i) - go func(id string, dataDirName string, done chan bool, errchan chan error) { - err := f.nodePublishVolume(id, dataDirName) - done <- true - errchan <- err - }(id, dataDirName, done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Node publish", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - - t1 := time.Now() - fmt.Printf("Node publish volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(2 * SleepTime) - return nil -} - -func (f *feature) iNodeUnpublishVolumesInParallel(nVols int) error { - nvols := len(f.volIDList) - done := make(chan bool, nvols) - errchan := make(chan error, nvols) - - // Send requests - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - dataDirName := fmt.Sprintf("/tmp/datadir%d", i) - go func(id string, dataDirName string, done chan bool, errchan chan error) { - err := f.nodeUnpublishVolume(id, dataDirName) - done <- true - errchan <- err - }(id, dataDirName, done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Node unpublish", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - - t1 := time.Now() - fmt.Printf("Node unpublish volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iNodeUnstageVolumesInParallel(nVols int) error { - nvols := len(f.volIDList) - done := make(chan bool, nvols) - errchan := make(chan error, nvols) - - // Send requests - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - dataDirName := fmt.Sprintf("/tmp/stagedir%d", i) - go func(id string, dataDirName string, done chan bool, errchan chan error) { - err := f.nodeUnstageVolume(id, dataDirName) - done <- true - errchan <- err - }(id, dataDirName, done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Node unstage", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - - t1 := time.Now() - fmt.Printf("Node unstage volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iUnpublishVolumesInParallel(nVols int) error { - nvols := len(f.volIDList) - done := make(chan bool, nvols) - errchan := make(chan error, nvols) - - // Send request - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - go func(id string, done chan bool, errchan chan error) { - err := f.controllerUnpublishVolume(id, "nodeID") - done <- true - errchan <- err - }(id, done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Controller unpublish", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - - t1 := time.Now() - fmt.Printf("Controller unpublish volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) whenIDeleteVolumesInParallel(nVols int) error { - nVols = len(f.volIDList) - done := make(chan bool, nVols) - errchan := make(chan error, nVols) - - // Send requests - t0 := time.Now() - for i := 0; i < nVols; i++ { - id := f.volIDList[i] - if id == "" { - continue - } - go func(id string, done chan bool, errchan chan error) { - err := f.deleteVolume(id) - done <- true - errchan <- err - }(f.volIDList[i], done, errchan) - } - - // Wait for responses - nerrors, err := f.waitOnParallelResponses("Delete volume", nVols, f.volIDList, done, errchan) - if err != nil { - return err - } - t1 := time.Now() - fmt.Printf("Delete volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(RetrySleepTime) - return nil -} - -func (f *feature) allVolumesAreDeletedSuccessfully() error { - t0 := time.Now() - nVols := len(f.volIDList) - for _, id := range f.volIDList { - deleted := false - idComponents := strings.Split(id, "-") - symVolumeID := idComponents[len(idComponents)-1] - symID := idComponents[len(idComponents)-2] - fmt.Printf("Waiting for volume %s %s to be deleted\n", id, symVolumeID) - maxVols := 40 * nVols - if 300 > maxVols { - maxVols = 300 - } - for i := 0; i < maxVols; i++ { - vol, err := f.pmaxClient.GetVolumeByID(context.Background(), symID, symVolumeID) - if vol == nil { - deleted = strings.Contains(err.Error(), "Could not find") - fmt.Printf("volume deleted?: %s\n", err) - break - } - fmt.Printf("Waiting for volume %s %s to be deleted\n", id, symVolumeID) - time.Sleep(5 * time.Second) - } - if !deleted { - return fmt.Errorf("Volume was never deleted: %s", id) - } - } - t1 := time.Now() - fmt.Printf("Background deletion time for %d volumes approximately: %v %v\n", nVols, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(5 * time.Second) - return nil -} - -func (f *feature) iMungeTheCSIVolumeID() error { - str := []byte(f.volID) - strlen := len(str) - str[strlen-20] = 'z' - f.volID = string(str) - return nil -} - -func (f *feature) iUnmungeTheCSIVolumeID() error { - f.volID = f.volIDList[0] - f.errs = make([]error, 0) - return nil -} - -func (f *feature) theVolumeIsNotDeleted() error { - id := f.volIDList[0] - splitid := strings.Split(id, "-") - deviceID := splitid[len(splitid)-1] - vol, err := f.pmaxClient.GetVolumeByID(context.Background(), f.symID, deviceID) - if err != nil { - return err - } - volumePrefix := os.Getenv(service.EnvClusterPrefix) - expectedName := service.CsiVolumePrefix + volumePrefix + "-" + f.createVolumeRequest.Name + "-" + f.createVolumeRequest.Parameters[service.ApplicationPrefixParam] - if vol.VolumeIdentifier != expectedName { - return fmt.Errorf("Expected VolumeIdentifier %s to match original requested name %s indicating volume was not deleted, but they differ", - vol.VolumeIdentifier, expectedName) - } - return nil -} - -func (f *feature) aGetCapacityRequest(srpID string) error { - req := new(csi.GetCapacityRequest) - params := make(map[string]string) - params[service.StoragePoolParam] = srpID - params[service.SymmetrixIDParam] = f.symID - params[service.ServiceLevelParam] = f.serviceLevel - req.Parameters = params - req.VolumeCapabilities = f.capabilities - f.getCapacityRequest = req - return nil -} - -func (f *feature) iCallGetCapacity() error { - var err error - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - f.getCapacityResponse, err = client.GetCapacity(ctx, f.getCapacityRequest) - if err != nil { - fmt.Printf("GetCapacity %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("GetCapacity %d and MaxVolumeSize %v\n", f.getCapacityResponse.AvailableCapacity, f.getCapacityResponse.MaximumVolumeSize) - } - return nil -} - -func (f *feature) aValidGetCapacityResponseIsReturned() error { - resp := f.getCapacityResponse - capacity := resp.AvailableCapacity - if capacity <= 0 { - return errors.New("Storage capacity is zero or less") - } - return nil -} - -func (f *feature) anIdempotentTest() error { - f.idempotentTest = true - return nil -} - -func (f *feature) theVolumeSizeIs(expectedVolSize string) error { - if len(f.errs) > 0 { - return nil - } - if f.createVolumeResponse == nil { - return fmt.Errorf("expected CreateVolume response but there was none") - } - capGB := f.createVolumeResponse.GetVolume().VolumeContext["CapacityGB"] - if capGB != expectedVolSize { - return fmt.Errorf("Expected %s GB but got %s", expectedVolSize, capGB) - } - return nil -} - -func (f *feature) iCreateSnapshotsInParallel(nSnaps int) error { - idchan := make(chan string, nSnaps) - errchan := make(chan error, nSnaps) - t0 := time.Now() - // Send requests - for i := 0; i < nSnaps; i++ { - name := fmt.Sprintf("Scale_Test_Snap%d", i) - go func(name string, idchan chan string, errchan chan error) { - var resp *csi.CreateSnapshotResponse - var err error - req := &csi.CreateSnapshotRequest{ - SourceVolumeId: f.volID, - Name: name, - } - if req != nil { - resp, err = f.createSnapshot(req) - if resp != nil { - fmt.Println("response for createSnapshot", resp) - idchan <- resp.GetSnapshot().GetSnapshotId() - } else { - fmt.Println("response for createSnapshot is nil") - idchan <- "" - } - } - errchan <- err - }(name, idchan, errchan) - } - // wait on complete, collecting ids and errors - nerrors := 0 - for i := 0; i < nSnaps; i++ { - var id string - var err error - id = <-idchan - if id != "" { - f.snapIDList = append(f.snapIDList, id) - } - err = <-errchan - if err != nil { - fmt.Printf("create snapshot received error: %s\n", err.Error()) - f.addError(err) - nerrors++ - } - } - t1 := time.Now() - if len(f.snapIDList) > nSnaps { - f.snapIDList = f.snapIDList[0:nSnaps] - } - fmt.Printf("Create snapshot time for %d snap %d errors: %v %v\n", nSnaps, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nSnaps)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCreateVolumesFromSnapshotInParallel(nVols int) error { - idchan := make(chan string, nVols) - errchan := make(chan error, nVols) - t0 := time.Now() - // Send requests - for i := 0; i < nVols; i++ { - name := fmt.Sprintf("scale%d", i) - go func(name string, idchan chan string, errchan chan error) { - var resp *csi.CreateVolumeResponse - var err error - req := f.getMountVolumeRequest(name) - source := &csi.VolumeContentSource_SnapshotSource{SnapshotId: f.snapshotID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Snapshot{Snapshot: source} - if req != nil { - resp, err = f.createVolume(req) - if resp != nil { - idchan <- resp.GetVolume().VolumeId - } else { - idchan <- "" - } - } - errchan <- err - }(name, idchan, errchan) - } - // Wait on complete, collecting ids and errors - nerrors := 0 - for i := 0; i < nVols; i++ { - var id string - var err error - id = <-idchan - if id != "" { - f.volIDList = append(f.volIDList, id) - } - err = <-errchan - if err != nil { - fmt.Printf("create volume received error: %s\n", err.Error()) - f.addError(err) - nerrors++ - } - } - t1 := time.Now() - if len(f.volIDList) > nVols { - f.volIDList = f.volIDList[0:nVols] - } - fmt.Printf("Create volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCreateVolumesFromVolumeInParallel(nVols int) error { - idchan := make(chan string, nVols) - errchan := make(chan error, nVols) - t0 := time.Now() - // Send requests - for i := 0; i < nVols; i++ { - name := fmt.Sprintf("scale%d", i) - go func(name string, idchan chan string, errchan chan error) { - var resp *csi.CreateVolumeResponse - var err error - req := f.getMountVolumeRequest(name) - source := &csi.VolumeContentSource_VolumeSource{VolumeId: f.volID} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Volume{Volume: source} - if req != nil { - resp, err = f.createVolume(req) - if resp != nil { - idchan <- resp.GetVolume().VolumeId - } else { - idchan <- "" - } - } - errchan <- err - }(name, idchan, errchan) - } - // Wait on complete, collecting ids and errors - nerrors := 0 - for i := 0; i < nVols; i++ { - var id string - var err error - id = <-idchan - if id != "" { - f.volIDList = append(f.volIDList, id) - } - err = <-errchan - if err != nil { - fmt.Printf("create volume received error: %s\n", err.Error()) - f.addError(err) - nerrors++ - } - } - t1 := time.Now() - if len(f.volIDList) > nVols { - f.volIDList = f.volIDList[0:nVols] - } - fmt.Printf("Create volume time for %d volumes %d errors: %v %v\n", nVols, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nVols)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCallDeleteSnapshotInParallel() error { - nSnaps := len(f.snapIDList) - fmt.Printf("The number of snapshots to delete%d ", nSnaps) - done := make(chan bool, nSnaps) - errchan := make(chan error, nSnaps) - t0 := time.Now() - // Send requests - for index := 0; index < nSnaps; index++ { - go func(snapID string, done chan bool, errchan chan error) { - err := f.deleteSnapshot(snapID) - done <- true - errchan <- err - }(f.snapIDList[index], done, errchan) - } - // Wait on complete, collecting ids and errors - nerrors := 0 - for i := 0; i < nSnaps; i++ { - if f.snapIDList[i] == "" { - continue - } - finished := <-done - if !finished { - return errors.New("premature completion") - } - err := <-errchan - if err != nil { - fmt.Printf("Delete snapshot received error: %s\n", err.Error()) - f.addError(err) - nerrors++ - } - } - t1 := time.Now() - fmt.Printf("Delete Snapshot time for %d Snaphots %d errors: %v %v\n", nSnaps, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nSnaps)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCallCreateSnapshotOnNewVolume() error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - volID := f.volIDList[len(f.volIDList)-1] - req := &csi.CreateSnapshotRequest{ - SourceVolumeId: volID, - Name: "snapshot-0eb5347a-0000-11e9-ab1c-005056a64ad3", - } - resp, err := client.CreateSnapshot(ctx, req) - if err != nil { - fmt.Printf("CreateSnapshot on new volume returned error: %s\n", err.Error()) - f.addError(err) - } else { - f.snapshotID = resp.Snapshot.SnapshotId - f.snapIDList = append(f.snapIDList, f.snapshotID) - fmt.Printf("createSnapshot: SnapshotId %s SourceVolumeId %s CreationTime %s\n", - resp.Snapshot.SnapshotId, resp.Snapshot.SourceVolumeId, resp.Snapshot.CreationTime.AsTime().Format(time.RFC3339Nano)) - } - time.Sleep(RetrySleepTime) - return nil -} - -func (f *feature) iCallCreateVolumeFromNewVolume() error { - req := f.createVolumeRequest - req.Name = "volFromVol-" + req.Name - // Take the latest VolumeId created from f.volIDList - source := &csi.VolumeContentSource_VolumeSource{VolumeId: f.volIDList[len(f.volIDList)-1]} - req.VolumeContentSource = new(csi.VolumeContentSource) - req.VolumeContentSource.Type = &csi.VolumeContentSource_Volume{Volume: source} - fmt.Printf("Calling CreateVolume with new volume source") - _ = f.createAVolume(req, "single CreateVolume from new Volume") - time.Sleep(SleepTime) - return nil -} - -func (f *feature) createSnapshot(req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - var resp *csi.CreateSnapshotResponse - var err error - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - resp, err = client.CreateSnapshot(ctx, req) - if err == nil { - break - } - fmt.Printf("Create Snapshot retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - return resp, err -} - -func (f *feature) deleteSnapshot(snapID string) error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - req := &csi.DeleteSnapshotRequest{ - SnapshotId: snapID, - } - var err error - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - _, err = client.DeleteSnapshot(ctx, req) - if err == nil { - break - } - fmt.Printf("DeleteSnapshot ID %s: retry %s\n", req.SnapshotId, err.Error()) - time.Sleep(RetrySleepTime) - } - return err -} - -func (f *feature) iCreateASnapshotPerVolumeInParallel() error { - nSnaps := len(f.volIDList) // nSnaps is the no. of vols present, on which we make 1 snap each - idchan := make(chan string, nSnaps) - errchan := make(chan error, nSnaps) - t0 := time.Now() - // Send requests - for i := 0; i < nSnaps; i++ { - name := fmt.Sprintf("Scale_Test_Snap%d", i) - go func(volID, name string, idchan chan string, errchan chan error) { - var resp *csi.CreateSnapshotResponse - var err error - req := &csi.CreateSnapshotRequest{ - SourceVolumeId: volID, - Name: name, - } - if req != nil { - resp, err = f.createSnapshot(req) - if resp != nil { - fmt.Println("response for createSnapshot", resp) - idchan <- resp.GetSnapshot().GetSnapshotId() - } else { - fmt.Println("response for createSnapshot is nil") - idchan <- "" - } - } - errchan <- err - }(f.volIDList[i], name, idchan, errchan) - } - // wait on complete, collecting ids and errors - nerrors := 0 - for i := 0; i < nSnaps; i++ { - var id string - var err error - id = <-idchan - if id != "" { - f.snapIDList = append(f.snapIDList, id) - } - err = <-errchan - if err != nil { - fmt.Printf("create snapshot received error: %s\n", err.Error()) - f.addError(err) - nerrors++ - } - } - t1 := time.Now() - if len(f.snapIDList) > nSnaps { - f.snapIDList = f.snapIDList[0:nSnaps] - } - fmt.Printf("Create snapshot time for %d snap %d errors: %v %v\n", nSnaps, nerrors, t1.Sub(t0).Seconds(), t1.Sub(t0).Seconds()/float64(nSnaps)) - time.Sleep(SleepTime) - return nil -} - -func (f *feature) iCallDeleteSnapshotAndCreateSnapshotInParallel() error { - errChan := make(chan error, 2) - go func() { - err := f.iCallCreateSnapshot() - errChan <- err - }() - go func() { - err := f.iCallDeleteSnapshot() - errChan <- err - }() - for i := 0; i < 2; i++ { - err := <-errChan - if err != nil { - f.addError(err) - } - } - return nil -} - -func (f *feature) iCheckIfVolumeExist() error { - _, _, devID, err := f.parseCsiID(f.volID) - if err != nil { - fmt.Printf("volID: %s malformed. Error: %s:\n", f.volID, err.Error()) - } - volume, err := f.pmaxClient.GetVolumeByID(context.Background(), f.symID, devID) - if err != nil { - fmt.Printf("GetVolumeByID %s:\n", err.Error()) - f.addError(err) - } else { - if strings.Contains(volume.VolumeIdentifier, "DS") { - fmt.Printf("Volume (%s) exist and is tagged to delete (%s)\n", volume.VolumeID, volume.VolumeIdentifier) - } else { - f.addError(fmt.Errorf("volume (%s) exist and Soft-Delete Failed", volume.VolumeID)) - } - } - return nil -} - -func (f *feature) iCheckIfVolumeIsDeleted() error { - volName, _, devID, err := f.parseCsiID(f.volID) - if err != nil { - fmt.Printf("volID: %s malformed. Error: %s:\n", f.volID, err.Error()) - } - return f.checkIfVolumeIsDeleted(volName, f.symID, devID) -} - -func (f *feature) checkIfVolumeIsDeleted(volName, symID, devID string) error { - vol, err := f.pmaxClient.GetVolumeByID(context.Background(), symID, devID) - if err != nil { - fmt.Printf("GetVolumeByID : %s", err.Error()) - fmt.Println(", Volume is successfully deleted") - } else { - if volName+"-DS" == vol.VolumeIdentifier { - f.addError(errors.New("Volume still exist")) - fmt.Printf("volume (%s) exist", vol.VolumeID) - } else { - fmt.Println("Volume is successfully deleted") - } - } - return nil -} - -func (f *feature) iDeleteASnapshot() error { - f.snapshotID = f.snapIDList[0] - f.iCallDeleteSnapshot() - if len(f.snapIDList) > 1 { - f.snapIDList = f.snapIDList[1:] - } - return nil -} - -func (f *feature) parseCsiID(csiID string) ( - volName string, arrayID string, devID string, err error, -) { - if csiID == "" { - err = fmt.Errorf("A Volume ID is required for the request") - return - } - // get the Device ID and Array ID - idComponents := strings.Split(csiID, "-") - // Protect against mal-formed component - numOfIDComponents := len(idComponents) - if numOfIDComponents < 3 { - // Not well formed - err = fmt.Errorf("The CSI ID %s is not formed correctly", csiID) - return - } - // Device ID is the last token - devID = idComponents[numOfIDComponents-1] - // Array ID is the second to last token - arrayID = idComponents[numOfIDComponents-2] - - // The two here is for two dashes - one at front of array ID and one between the Array ID and Device ID - lengthOfTrailer := len(devID) + len(arrayID) + 2 - length := len(csiID) - if length <= lengthOfTrailer+2 { - // Not well formed... - err = fmt.Errorf("The CSI ID %s is not formed correctly", csiID) - return - } - // calculate the volume name, which is everything before the array ID - volName = csiID[0 : length-lengthOfTrailer] - return -} - -func (f *feature) whenICallExpandVolumeToCylinders(nCYL int64) error { - err := f.controllerExpandVolume(f.volID, nCYL) - if err != nil { - fmt.Printf("ControllerExpandVolume %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("ControllerExpandVolume completed successfully\n") - } - time.Sleep(SleepTime) - return nil -} - -func (f *feature) controllerExpandVolume(volID string, nCYL int64) error { - const cylinderSizeInBytes = 1966080 - var resp *csi.ControllerExpandVolumeResponse - var err error - var req *csi.ControllerExpandVolumeRequest - if nCYL != 0 { - req = &csi.ControllerExpandVolumeRequest{ - VolumeId: volID, - CapacityRange: &csi.CapacityRange{RequiredBytes: nCYL * cylinderSizeInBytes}, - } - } else { - req = &csi.ControllerExpandVolumeRequest{ - VolumeId: volID, - } - } - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - resp, err = client.ControllerExpandVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("Controller ExpandVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - f.expandVolumeResponse = resp - return err -} - -func (f *feature) whenICallNodeExpandVolume() error { - nodePublishReq := f.nodePublishVolumeRequest - if nodePublishReq == nil { - err := fmt.Errorf("Volume is not stage, nodePublishVolumeRequest not found") - return err - } - err := f.nodeExpandVolume(f.volID, nodePublishReq.TargetPath) - if err != nil { - fmt.Printf("NodeExpandVolume %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("NodeExpandVolume completed successfully\n") - } - time.Sleep(SleepTime) - return nil -} - -func (f *feature) nodeExpandVolume(volID, volPath string) error { - var resp *csi.NodeExpandVolumeResponse - var err error - req := &csi.NodeExpandVolumeRequest{ - VolumeId: volID, - VolumePath: volPath, - } - ctx := context.Background() - client := csi.NewNodeClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - resp, err = client.NodeExpandVolume(ctx, req) - if err == nil { - break - } - fmt.Printf("Node ExpandVolume retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - f.nodeExpandVolumeResponse = resp - return err -} - -func (f *feature) iCallNodeGetVolumeStats() error { - nodePublishReq := f.nodePublishVolumeRequest - if nodePublishReq == nil { - err := fmt.Errorf("Volume is not stage, nodePublishVolumeRequest not found") - return err - } - err := f.nodeGetVolumeStats(f.volID, nodePublishReq.TargetPath) - if err != nil { - fmt.Printf("NodeGetVolumeStats %s:\n", err.Error()) - f.addError(err) - } else { - fmt.Printf("NodeGetVolumeStats completed successfully\n") - } - time.Sleep(SleepTime) - return nil -} - -func (f *feature) nodeGetVolumeStats(volID string, volPath string) error { - var resp *csi.NodeGetVolumeStatsResponse - var err error - req := &csi.NodeGetVolumeStatsRequest{ - VolumeId: volID, - VolumePath: volPath, - } - ctx := context.Background() - client := csi.NewNodeClient(grpcClient) - // Retry loop to deal with API being overwhelmed - for i := 0; i < f.maxRetryCount; i++ { - resp, err = client.NodeGetVolumeStats(ctx, req) - if err == nil { - break - } - fmt.Printf("NodeGetVolumeStats retry: %s\n", err.Error()) - time.Sleep(RetrySleepTime) - } - fmt.Printf("NodeGetVolumeStats: (%v)", resp) - return err -} - -func (f *feature) iCallControllerGetVolume() error { - ctx := context.Background() - client := csi.NewControllerClient(grpcClient) - req := &csi.ControllerGetVolumeRequest{ - VolumeId: f.volID, - } - resp, err := client.ControllerGetVolume(ctx, req) - if err != nil { - fmt.Printf("ControllerGetVolume returned error: %s\n", err.Error()) - f.addError(err) - } - fmt.Printf("ControllerGetVolume: Volume %v VolumeCondition %s PublishedNodeIDs %v\n", - resp.Volume, resp.Status.VolumeCondition, resp.Status.PublishedNodeIds) - time.Sleep(RetrySleepTime) - return nil -} - -func (f *feature) iHaveSetHostIOLimitsOnTheStorageGroup() error { - f.setIOLimits = true - return nil -} - -func FeatureContext(s *godog.ScenarioContext) { - f := &feature{} - s.Step(`^a Powermax service$`, f.aPowermaxService) - s.Step(`^a basic block volume request "([^"]*)" "(\d+)"$`, f.aBasicBlockVolumeRequest) - s.Step(`^adds replication capability with mode "([^"]*)" namespace "([^"]*)"$`, f.addsReplicationCapability) - s.Step(`^adds auto SRDFG replication capability with mode "([^"]*)" namespace "([^"]*)"$`, f.addsAutoSRDFReplicationCapability) - s.Step(`^I call CreateVolume$`, f.iCallCreateVolume) - s.Step(`^when I call DeleteVolume$`, f.whenICallDeleteVolume) - s.Step(`^there are no errors$`, f.thereAreNoErrors) - s.Step(`^the error message should contain "([^"]*)"$`, f.theErrorMessageShouldContain) - s.Step(`^a mount volume request "([^"]*)"$`, f.aMountVolumeRequest) - s.Step(`^when I call PublishVolume$`, f.whenICallPublishVolume) - s.Step(`^when I call UnpublishVolume$`, f.whenICallUnpublishVolume) - s.Step(`^when I call PublishVolume "([^"]*)"$`, f.whenICallPublishVolume) - s.Step(`^when I call UnpublishVolume "([^"]*)"$`, f.whenICallUnpublishVolume) - s.Step(`^when I call ExpandVolume to (\d+) cylinders$$`, f.whenICallExpandVolumeToCylinders) - s.Step(`^when I call NodeExpandVolume$`, f.whenICallNodeExpandVolume) - s.Step(`^access type is "([^"]*)"$`, f.accessTypeIs) - s.Step(`^max retries (\d+)$`, f.maxRetries) - s.Step(`^a capability with voltype "([^"]*)" access "([^"]*)" fstype "([^"]*)"$`, f.aCapabilityWithVoltypeAccessFstype) - s.Step(`^a volume request "([^"]*)" "(\d+)"$`, f.aVolumeRequest) - s.Step(`^when I call NodePublishVolume "([^"]*)"$`, f.whenICallNodePublishVolume) - s.Step(`^when I call NodeUnpublishVolume "([^"]*)"$`, f.whenICallNodeUnpublishVolume) - s.Step(`^verify published volume with voltype "([^"]*)" access "([^"]*)" fstype "([^"]*)"$`, f.verifyPublishedVolumeWithVoltypeAccessFstype) - s.Step(`^I call CreateSnapshot$`, f.iCallCreateSnapshot) - s.Step(`^I call CreateSnapshotConsistencyGroup$`, f.iCallCreateSnapshotConsistencyGroup) - s.Step(`^when I call DeleteAllVolumes$`, f.whenICallDeleteAllVolumes) - s.Step(`^I call DeleteSnapshot$`, f.iCallDeleteSnapshot) - s.Step(`^I call LinkVolumeToSnapshot$`, f.iCallLinkVolumeToSnapshot) - s.Step(`^I call LinkVolumeToSnapshotAgain$`, f.iCallLinkVolumeToSnapshotAgain) - s.Step(`^I call CreateManyVolumesFromSnapshot$`, f.iCallCreateManyVolumesFromSnapshot) - s.Step(`^I call ListVolume$`, f.iCallListVolume) - s.Step(`^a valid ListVolumeResponse is returned$`, f.aValidListVolumeResponseIsReturned) - s.Step(`^I call ListSnapshot$`, f.iCallListSnapshot) - s.Step(`^a valid ListSnapshotResponse is returned$`, f.aValidListSnapshotResponseIsReturned) - s.Step(`^I create (\d+) volumes in parallel$`, f.iCreateVolumesInParallel) - s.Step(`^I publish (\d+) volumes in parallel$`, f.iPublishVolumesInParallel) - s.Step(`^I node publish (\d+) volumes in parallel$`, f.iNodePublishVolumesInParallel) - s.Step(`^I node unpublish (\d+) volumes in parallel$`, f.iNodeUnpublishVolumesInParallel) - s.Step(`^I unpublish (\d+) volumes in parallel$`, f.iUnpublishVolumesInParallel) - s.Step(`^when I delete (\d+) volumes in parallel$`, f.whenIDeleteVolumesInParallel) - s.Step(`^an alternate ServiceLevel "([^"]*)"$`, f.anAlternateServiceLevel) - s.Step(`^I receive a valid volume$`, f.iReceiveAValidVolume) - s.Step(`^all volumes are deleted successfully$`, f.allVolumesAreDeletedSuccessfully) - s.Step(`^I use thick provisioning$`, f.iUseThickProvisioning) - s.Step(`^I munge the CSI VolumeId$`, f.iMungeTheCSIVolumeID) - s.Step(`^I unmunge the CSI VolumeId$`, f.iUnmungeTheCSIVolumeID) - s.Step(`^the volume is not deleted$`, f.theVolumeIsNotDeleted) - s.Step(`^a get capacity request "([^"]*)"$`, f.aGetCapacityRequest) - s.Step(`^I call GetCapacity$`, f.iCallGetCapacity) - s.Step(`^a valid GetCapacityResponse is returned$`, f.aValidGetCapacityResponseIsReturned) - s.Step(`^an idempotent test$`, f.anIdempotentTest) - s.Step(`^a volume request with file system "([^"]*)" fstype "([^"]*)" access "([^"]*)" voltype "([^"]*)"$`, f.aVolumeRequestFileSystem) - s.Step(`^when I call NodeStageVolume "([^"]*)"$`, f.whenICallNodeStageVolume) - s.Step(`^when I call NodeUnstageVolume "([^"]*)"$`, f.whenICallNodeUnstageVolume) - s.Step(`^I node stage (\d+) volumes in parallel$`, f.iNodeStageVolumesInParallel) - s.Step(`^I node unstage (\d+) volumes in parallel$`, f.iNodeUnstageVolumesInParallel) - s.Step(`^the volume size is "([^"]*)"$`, f.theVolumeSizeIs) - s.Step(`^I call LinkVolumeToVolume$`, f.iCallLinkVolumeToVolume) - s.Step(`^I call LinkVolumeToVolumeAgain$`, f.iCallLinkVolumeToVolumeAgain) - s.Step(`^I create (\d+) snapshots in parallel$`, f.iCreateSnapshotsInParallel) - s.Step(`^I create (\d+) volumes from snapshot in parallel$`, f.iCreateVolumesFromSnapshotInParallel) - s.Step(`^I create (\d+) volumes from volume in parallel$`, f.iCreateVolumesFromVolumeInParallel) - s.Step(`^I call DeleteSnapshot in parallel$`, f.iCallDeleteSnapshotInParallel) - s.Step(`^I call DeleteAllSnapshots$`, f.ICallDeleteAllSnapshots) - s.Step(`^I call CreateSnapshot on new volume$`, f.iCallCreateSnapshotOnNewVolume) - s.Step(`^I call CreateVolume from new volume$`, f.iCallCreateVolumeFromNewVolume) - s.Step(`^I call DeleteSnapshot and CreateSnapshot in parallel$`, f.iCallDeleteSnapshotAndCreateSnapshotInParallel) - s.Step(`^I call CreateStorageProtectionGroup with mode "([^"]*)"$`, f.iCallCreateStorageProtectionGroup) - s.Step(`^I call CreateRemoteVolume with mode "([^"]*)"$`, f.iCallCreateRemoteVolume) - s.Step(`^I call Delete LocalStorageProtectionGroup$`, f.iCallDeleteLocalStorageProtectionGroup) - s.Step(`^I call Delete RemoteStorageProtectionGroup$`, f.iCallDeleteRemoteStorageProtectionGroup) - s.Step(`^I call ExecuteAction with action "([^"]*)"$`, f.iCallExecuteAction) - s.Step(`^I call GetStorageProtectionGroupStatus to get "([^"]*)"$`, f.iCallGetStorageProtectionGroupStatus) - s.Step(`^I check if volume exist$`, f.iCheckIfVolumeExist) - s.Step(`^I check if volume is deleted$`, f.iCheckIfVolumeIsDeleted) - s.Step(`^I delete a snapshot$`, f.iDeleteASnapshot) - s.Step(`^I create a snapshot per volume in parallel$`, f.iCreateASnapshotPerVolumeInParallel) - s.Step(`^when I call ControllerGetVolume$`, f.iCallControllerGetVolume) - s.Step(`^when I call NodeGetVolumeStats$`, f.iCallNodeGetVolumeStats) - s.Step(`^I have SetHostIOLimits on the storage group$`, f.iHaveSetHostIOLimitsOnTheStorageGroup) - s.Step(`^when I call DeleteLocalVolume`, f.whenICallDeleteLocalVolume) -} diff --git a/test/integration/validate_http_unauthorized.sh b/test/integration/validate_http_unauthorized.sh deleted file mode 100644 index acafde8f..00000000 --- a/test/integration/validate_http_unauthorized.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Copyright © 2020-2025 Dell Inc. or its subsidiaries. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -#!/bin/bash -source ../../env.sh -rm -rf unix_sock -nonhttp=$(echo $X_CSI_POWERMAX_ENDPOINT | sed 's/https:/http:/') -echo "testing http validation with URL: " $nonhttp -export X_CSI_POWERMAX_ENDPOINT=$nonhttp - -../../csi-powermax 2>stderr -grep "Unauthorized" stderr -rc=$? -echo rc $rc -if [ $rc -ne 0 ]; then echo "failed..."; else echo "passed"; fi -exit $rc diff --git a/test/scale/README.md b/test/scale/README.md deleted file mode 100644 index 0fe69dee..00000000 --- a/test/scale/README.md +++ /dev/null @@ -1,4 +0,0 @@ -## Scalability and Longevity Tests - -The Scalability and Longevity tests have been refactored into a seperate git repository. -