diff --git a/.gitignore b/.gitignore
index 173454be..8aac5e8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,7 @@
*.iml
out
gen
+go.sum
+docs/site/public
+docs/site/resources
+docs/site/.hugo_build.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 360b717c..968d9bcd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,12 @@
[Releases](https://github.com/Huawei/eSDK_K8S_Plugin/releases)
-## Changes since v4.3.0
+## v4.5.0
+
+- The default synchronization speed of hyper metro pair is changed from the highest speed to the default speed determined by the storage.
+- Fixed the semaphore timeout issue when a large number of PVs fail to be mounted.
+
+## v4.4.0
- Support Kubernetes 1.30
- Support OceanStor 6.1.8
@@ -12,7 +17,7 @@
- The new feature Modify Volume allows a normal PV to be changed to a HyperMetro PV
- Create VolumeSnapshot and Clone Persistent Volume support HyperMetro PV
-## Changes since v4.2.0
+## v4.3.0
- Support UltraPath 31.2.1/NVMe over RoCE on Rocky Linux 8.6 X86_64
- Support OpenShift 4.14
@@ -32,7 +37,7 @@
- Support configuring requests and limits of container
- The log directory of the oceanctl tool can be configured
-## Changes since v4.1.0
+## v4.2.0
- Support OpenShift 4.13
- Support Centos 8.4 X86_64
@@ -42,7 +47,7 @@
- Support configuring the timeout for executing commands
- Support create volume snapshot for Hyper-Metro
-## Changes since v4.0.0
+## v4.1.0
**Enhancements**
@@ -52,7 +57,7 @@
- Support Kylin 7.6 x86_64
- The number of path groups aggregated by DM-multipath can be configured
-## Changes since v3.2.x
+## v4.0.0
**Enhancements**
@@ -66,7 +71,7 @@
- Support k8s 1.26
- Upgrade using go 1.18
-## Changes since v3.1.0
+## v3.2.0
**Enhancements**
@@ -79,7 +84,7 @@
- Support Ubuntu 22.04, SUSE 15 SP3
- Support EulerOS V2R10
-## Changes since v3.0.0
+## v3.1.0
**Enhancements**
@@ -90,7 +95,7 @@
- Support k8s 1.19 and 1.20
- Support Debian 11
-## Changes since v2.2.16
+## v3.0.0
**Enhancements**
@@ -105,7 +110,7 @@
- Support OceanStor V6 6.1.5
- Support OceanStor Pacific 8.1.3
-## Changes since v2.2.15
+## v2.2.16
**Enhancements**
diff --git a/CI/.cloudbuild/build.yml b/CI/.cloudbuild/build.yml
deleted file mode 100644
index 86d4a123..00000000
--- a/CI/.cloudbuild/build.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-
----
-version: 2.0
-
-# 构建环境
-
-
-# 构建参数定义, 构建脚本可从环境变量中读取使用这些参数
-params:
- - name: upload_to_Cloudcmc
- value: 'false'
- - name: upload_to_Enterprisecmc
- value: 'false'
- - name: RELEASE_VER
- value: 2.3.2
- - name: VER
- value: 2.2.13.4
- - name: PLATFORM
- value: X86
- - name: esdk_ci_branch
- value: master
- - name: DRCSI_branch
- value: master
- - name: dockerimg
- value: 'szvecr02.his.huawei.com:80/ecr-build/esdk_suse_x86_12sp5:2.1.RC1'
-
-
-env:
- resource:
- type: docker
- image: ${dockerimg}
- resource_class: 4U4G
- pool: docker-gz-x86-ondocker-16u-01
-
-steps:
- PRE_BUILD:
- - checkout:
- path: eSDK_Enterprise_Storage_Kubernetes
- - codehub:
- url: https://codehub-dg-y.huawei.com/esdk/esdk_public/esdk_ci.git
- branch: ${esdk_ci_branch}
- path: esdk_ci
- - codehub:
- url: https://codehub-dg-y.huawei.com/esdk/XuanWu/Public/DRCSI/DRCSI.git
- branch: ${DRCSI_branch}
- path: DRCSI
- BUILD:
- - build_execute:
- command: sh eSDK_Enterprise_Storage_Kubernetes/CI/build.sh ${RELEASE_VER} ${VER} ${PLATFORM};echo "buildVersion=${eSDK_version}.$(date "+%Y%m%d%H%M%S")" > buildInfo.properties
- accelerate: false
- check: true
- POST_BUILD:
- - get_build_metadata
- - upload_cloud_artifact:
- file_path: 'eSDK_Enterprise_Storage_Kubernetes/*.zip*'
- - version_set
- - when:
- condition: upload_to_Cloudcmc == 'true'
- steps:
- - get_build_metadata
- - artget:
- artifact_type: cmcbinary
- action: push
- dependency: eSDK_Enterprise_Storage_Kubernetes/CI/conf/cmc_dependency.xml
- version_output_path: .
- username: ${cmc_username}
- password: ${cmc_password}
- agent: .
- cache: /home/
- add_source_code: true
- params: {'version':'${Cloudversion}','dir':'eSDK_Enterprise_Storage_Kubernetes/eSDK_Huawei_Storage*.zip*','dist':'${dist}','offering':'eSDK Cloud Storage Plugins'}
- - get_build_metadata
- - when:
- condition: upload_to_Enterprisecmc == 'true'
- steps:
- - get_build_metadata
- - artget:
- artifact_type: cmcbinary
- action: push
- dependency: eSDK_Enterprise_Storage_Kubernetes/CI/conf/cmc_dependency.xml
- version_output_path: .
- username: ${cmc_username}
- password: ${cmc_password}
- agent: .
- cache: /home/
- add_source_code: true
- params: {'version':'${Enterpriseversion}','dir':'eSDK_Enterprise_Storage_Kubernetes/eSDK_Huawei_Storage*.zip*','dist':'${dist}','offering':'eSDK Enterprise Storage Plugins'}
- - get_build_metadata
-
diff --git a/CI/build.sh b/CI/build.sh
deleted file mode 100644
index 5d635028..00000000
--- a/CI/build.sh
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. 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.
-#
-
-export GOPROXY=http://mirrors.tools.huawei.com/goproxy/
-
-if [ ${PLATFORM} == X86 ];then
- wget http://10.29.160.97/busybox-x86.tar
- wget http://10.29.160.97/gcr-x86.tar
- docker load -i busybox-x86.tar > imageidfile
- docker load -i gcr-x86.tar > imageidfile1
- imageId=$(cat imageidfile|awk '{print $3}')
- imageId1=$(cat imageidfile1|awk '{print $3}')
- docker tag busybox:1.36.1 busybox:stable-glibc
- docker tag ${imageId1} gcr.io/distroless/base:latest
-elif [ ${PLATFORM} == ARM ]; then
- wget http://10.29.160.97/busybox-arm.tar
- wget http://10.29.160.97/gcr-arm.tar
- docker load -i busybox-arm.tar > imageidfile
- docker load -i gcr-arm.tar > imageidfile1
- imageId=$(cat imageidfile|awk '{print $3}')
- imageId1=$(cat imageidfile1|awk '{print $3}')
- docker tag busybox:1.36.1 busybox:stable-glibc
- docker tag ${imageId1} gcr.io/distroless/base:latest
-fi
-
-package_name="eSDK_Huawei_Storage_${RELEASE_VER}_Kubernetes_CSI_Plugin_V${VER}_${PLATFORM}_64"
-
-cd eSDK_Enterprise_Storage_Kubernetes
-go mod tidy || go mod tidy
-make -f Makefile RELEASE_VER=$1 VER=$2 PLATFORM=$3
-
-# -------------------------------------------------------------------------------
-echo "Platform confirmation"
-if [[ "${PLATFORM}" == "ARM" ]];then
- PULL_FLAG="--platform=arm64"
- BUILD_FLAG="--platform linux/arm64"
-elif [[ "${PLATFORM}" == "X86" ]];then
- PULL_FLAG="--platform=amd64"
- BUILD_FLAG="--platform linux/amd64"
-else
- echo "Wrong PLATFORM, support [X86, ARM]"
- exit
-fi
-
-rm -rf build_dir
-rm -f ./huawei-csi
-rm -f ./storage-backend-controller
-rm -f ./storage-backend-sidecar
-rm -f ./huawei-csi-extender
-unzip -d build_dir -q ${package_name}.zip
-mv build_dir/${package_name}/bin/huawei-csi ./
-mv build_dir/${package_name}/bin/storage-backend-controller ./
-mv build_dir/${package_name}/bin/storage-backend-sidecar ./
-mv build_dir/${package_name}/bin/huawei-csi-extender ./
-
-docker build ${BUILD_FLAG} --build-arg VERSION=${VER} --target huawei-csi-driver -f Dockerfile -t huawei-csi:${VER} .
-docker build ${BUILD_FLAG} --build-arg VERSION=${VER} --target storage-backend-controller -f Dockerfile -t storage-backend-controller:${VER} .
-docker build ${BUILD_FLAG} --build-arg VERSION=${VER} --target storage-backend-sidecar -f Dockerfile -t storage-backend-sidecar:${VER} .
-docker build ${BUILD_FLAG} --build-arg VERSION=${VER} --target huawei-csi-extender -f Dockerfile -t huawei-csi-extender:${VER} .
-
-
-plat=$(echo ${PLATFORM}|tr 'A-Z' 'a-z')
-docker save huawei-csi:${VER} -o huawei-csi-v${VER}-${plat}.tar
-docker save storage-backend-controller:${VER} -o storage-backend-controller-v${VER}-${plat}.tar
-docker save storage-backend-sidecar:${VER} -o storage-backend-sidecar-v${VER}-${plat}.tar
-docker save huawei-csi-extender:${VER} -o huawei-csi-extender-v${VER}-${plat}.tar
-
-
-mkdir build_dir/${package_name}/image
-mv huawei-csi-v${VER}-${plat}.tar build_dir/${package_name}/image
-mv storage-backend-controller-v${VER}-${plat}.tar build_dir/${package_name}/image
-mv storage-backend-sidecar-v${VER}-${plat}.tar build_dir/${package_name}/image
-mv huawei-csi-extender-v${VER}-${plat}.tar build_dir/${package_name}/image
-# -------------------------------------------------------------------------------
-
-rm -rf ${package_name}.zip
-cd build_dir
-zip -rq ../${package_name}.zip *
-# 签名
-cd ..
-mkdir sign
-mv ${package_name}.zip sign
-sh esdk_ci/ci/build_product_signature.sh $(pwd)/sign
-mkdir cms
-mv sign/*.cms .
-sh esdk_ci/ci/build_product_signature_hwp7s.sh $(pwd)/sign
-mv sign/* ${WORKSPACE}/eSDK_Enterprise_Storage_Kubernetes
\ No newline at end of file
diff --git a/CI/conf/cmc_dependency.xml b/CI/conf/cmc_dependency.xml
deleted file mode 100644
index a77c7ba9..00000000
--- a/CI/conf/cmc_dependency.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
- BVersion
- Generic
-
- ${offering}
- ${version}
-
- N
-
-
-
- ${dist}
-
-
-
-
-
- BVersion
- Generic
-
- eSDK Enterprise Storage Plugins
- eSDK Enterprise Storage Plugins 2.3.RC1.B040
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 0b4c162c..4c719d68 100644
--- a/Makefile
+++ b/Makefile
@@ -9,9 +9,10 @@ PLATFORM=PLATFORM
RELEASE_VER=RELEASE_VER
# (Optional) [TRUE FALSE] Compile Binary Only, Cancel Inline Optimization
ONLY_BIN=ONLY_BIN
+# (Optional) [github] Specifies the platform which to build on
+BUILD_ON=BUILD_ON
export GO111MODULE=on
-export GOPATH:=$(GOPATH):$(shell pwd)
ifeq (${RELEASE_VER}, RELEASE_VER)
export PACKAGE=eSDK_Huawei_Storage_Kubernetes_CSI_Plugin_V${VER}_${PLATFORM}_64
@@ -24,16 +25,26 @@ ifeq (${ONLY_BIN}, TRUE)
all:PREPARE BUILD PACK
# Disable inline optimization
flag = -gcflags "all=-N -l"
+binary_flag = -gcflags "all=-N -l"
else
-flag = -ldflags="-s" -buildmode=pie
+flag = -ldflags="-s -linkmode 'external' -extldflags '-Wl,-z,now'" -buildmode=pie
+binary_flag = -ldflags="-s" -buildmode=pie
all:PREPARE BUILD COPY_FILE PACK
endif
# Platform [X86, ARM]
ifeq (${PLATFORM}, X86)
-env = CGO_ENABLED=0 GOOS=linux GOARCH=amd64
+env = CGO_CFLAGS="-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2" GOOS=linux GOARCH=amd64
+binary_env = CGO_ENABLED=0 GOOS=linux GOARCH=amd64
else
-env = CGO_ENABLED=0 GOOS=linux GOARCH=arm64
+env = CGO_CFLAGS="-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2" GOOS=linux GOARCH=arm64
+binary_env = CGO_ENABLED=0 GOOS=linux GOARCH=arm64
+endif
+
+# Build_ON [github]
+ifeq ($(BUILD_ON), github)
+flag = -ldflags="-s -bindnow" -buildmode=pie
+env = $(binary_env)
endif
PREPARE:
@@ -42,11 +53,12 @@ PREPARE:
BUILD:
# usage: [env] go build [-o output] [flags] packages
+ go mod tidy
${env} go build -o ./${PACKAGE}/bin/huawei-csi ${flag} ./csi
${env} go build -o ./${PACKAGE}/bin/storage-backend-controller ${flag} ./cmd/storage-backend-controller
${env} go build -o ./${PACKAGE}/bin/storage-backend-sidecar ${flag} ./cmd/storage-backend-sidecar
${env} go build -o ./${PACKAGE}/bin/huawei-csi-extender ${flag} ./cmd/huawei-csi-extender
- ${env} go build -o ./${PACKAGE}/bin/oceanctl ${flag} ./cli
+ ${binary_env} go build -o ./${PACKAGE}/bin/oceanctl ${binary_flag} ./cli
COPY_FILE:
mkdir -p ./${PACKAGE}/examples
diff --git a/build.sh b/build.sh
index f913d9da..ff7ae735 100644
--- a/build.sh
+++ b/build.sh
@@ -25,7 +25,7 @@ PLATFORM=$2
package_name="eSDK_Huawei_Storage_Kubernetes_CSI_Plugin_V${VER}_${PLATFORM}_64"
echo "Start to make with Makefile"
-make -f Makefile VER=$1 PLATFORM=$2
+make -f Makefile VER=$1 PLATFORM=$2 BUILD_ON=github
echo "Platform confirmation"
if [[ "${PLATFORM}" == "ARM" ]];then
diff --git a/cli/client/client_helper.go b/cli/client/client_helper.go
index fc18acb4..cd4244d4 100644
--- a/cli/client/client_helper.go
+++ b/cli/client/client_helper.go
@@ -21,10 +21,10 @@ import (
"encoding/json"
"errors"
"fmt"
+ "path"
"reflect"
corev1 "k8s.io/api/core/v1"
- k8string "k8s.io/utils/strings"
"huawei-csi-driver/cli/helper"
xuanwuV1 "huawei-csi-driver/client/apis/xuanwu/v1"
@@ -86,7 +86,7 @@ func (r *CommonCallHandler[T]) DeleteByNames(namespace string, names ...string)
return err
}
for _, name := range names {
- qualifiedNames = append(qualifiedNames, k8string.JoinQualifiedName(string(resourceType), name))
+ qualifiedNames = append(qualifiedNames, path.Join(string(resourceType), name))
}
_, err = r.client.DeleteResourceByQualifiedNames(qualifiedNames, namespace)
return err
diff --git a/cli/client/client_helper_test.go b/cli/client/client_helper_test.go
index 07f408b7..6a11bca1 100644
--- a/cli/client/client_helper_test.go
+++ b/cli/client/client_helper_test.go
@@ -23,7 +23,7 @@ import (
"testing"
"github.com/agiledragon/gomonkey/v2"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -32,11 +32,11 @@ func TestGetObjectType_get_namespace_type(t *testing.T) {
// arrange
var mockObject corev1.Namespace
var except = Namespace
- convey.Convey("test get_namespace_type", t, func() {
+ t.Run("test get_namespace_type", func(t *testing.T) {
// action
objectType := getObjectType(&mockObject)
// assert
- convey.So(objectType, convey.ShouldResemble, except)
+ require.Equal(t, except, objectType)
})
}
@@ -44,11 +44,11 @@ func TestGetObjectType_get_node_type(t *testing.T) {
// arrange
var mockObject corev1.NodeList
var except = Node
- convey.Convey("test get_node_type", t, func() {
+ t.Run("test get_node_type", func(t *testing.T) {
// action
objectType := getObjectType(&mockObject)
// assert
- convey.So(objectType, convey.ShouldResemble, except)
+ require.Equal(t, except, objectType)
})
}
@@ -56,11 +56,11 @@ func TestGetObjectType_get_pod_type(t *testing.T) {
// arrange
var mockObject corev1.Pod
var except = Pod
- convey.Convey("test get_pod_type", t, func() {
+ t.Run("test get_pod_type", func(t *testing.T) {
// action
objectType := getObjectType(&mockObject)
// assert
- convey.So(objectType, convey.ShouldResemble, except)
+ require.Equal(t, except, objectType)
})
}
@@ -68,11 +68,11 @@ func TestGetObjectType_get_unknown_type(t *testing.T) {
// arrange
var mockObject interface{}
var except = Unknown
- convey.Convey("test get_unknown_type", t, func() {
+ t.Run("test get_unknown_type", func(t *testing.T) {
// action
objectType := getObjectType(&mockObject)
// assert
- convey.So(objectType, convey.ShouldResemble, except)
+ require.Equal(t, except, objectType)
})
}
@@ -97,12 +97,12 @@ func TestCommonCallHandler_CheckObjectExist_check_exist_namespace(t *testing.T)
})
defer patches.Reset()
- convey.Convey("test check_exist_namespace", t, func() {
+ t.Run("test check_exist_namespace", func(t *testing.T) {
// action
exist, err := mockCli.CheckObjectExist(context.Background(), mockNamespace, mockNodeName, mockObjectName)
// assert
- convey.So(exist, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, exist)
})
}
@@ -123,12 +123,12 @@ func TestCommonCallHandler_CheckObjectExist_check_not_exist_node(t *testing.T) {
})
defer patches.Reset()
- convey.Convey("test check_not_exist_node", t, func() {
+ t.Run("test check_not_exist_node", func(t *testing.T) {
// action
exist, err := mockCli.CheckObjectExist(context.Background(), mockNamespace, mockNodeName, mockObjectName)
// assert
- convey.So(exist, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, exist)
})
}
@@ -165,11 +165,11 @@ func TestCommonCallHandler_GetObject_get_podList(t *testing.T) {
})
defer patches.Reset()
- convey.Convey("test get pod list", t, func() {
+ t.Run("test get pod list", func(t *testing.T) {
// action
object, err := mockCli.GetObject(context.Background(), mockNamespace, mockNodeName)
// assert
- convey.So(object, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, object)
})
}
diff --git a/cli/client/kube_client_test.go b/cli/client/kube_client_test.go
index 44cf4224..ac10c7fb 100644
--- a/cli/client/kube_client_test.go
+++ b/cli/client/kube_client_test.go
@@ -21,7 +21,7 @@ import (
"encoding/json"
"testing"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
coreV1 "k8s.io/api/core/v1"
)
@@ -42,7 +42,7 @@ func TestKubernetesCLI_GetObject_success(t *testing.T) {
patches := mockExecReturnStdOut("kubectl get pod huawei-csi-node-9lxhm -n huawei-csi -o=json")
defer patches.Reset()
- convey.Convey("test get object success", t, func() {
+ t.Run("test get object success", func(t *testing.T) {
// action
if mockCli == nil {
t.Errorf("mockCLi is nil")
@@ -52,8 +52,8 @@ func TestKubernetesCLI_GetObject_success(t *testing.T) {
mockObjectName)
//assert
- convey.So(mockData, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, mockData)
})
}
@@ -69,7 +69,7 @@ func TestKubernetesCLI_GetObject_failed_without_objectType(t *testing.T) {
patches := mockExecReturnStdOut("kubectl get pod huawei-csi-node-9lxhm -n huawei-csi -o=json")
defer patches.Reset()
- convey.Convey("test get object success", t, func() {
+ t.Run("test get object success", func(t *testing.T) {
// action
if mockCli == nil {
t.Errorf("mockCLi is nil")
@@ -79,8 +79,8 @@ func TestKubernetesCLI_GetObject_failed_without_objectType(t *testing.T) {
mockObjectName)
//assert
- convey.So(mockData, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
+ require.Equal(t, except, mockData)
})
}
@@ -95,7 +95,7 @@ func TestKubernetesCLI_CopyContainerFileToLocal_success(t *testing.T) {
"/tmp/slave1/a.tar -c huawei-csi-driver")
defer patches.Reset()
- convey.Convey("test copy file from container to local", t, func() {
+ t.Run("test copy file from container to local", func(t *testing.T) {
// action
if mockCli == nil {
t.Errorf("mockCLi is nil")
@@ -105,8 +105,8 @@ func TestKubernetesCLI_CopyContainerFileToLocal_success(t *testing.T) {
mockObjectName)
//assert
- convey.So(out, convey.ShouldResemble, []byte(returnStr))
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, []byte(returnStr), out)
})
}
@@ -120,7 +120,7 @@ func TestKubernetesCLI_GetConsoleLogs_success(t *testing.T) {
patches := mockExecReturnStdOut("kubectl logs huawei-csi-node-9lxhm -c huawei-csi-driver -n huawei-csi")
defer patches.Reset()
- convey.Convey("test get container console logs", t, func() {
+ t.Run("test get container console logs", func(t *testing.T) {
// action
if mockCli == nil {
t.Errorf("mockCLi is nil")
@@ -129,8 +129,8 @@ func TestKubernetesCLI_GetConsoleLogs_success(t *testing.T) {
out, err := mockCli.GetConsoleLogs(mockCtx, mockNamespace, mockContainerName, false, mockObjectName)
//assert
- convey.So(out, convey.ShouldResemble, []byte(returnStr))
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, []byte(returnStr), out)
})
}
@@ -145,7 +145,7 @@ func TestKubernetesCLI_ExecCmdInSpecifiedContainer_success(t *testing.T) {
"-n huawei-csi -- collect.sh")
defer patches.Reset()
- convey.Convey("test exec script in container", t, func() {
+ t.Run("test exec script in container", func(t *testing.T) {
// action
if mockCli == nil {
t.Errorf("mockCLi is nil")
@@ -155,7 +155,7 @@ func TestKubernetesCLI_ExecCmdInSpecifiedContainer_success(t *testing.T) {
mockObjectName)
//assert
- convey.So(out, convey.ShouldResemble, []byte(returnStr))
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, []byte(returnStr), out)
})
}
diff --git a/cli/client/kubectl_options.go b/cli/client/kubectl_options.go
index 4a6bb018..848404a8 100644
--- a/cli/client/kubectl_options.go
+++ b/cli/client/kubectl_options.go
@@ -22,7 +22,7 @@ import (
"errors"
"fmt"
- "github.com/ghodss/yaml"
+ "gopkg.in/yaml.v3"
"huawei-csi-driver/cli/helper"
)
diff --git a/cli/client/kubectl_options_test.go b/cli/client/kubectl_options_test.go
index fa8eb094..d48390c2 100644
--- a/cli/client/kubectl_options_test.go
+++ b/cli/client/kubectl_options_test.go
@@ -25,7 +25,7 @@ import (
"testing"
"github.com/agiledragon/gomonkey/v2"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
coreV1 "k8s.io/api/core/v1"
)
@@ -69,13 +69,13 @@ func TestKubernetesCLIArgs_Get_failed_without_options(t *testing.T) {
patches := mockExecReturnStdOut("kubectl get pod huawei-csi-node-9lxhm -n huawei-csi -o=json")
defer patches.Reset()
- convey.Convey("test get object failed without options", t, func() {
+ t.Run("test get object failed without options", func(t *testing.T) {
// action
err := mockArgs.Get(mockCtx, &mockData)
//assert
- convey.So(mockData, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
+ require.Equal(t, except, mockData)
})
}
@@ -96,13 +96,13 @@ func TestKubernetesCLIArgs_Get_success(t *testing.T) {
patches := mockExecReturnStdOut("kubectl get pod huawei-csi-node-9lxhm -n huawei-csi -o=json")
defer patches.Reset()
- convey.Convey("test get object success", t, func() {
+ t.Run("test get object success", func(t *testing.T) {
// action
err := mockArgs.Get(mockCtx, &mockData)
//assert
- convey.So(mockData, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, mockData)
})
}
@@ -120,13 +120,13 @@ func TestKubernetesCLIArgs_Exec_success(t *testing.T) {
"-n huawei-csi -- collect.sh")
defer patches.Reset()
- convey.Convey("test exec cmd in container success", t, func() {
+ t.Run("test exec cmd in container success", func(t *testing.T) {
// action
out, err := mockArgs.Exec(mockCtx, mockCmd)
// assert
- convey.So(out, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, out)
})
}
@@ -145,13 +145,13 @@ func TestKubernetesCLIArgs_Copy_success(t *testing.T) {
"/tmp/slave1/a.tar -c huawei-csi-driver")
defer patches.Reset()
- convey.Convey("test copy file from container to local success", t, func() {
+ t.Run("test copy file from container to local success", func(t *testing.T) {
// action
out, err := mockArgs.Copy(mockCtx, mockContainerPath, mockLocalPath, mockCopyType)
// assert
- convey.So(out, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, out)
})
}
@@ -167,12 +167,12 @@ func TestKubernetesCLIArgs_Logs(t *testing.T) {
patches := mockExecReturnStdOut("kubectl logs huawei-csi-node-9lxhm -c huawei-csi-driver -n huawei-csi")
defer patches.Reset()
- convey.Convey("test get container console logs success", t, func() {
+ t.Run("test get container console logs success", func(t *testing.T) {
// action
out, err := mockArgs.Logs(mockCtx)
// assert
- convey.So(out, convey.ShouldResemble, except)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, except, out)
})
}
diff --git a/cli/cmd/collect.go b/cli/cmd/collect.go
index 912c0191..6472ebdc 100644
--- a/cli/cmd/collect.go
+++ b/cli/cmd/collect.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-// Package command for collecting messages in Kubernetes.
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -23,7 +22,7 @@ import (
"huawei-csi-driver/cli/cmd/options"
)
-func init() {
+func registerCollectCmd() {
options.NewFlagsOptions(collectCmd).WithParent(RootCmd)
}
diff --git a/cli/cmd/collect_logs.go b/cli/cmd/collect_logs.go
index bf92ab7d..3f379812 100644
--- a/cli/cmd/collect_logs.go
+++ b/cli/cmd/collect_logs.go
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -25,7 +25,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerCollectLogsCmd() {
options.NewFlagsOptions(collectLogsCmd).
WithNameSpace(true).
WithAllNodes().
diff --git a/cli/cmd/create.go b/cli/cmd/create.go
index 5145ed49..cc43586e 100644
--- a/cli/cmd/create.go
+++ b/cli/cmd/create.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-// Package command is used to creating a resource to Ocean Storage in Kubernetes.
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -23,7 +22,7 @@ import (
"huawei-csi-driver/cli/cmd/options"
)
-func init() {
+func registerCreateCmd() {
options.NewFlagsOptions(CreateCmd).WithParent(RootCmd)
}
diff --git a/cli/cmd/create_backend.go b/cli/cmd/create_backend.go
index 95f6fa6a..f26561f6 100644
--- a/cli/cmd/create_backend.go
+++ b/cli/cmd/create_backend.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -26,7 +26,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerCreateBackendCmd() {
options.NewFlagsOptions(createBackendCmd).
WithNameSpace(false).
WithFilename(true).
diff --git a/cli/cmd/create_cert.go b/cli/cmd/create_cert.go
index f6e5b432..2257f88e 100644
--- a/cli/cmd/create_cert.go
+++ b/cli/cmd/create_cert.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -26,7 +26,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerCreateCertCmd() {
options.NewFlagsOptions(createSecretCmd).
WithNameSpace(false).
WithFilename(true).
diff --git a/cli/cmd/delete.go b/cli/cmd/delete.go
index e40136cc..40cef39a 100644
--- a/cli/cmd/delete.go
+++ b/cli/cmd/delete.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -22,7 +22,7 @@ import (
"huawei-csi-driver/cli/cmd/options"
)
-func init() {
+func registerDeleteCmd() {
options.NewFlagsOptions(deleteCmd).WithParent(RootCmd)
}
diff --git a/cli/cmd/delete_backend.go b/cli/cmd/delete_backend.go
index 2604ee4b..faf4d348 100644
--- a/cli/cmd/delete_backend.go
+++ b/cli/cmd/delete_backend.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -26,7 +26,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerDeleteBackendCmd() {
options.NewFlagsOptions(deleteBackendCmd).
WithNameSpace(false).
WithDeleteAll().
diff --git a/cli/cmd/delete_cert.go b/cli/cmd/delete_cert.go
index 9452b1f7..c06bfe4e 100644
--- a/cli/cmd/delete_cert.go
+++ b/cli/cmd/delete_cert.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -25,7 +25,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerDeleteCertCmd() {
options.NewFlagsOptions(deleteSecretCmd).
WithNameSpace(false).
WithBackend(true).
diff --git a/cli/cmd/get.go b/cli/cmd/get.go
index 147daa05..d8346441 100644
--- a/cli/cmd/get.go
+++ b/cli/cmd/get.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -22,7 +22,7 @@ import (
"huawei-csi-driver/cli/cmd/options"
)
-func init() {
+func registerGetCmd() {
options.NewFlagsOptions(getCmd).WithParent(RootCmd)
}
diff --git a/cli/cmd/get_backend.go b/cli/cmd/get_backend.go
index 4bee1399..9e139692 100644
--- a/cli/cmd/get_backend.go
+++ b/cli/cmd/get_backend.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -26,7 +26,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerGetBackendCmd() {
options.NewFlagsOptions(getBackendCmd).
WithNameSpace(false).
WithOutPutFormat().
diff --git a/cli/cmd/get_cert.go b/cli/cmd/get_cert.go
index 88823a3a..128176b2 100644
--- a/cli/cmd/get_cert.go
+++ b/cli/cmd/get_cert.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -25,7 +25,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerGetCertCmd() {
options.NewFlagsOptions(getSecretCmd).
WithNameSpace(false).
WithBackend(true).
diff --git a/cli/cmd/root.go b/cli/cmd/root.go
index 811616b0..9b9101e5 100644
--- a/cli/cmd/root.go
+++ b/cli/cmd/root.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-// Package command defines commands of oceanctl.
-package command
+// cmd defines commands of oceanctl.
+package cmd
import (
"path"
@@ -44,9 +44,30 @@ var RootCmd = &cobra.Command{
},
}
-func init() {
- options.NewFlagsOptions(RootCmd).
- WithLogDir()
+// Execute runs the root command
+func Execute() error {
+ registerRootCmd()
+ registerCollectCmd()
+ registerCollectLogsCmd()
+ registerCreateCmd()
+ registerCreateBackendCmd()
+ registerCreateCertCmd()
+ registerDeleteCmd()
+ registerDeleteBackendCmd()
+ registerDeleteCertCmd()
+ registerGetCmd()
+ registerGetBackendCmd()
+ registerGetCertCmd()
+ registerUpdateCmd()
+ registerUpdateBackendCmd()
+ registerUpdateCertCmd()
+ registerVersionCmd()
+
+ return RootCmd.Execute()
+}
+
+func registerRootCmd() {
+ options.NewFlagsOptions(RootCmd).WithLogDir()
}
func discoverOperating() error {
@@ -75,7 +96,7 @@ func startLogging() error {
if config.LogDir == "" {
config.LogDir = config.DefaultLogDir
}
- logRequest := &log.LoggingRequest{
+ logRequest := &log.Config{
LogName: config.DefaultLogName,
LogFileSize: config.DefaultLogSize,
LoggingModule: config.DefaultLogModule,
diff --git a/cli/cmd/update.go b/cli/cmd/update.go
index 0fda2a56..db717fd4 100644
--- a/cli/cmd/update.go
+++ b/cli/cmd/update.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -22,7 +22,7 @@ import (
"huawei-csi-driver/cli/cmd/options"
)
-func init() {
+func registerUpdateCmd() {
options.NewFlagsOptions(updateCmd).WithParent(RootCmd)
}
diff --git a/cli/cmd/update_backend.go b/cli/cmd/update_backend.go
index 1b967ede..8d77b12c 100644
--- a/cli/cmd/update_backend.go
+++ b/cli/cmd/update_backend.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -26,7 +26,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerUpdateBackendCmd() {
options.NewFlagsOptions(updateBackendCmd).
WithNameSpace(false).
WithPassword(true).
diff --git a/cli/cmd/update_cert.go b/cli/cmd/update_cert.go
index 297c9eb2..ec3badee 100644
--- a/cli/cmd/update_cert.go
+++ b/cli/cmd/update_cert.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"github.com/spf13/cobra"
@@ -25,7 +25,7 @@ import (
"huawei-csi-driver/cli/resources"
)
-func init() {
+func registerUpdateCertCmd() {
options.NewFlagsOptions(updateSecretCmd).
WithNameSpace(false).
WithFilename(true).
diff --git a/cli/cmd/version.go b/cli/cmd/version.go
index 4b8b2376..80fd3c1c 100644
--- a/cli/cmd/version.go
+++ b/cli/cmd/version.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-package command
+package cmd
import (
"fmt"
+
"github.com/spf13/cobra"
"huawei-csi-driver/cli/config"
)
-func init() {
+func registerVersionCmd() {
RootCmd.AddCommand(versionCmd)
}
diff --git a/cli/config/config.go b/cli/config/config.go
index da447397..5641acc2 100644
--- a/cli/config/config.go
+++ b/cli/config/config.go
@@ -23,7 +23,7 @@ import (
const (
// CliVersion oceanctl version
- CliVersion = "v4.4.0"
+ CliVersion = "v4.5.0"
// DefaultMaxClientThreads default max client threads
DefaultMaxClientThreads = "30"
diff --git a/cli/helper/command_helper.go b/cli/helper/command_helper.go
index a6d690d1..d6a0a319 100644
--- a/cli/helper/command_helper.go
+++ b/cli/helper/command_helper.go
@@ -106,20 +106,19 @@ func getInputString(tips string, isVisible bool) (string, error) {
}
// GetSelectedNumber get the number entered by the user
-func GetSelectedNumber(tips string, maxValue int) (int, error) {
+func GetSelectedNumber(tips string, maxValue int) (int, bool, error) {
input, err := getInputString(tips, true)
if err != nil {
- return 0, err
+ return 0, false, err
}
if strings.ToLower(input) == exitCommand {
- os.Exit(0)
- return 0, nil
+ return 0, true, nil
}
number, err := strconv.Atoi(input)
if err == nil && number > 0 && number <= maxValue {
- return number, nil
+ return number, false, nil
}
fmt.Printf("Input invalid. The valid backend number is [1-%d].\n", maxValue)
diff --git a/cli/helper/utils.go b/cli/helper/utils.go
index 8f7c4249..4191e25b 100644
--- a/cli/helper/utils.go
+++ b/cli/helper/utils.go
@@ -319,3 +319,13 @@ func ConvertInterface(i interface{}) interface{} {
}
return i
}
+
+// SplitQualifiedName Splits a fully qualified name and returns its namespace and name.
+// Assumes that the input 'str' has been validated.
+func SplitQualifiedName(str string) (string, string) {
+ parts := strings.Split(str, "/")
+ if len(parts) < 2 {
+ return "", str
+ }
+ return parts[0], parts[1]
+}
diff --git a/cli/helper/yaml_helper.go b/cli/helper/yaml_helper.go
index 2e85a54d..a73bcc62 100644
--- a/cli/helper/yaml_helper.go
+++ b/cli/helper/yaml_helper.go
@@ -19,7 +19,7 @@ package helper
import (
"encoding/json"
- "github.com/ghodss/yaml"
+ "gopkg.in/yaml.v3"
)
// StructToYAML convert struct to yaml
diff --git a/cli/main.go b/cli/main.go
index 41a55f4c..c98daf6b 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -30,7 +30,7 @@ const (
)
func main() {
- if err := command.RootCmd.Execute(); err != nil {
+ if err := cmd.Execute(); err != nil {
os.Exit(ExitCodeFailure)
}
os.Exit(ExitCodeSuccess)
diff --git a/cli/resources/backend.go b/cli/resources/backend.go
index d61e7f6b..a58d3ef5 100644
--- a/cli/resources/backend.go
+++ b/cli/resources/backend.go
@@ -19,12 +19,12 @@ package resources
import (
"errors"
"fmt"
+ "path"
"reflect"
"strconv"
"strings"
corev1 "k8s.io/api/core/v1"
- k8string "k8s.io/utils/strings"
"huawei-csi-driver/cli/client"
"huawei-csi-driver/cli/config"
@@ -142,7 +142,7 @@ func (b *Backend) Update() error {
secretClient := client.NewCommonCallHandler[corev1.Secret](config.Client)
newClaim := oldClaim.DeepCopy()
- newClaim.Spec.SecretMeta = k8string.JoinQualifiedName(newClaim.Namespace, nameWithUUid)
+ newClaim.Spec.SecretMeta = path.Join(newClaim.Namespace, nameWithUUid)
if err = storageBackendClaimClient.Update(*newClaim); err != nil {
if err := storageBackendClaimClient.Update(oldClaim); err != nil {
@@ -155,7 +155,7 @@ func (b *Backend) Update() error {
return err
}
- _, oldSecretName := k8string.SplitQualifiedName(oldClaim.Spec.SecretMeta)
+ _, oldSecretName := helper.SplitQualifiedName(oldClaim.Spec.SecretMeta)
if err := secretClient.DeleteByNames(newClaim.Namespace, oldSecretName); err != nil {
log.Errorf("delete old created secret failed, error: %v", err)
}
@@ -201,7 +201,7 @@ func fetchBackendShows(claims []xuanwuV1.StorageBackendClaim, namespace string)
if claim.Status != nil {
contentNames = append(contentNames, claim.Status.BoundContentName)
}
- _, configName := k8string.SplitQualifiedName(claim.Spec.ConfigMapMeta)
+ _, configName := helper.SplitQualifiedName(claim.Spec.ConfigMapMeta)
configmapNames = append(configmapNames, configName)
}
@@ -236,7 +236,7 @@ func buildBackendShow(claims []xuanwuV1.StorageBackendClaim, contentList []xuanw
}
}
- _, name := k8string.SplitQualifiedName(claim.Spec.ConfigMapMeta)
+ _, name := helper.SplitQualifiedName(claim.Spec.ConfigMapMeta)
if configuration, ok := config[name]; ok {
item.ShowWithConfigOption(*configuration)
}
@@ -248,11 +248,11 @@ func buildBackendShow(claims []xuanwuV1.StorageBackendClaim, contentList []xuanw
}
func deleteSbcReferenceResources(claim xuanwuV1.StorageBackendClaim) error {
- _, secretName := k8string.SplitQualifiedName(claim.Spec.SecretMeta)
- _, configmapName := k8string.SplitQualifiedName(claim.Spec.ConfigMapMeta)
- _, certSecretName := k8string.SplitQualifiedName(claim.Spec.CertSecret)
+ _, secretName := helper.SplitQualifiedName(claim.Spec.SecretMeta)
+ _, configmapName := helper.SplitQualifiedName(claim.Spec.ConfigMapMeta)
+ _, certSecretName := helper.SplitQualifiedName(claim.Spec.CertSecret)
needDeleteFinalizersResources := []string{
- k8string.JoinQualifiedName(string(client.ConfigMap), configmapName),
+ path.Join(string(client.ConfigMap), configmapName),
}
err := config.Client.DeleteFinalizersInResourceByQualifiedNames(needDeleteFinalizersResources, claim.Namespace)
@@ -261,12 +261,12 @@ func deleteSbcReferenceResources(claim xuanwuV1.StorageBackendClaim) error {
}
referenceResources := append(needDeleteFinalizersResources,
- k8string.JoinQualifiedName(string(client.Secret), secretName),
- k8string.JoinQualifiedName(string(client.Storagebackendclaim), claim.Name))
+ path.Join(string(client.Secret), secretName),
+ path.Join(string(client.Storagebackendclaim), claim.Name))
if certSecretName != "" {
referenceResources = append(referenceResources,
- k8string.JoinQualifiedName(string(client.Secret), certSecretName))
+ path.Join(string(client.Secret), certSecretName))
}
_, err = config.Client.DeleteResourceByQualifiedNames(referenceResources, claim.Namespace)
@@ -329,6 +329,10 @@ func (b *Backend) Create() error {
break
}
+ if selectedBackend == nil {
+ return nil
+ }
+
if selectedBackend.Configured {
fmt.Printf("backend [%s] has been Configured, please select another\n", selectedBackend.Name)
continue
@@ -353,7 +357,7 @@ func FetchConfiguredBackends(namespace string) (map[string]*BackendConfiguration
}
configuredBackend := helper.MapTo(sbcList, func(claim xuanwuV1.StorageBackendClaim) string {
- _, name := k8string.SplitQualifiedName(claim.Spec.ConfigMapMeta)
+ _, name := helper.SplitQualifiedName(claim.Spec.ConfigMapMeta)
return name
})
@@ -433,13 +437,17 @@ func ConfigOneBackend(backendConfig *BackendConfiguration) error {
func selectOneBackend(backendList []*BackendConfiguration) (*BackendConfiguration, error) {
printBackendsStatusTable(backendList)
- number, err := helper.GetSelectedNumber("Please enter the backend number to configure "+
+ number, isExit, err := helper.GetSelectedNumber("Please enter the backend number to configure "+
"(Enter 'exit' to exit):", len(backendList))
if err != nil {
log.Errorf("failed to get backend number entered by user. %v", err)
return nil, err
}
+ if isExit {
+ return nil, nil
+ }
+
if number > len(backendList) {
number = len(backendList)
}
diff --git a/cli/resources/backend_helper.go b/cli/resources/backend_helper.go
index 07674d97..1c097100 100644
--- a/cli/resources/backend_helper.go
+++ b/cli/resources/backend_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -22,14 +22,14 @@ import (
"errors"
"io"
"os"
+ "path"
"reflect"
"strconv"
"strings"
- "gopkg.in/yaml.v2"
+ "gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- k8string "k8s.io/utils/strings"
"huawei-csi-driver/cli/helper"
xuanwuv1 "huawei-csi-driver/client/apis/xuanwu/v1"
@@ -181,8 +181,8 @@ func (b *BackendConfiguration) ToStorageBackendClaimConfig() StorageBackendClaim
return StorageBackendClaimConfig{
Name: b.Name,
Namespace: b.NameSpace,
- ConfigmapMeta: k8string.JoinQualifiedName(b.NameSpace, b.Name),
- SecretMeta: k8string.JoinQualifiedName(b.NameSpace, b.Name),
+ ConfigmapMeta: path.Join(b.NameSpace, b.Name),
+ SecretMeta: path.Join(b.NameSpace, b.Name),
MaxClientThreads: b.MaxClientThreads,
Provisioner: b.Provisioner,
}
@@ -192,7 +192,7 @@ func (b *BackendConfiguration) ToStorageBackendClaimConfig() StorageBackendClaim
func (b *BackendConfiguration) ToConfigMapConfig() (ConfigMapConfig, error) {
config := struct {
Backends BackendConfiguration `json:"backends"`
- }{*b}
+ }{Backends: *b}
config.Backends.Parameters.Portals = helper.ConvertInterface(config.Backends.Parameters.Portals)
diff --git a/cli/resources/cert.go b/cli/resources/cert.go
index 7b9005f6..bdeeee10 100644
--- a/cli/resources/cert.go
+++ b/cli/resources/cert.go
@@ -19,9 +19,9 @@ package resources
import (
"fmt"
"os"
+ "path"
corev1 "k8s.io/api/core/v1"
- k8string "k8s.io/utils/strings"
"huawei-csi-driver/cli/client"
"huawei-csi-driver/cli/config"
@@ -54,7 +54,7 @@ func (c *Cert) Get() error {
return nil
}
- _, certSecretName := k8string.SplitQualifiedName(claim.Spec.CertSecret)
+ _, certSecretName := helper.SplitQualifiedName(claim.Spec.CertSecret)
if certSecretName == "" {
helper.PrintNoResourceCert(claim.Name, claim.Namespace)
@@ -85,7 +85,7 @@ func (c *Cert) Delete() error {
return nil
}
- _, certSecretName := k8string.SplitQualifiedName(oldClaim.Spec.CertSecret)
+ _, certSecretName := helper.SplitQualifiedName(oldClaim.Spec.CertSecret)
if certSecretName == "" {
helper.PrintNoResourceCert(oldClaim.Name, oldClaim.Namespace)
@@ -126,7 +126,7 @@ func (c *Cert) Update() error {
return nil
}
- _, certSecretName := k8string.SplitQualifiedName(claim.Spec.CertSecret)
+ _, certSecretName := helper.SplitQualifiedName(claim.Spec.CertSecret)
if certSecretName == "" {
helper.PrintNoResourceCert(claim.Name, claim.Namespace)
@@ -187,7 +187,7 @@ func (c *Cert) Create() error {
newClaim := oldClaim.DeepCopy()
newClaim.Spec.UseCert = true
- newClaim.Spec.CertSecret = k8string.JoinQualifiedName(newClaim.Namespace, certConfig.Name)
+ newClaim.Spec.CertSecret = path.Join(newClaim.Namespace, certConfig.Name)
if err = storageBackendClaimClient.Update(*newClaim); err != nil {
if err := secretClient.DeleteByNames(newClaim.Namespace, certConfig.Name); err != nil {
@@ -221,7 +221,7 @@ func (c *Cert) LoadCertsFromDate(Data []byte) (*CertConfig, error) {
func deleteSecretResources(secret corev1.Secret) error {
referenceResources := []string{
- k8string.JoinQualifiedName(string(client.Secret), secret.Name),
+ path.Join(string(client.Secret), secret.Name),
}
_, err := config.Client.DeleteResourceByQualifiedNames(referenceResources, secret.Namespace)
diff --git a/cli/resources/logs.go b/cli/resources/logs.go
index 27ff2f69..3b0dedbe 100644
--- a/cli/resources/logs.go
+++ b/cli/resources/logs.go
@@ -49,6 +49,7 @@ const (
maxTransmissionsNum = 10
maxNodeGoroutineLimit = 1000
+ zipFilePermission = 0600
)
var (
@@ -252,7 +253,7 @@ func zipMultiFiles(zipPath string, filePaths ...string) error {
if err := os.MkdirAll(filepath.Dir(zipPath), os.ModePerm); err != nil {
return helper.LogErrorf("create compressed logs directory failed, error: %v", err)
}
- archive, err := os.OpenFile(zipPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
+ archive, err := os.OpenFile(zipPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, zipFilePermission)
if err != nil {
return helper.LogErrorf("create compressed logs file failed, error: %v", err)
}
diff --git a/cli/resources/logs_helper.go b/cli/resources/logs_helper.go
index 922453d6..e781845a 100644
--- a/cli/resources/logs_helper.go
+++ b/cli/resources/logs_helper.go
@@ -41,10 +41,18 @@ const (
progressBarLength = 100
consoleLogDirectory = "console"
+
+ logFilePermission = 0600
+ colorHexCode = 0x1B
+ displayPeriod = 10 * time.Millisecond
)
var (
- identifyPodTypesFunc = make(map[PodType]func(pod *coreV1.Pod) bool)
+ identifyPodTypesFunc = map[PodType]func(pod *coreV1.Pod) bool{
+ CSI: checkCSIPod,
+ CSM: checkCSMPod,
+ Xuanwu: checkXuanwuPod,
+ }
xuanwuPodPrefixNameList = []string{"xuanwu-backup-mngt", "xuanwu-backup-service", "xuanwu-base-mngt",
"xuanwu-disaster-service", "xuanwu-disaster-mngt", "xuanwu-metadata-service", "xuanwu-volume-service"}
)
@@ -91,12 +99,6 @@ type TransmitTask struct {
FileLogsCollect
}
-func init() {
- RegisterIdentifyPodTypeFunc(CSI, checkCSIPod)
- RegisterIdentifyPodTypeFunc(CSM, checkCSMPod)
- RegisterIdentifyPodTypeFunc(Xuanwu, checkXuanwuPod)
-}
-
// Do copy the compressed log file to the local host.
func (t *TransmitTask) Do() {
_ = t.CopyToLocal(t.namespace, t.nodeName, t.podName, t.containerName)
@@ -129,7 +131,9 @@ func (d *Display) Add(prefixDesc string, f func()) {
func (d *Display) show() {
for idx, display := range d.displayFunc {
- fmt.Printf("%s", d.prefixDesc[idx])
+ if idx < len(d.prefixDesc) {
+ fmt.Printf("%s", d.prefixDesc[idx])
+ }
display()
}
}
@@ -160,21 +164,23 @@ func (d *Display) Show(ctx context.Context) {
default:
d.resetCursor()
d.show()
- time.Sleep(10 * time.Millisecond)
+ time.Sleep(displayPeriod)
}
}
}
func (n *Status) getPercent() int {
- return int(atomic.LoadInt32(&n.completed)) * 100 / n.total
+ return int(atomic.LoadInt32(&n.completed)) * progressBarLength / n.total
}
func (n *Status) getCompletedStr() string {
- return fmt.Sprintf("%c[1;40;32m%s%c[0m", 0x1B, strings.Repeat("+", n.getPercent()*progressBarLength/100), 0x1B)
+ return fmt.Sprintf("%c[1;40;32m%s%c[0m", colorHexCode,
+ strings.Repeat("+", n.getPercent()*progressBarLength/progressBarLength), colorHexCode)
}
func (n *Status) getRemainedStr() string {
- return fmt.Sprintf("%s", strings.Repeat("-", progressBarLength-n.getPercent()*progressBarLength/100))
+ return fmt.Sprintf("%s", strings.Repeat("-",
+ progressBarLength-n.getPercent()*progressBarLength/progressBarLength))
}
// Display displays the pod log collection status of the current node.
@@ -270,16 +276,16 @@ func (n *NodeLogCollector) collectPodLogs(pod *coreV1.Pod, onceIdx int) {
msg := fmt.Sprintf("Failed to collect [%s] file logs on node [%s], please collect logs manually,"+
" file logs path is [%s]", pod.Name, pod.Spec.NodeName, logPath)
n.display.Add("", func() {
- fmt.Printf("%c[1;40;31m%s%c[0m\n", 0x1B, msg, 0x1B)
+ fmt.Printf("%c[1;40;31m%s%c[0m\n", colorHexCode, msg, colorHexCode)
})
_ = helper.LogWarningf(ctx, "error: %v", errors.New(msg))
}
for idx := range pod.Spec.Containers {
container := &pod.Spec.Containers[idx]
- getConsoleLogs(ctx, pod.Namespace, container.Name, pod.Name, pod.Spec.NodeName, false)
- getConsoleLogs(ctx, pod.Namespace, container.Name, pod.Name, pod.Spec.NodeName, true)
- if isRunning {
+ getConsoleLogs(ctx, getLogArgs(pod.Namespace, container.Name, pod.Name, pod.Spec.NodeName, false))
+ getConsoleLogs(ctx, getLogArgs(pod.Namespace, container.Name, pod.Name, pod.Spec.NodeName, true))
+ if isRunning && onceIdx < len(n.fileLogsOnce) {
n.fileLogsOnce[onceIdx].Do(func() error {
fileLogPath, err := getContainerFileLogPaths(container)
if err != nil {
@@ -316,12 +322,31 @@ func (n *NodeLogCollector) isCollected(fileLogPath string) bool {
return false
}
-func getConsoleLogs(ctx context.Context, namespace, containerName, podName, nodeName string, isHistoryLogs bool) {
- logs, err := config.Client.GetConsoleLogs(ctx, namespace, containerName, isHistoryLogs, podName)
+type consoleLogArgs struct {
+ namespace string
+ containerName string
+ podName string
+ nodeName string
+ isHistoryLogs bool
+}
+
+func getLogArgs(namespace, containerName, podName, nodeName string, isHistoryLogs bool) consoleLogArgs {
+ return consoleLogArgs{
+ namespace: namespace,
+ containerName: containerName,
+ podName: podName,
+ nodeName: nodeName,
+ isHistoryLogs: isHistoryLogs,
+ }
+}
+
+func getConsoleLogs(ctx context.Context, logArgs consoleLogArgs) {
+ logs, err := config.Client.GetConsoleLogs(ctx,
+ logArgs.namespace, logArgs.containerName, logArgs.isHistoryLogs, logArgs.podName)
if err != nil {
_ = helper.LogWarningf(ctx, "get container console logs failed, error: %v", err)
} else {
- err = saveConsoleLog(logs, namespace, podName, containerName, nodeName, isHistoryLogs)
+ err = saveConsoleLog(logs, logArgs)
if err != nil {
log.Errorf("save console log failed, error: %v", err)
}
@@ -337,19 +362,19 @@ func getPodType(pod *coreV1.Pod) PodType {
return UnKnow
}
-func saveConsoleLog(logs []byte, namespace, podName, containerName, nodeName string, isHistoryLogs bool) error {
- ctx := context.WithValue(context.Background(), "tag", podName)
- fileName := fmt.Sprintf("%s-%s-%s.log", namespace, podName, containerName)
- if isHistoryLogs {
+func saveConsoleLog(logs []byte, logArgs consoleLogArgs) error {
+ ctx := context.WithValue(context.Background(), "tag", logArgs.podName)
+ fileName := fmt.Sprintf("%s-%s-%s.log", logArgs.namespace, logArgs.podName, logArgs.containerName)
+ if logArgs.isHistoryLogs {
fileName = "last-" + fileName
}
- file, err := os.Create(path.Join(localLogsPrefixPath, nodeName, consoleLogDirectory, fileName))
+ file, err := os.Create(path.Join(localLogsPrefixPath, logArgs.nodeName, consoleLogDirectory, fileName))
if err != nil {
return helper.LogWarningf(ctx, "create container console log file failed, error: %v", err)
}
defer file.Close()
- err = file.Chmod(0600)
+ err = file.Chmod(logFilePermission)
if err != nil {
return helper.LogWarningf(ctx, "set the file permission failed, error: %v", err)
}
@@ -378,10 +403,11 @@ func getContainerFileLogPaths(container *coreV1.Container) (string, error) {
return "", helper.LogWarningf(context.Background(), "get container file log paths failed, error: %v",
errors.New("args is nil"))
}
+ const logPathSplitSegment = 2
for _, arg := range container.Args {
if strings.HasPrefix(arg, "--log-file-dir=") {
logPath := strings.Split(arg, "=")
- if len(logPath) != 2 {
+ if len(logPath) != logPathSplitSegment {
return "", helper.LogWarningf(context.Background(), "get container file log paths failed, error: %v",
errors.New("log-file-dir is not set correctly"))
}
diff --git a/cli/resources/logs_helper_test.go b/cli/resources/logs_helper_test.go
index 9d470b4e..ebdeca8d 100644
--- a/cli/resources/logs_helper_test.go
+++ b/cli/resources/logs_helper_test.go
@@ -49,7 +49,7 @@ func Test_saveConsoleLog_Success(t *testing.T) {
})
// act
- gotErr := saveConsoleLog(logs, namespace, podName, containerName, nodeName, true)
+ gotErr := saveConsoleLog(logs, getLogArgs(namespace, containerName, podName, nodeName, true))
// assert
if gotErr != nil {
@@ -79,7 +79,7 @@ func Test_saveConsoleLog_CreateFileFail(t *testing.T) {
})
// act
- gotErr := saveConsoleLog(logs, namespace, podName, containerName, nodeName, true)
+ gotErr := saveConsoleLog(logs, getLogArgs(namespace, containerName, podName, nodeName, true))
// assert
if !reflect.DeepEqual(gotErr, wantErr) {
@@ -114,7 +114,7 @@ func Test_saveConsoleLog_ChmodFileFail(t *testing.T) {
})
// act
- gotErr := saveConsoleLog(logs, namespace, podName, containerName, nodeName, true)
+ gotErr := saveConsoleLog(logs, getLogArgs(namespace, containerName, podName, nodeName, true))
// assert
if !reflect.DeepEqual(gotErr, wantErr) {
@@ -151,7 +151,7 @@ func Test_saveConsoleLog_WriteFileFail(t *testing.T) {
})
// act
- gotErr := saveConsoleLog(logs, namespace, podName, containerName, nodeName, true)
+ gotErr := saveConsoleLog(logs, getLogArgs(namespace, containerName, podName, nodeName, true))
// assert
if !reflect.DeepEqual(gotErr, wantErr) {
diff --git a/cli/resources/resource.go b/cli/resources/resource.go
index 26116a77..b20534bf 100644
--- a/cli/resources/resource.go
+++ b/cli/resources/resource.go
@@ -68,7 +68,7 @@ func NewResourceBuilder() *ResourceBuilder {
// Build convert ResourceBuilder to Resource
func (b *ResourceBuilder) Build() *Resource {
- return &Resource{b}
+ return &Resource{ResourceBuilder: b}
}
// NamespaceParam accepts the namespace that these resources should be
diff --git a/cli/resources/validate.go b/cli/resources/validate.go
index 75fbea0b..d94e3d5b 100644
--- a/cli/resources/validate.go
+++ b/cli/resources/validate.go
@@ -21,9 +21,8 @@ import (
"fmt"
"strings"
- "k8s.io/utils/strings/slices"
-
"huawei-csi-driver/cli/config"
+ "huawei-csi-driver/utils"
)
// Validator is used to validate a Resource object
@@ -99,7 +98,7 @@ func (b *ValidatorBuilder) ValidateOutputFormat() *ValidatorBuilder {
if b.resource.output == "" {
return b
}
- if !slices.Contains(config.SupportedFormats, b.resource.output) {
+ if !utils.Contains(config.SupportedFormats, b.resource.output) {
b.errs = append(b.errs, fmt.Errorf("unable to match a printer suitable for the output format %s, "+
"allowed formats are: %v", b.resource.output, strings.Join(config.SupportedFormats, ", ")))
}
diff --git a/client/apis/xuanwu/v1/register.go b/client/apis/xuanwu/v1/register.go
index 27700b8c..ba167749 100644
--- a/client/apis/xuanwu/v1/register.go
+++ b/client/apis/xuanwu/v1/register.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -51,8 +51,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&StorageBackendClaimList{},
&StorageBackendContent{},
&StorageBackendContentList{},
- &ResourceTopology{},
- &ResourceTopologyList{},
&VolumeModifyClaim{},
&VolumeModifyClaimList{},
&VolumeModifyContent{},
diff --git a/client/apis/xuanwu/v1/volumemodifycontent.go b/client/apis/xuanwu/v1/volumemodifycontent.go
index 1b262691..bdc22621 100644
--- a/client/apis/xuanwu/v1/volumemodifycontent.go
+++ b/client/apis/xuanwu/v1/volumemodifycontent.go
@@ -72,7 +72,7 @@ type VolumeModifyContentStatus struct {
type VolumeModifyContentPhase string
const (
- // VolumeModifyContentPending means the VolumeModifyContent has been accepted.
+ // VolumeModifyContentPending means the VolumeModifyContent has been accepted,
// but modify didn't start.
VolumeModifyContentPending VolumeModifyContentPhase = "Pending"
diff --git a/client/apis/xuanwu/v1/zz_generated.deepcopy.go b/client/apis/xuanwu/v1/zz_generated.deepcopy.go
index a002672d..8281e16e 100644
--- a/client/apis/xuanwu/v1/zz_generated.deepcopy.go
+++ b/client/apis/xuanwu/v1/zz_generated.deepcopy.go
@@ -2,7 +2,7 @@
// +build !ignore_autogenerated
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -61,126 +61,6 @@ func (in *Pool) DeepCopy() *Pool {
return out
}
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ResourceInfo) DeepCopyInto(out *ResourceInfo) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceInfo.
-func (in *ResourceInfo) DeepCopy() *ResourceInfo {
- if in == nil {
- return nil
- }
- out := new(ResourceInfo)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ResourceTopology) DeepCopyInto(out *ResourceTopology) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
- in.Spec.DeepCopyInto(&out.Spec)
- in.Status.DeepCopyInto(&out.Status)
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceTopology.
-func (in *ResourceTopology) DeepCopy() *ResourceTopology {
- if in == nil {
- return nil
- }
- out := new(ResourceTopology)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *ResourceTopology) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ResourceTopologyList) DeepCopyInto(out *ResourceTopologyList) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ListMeta.DeepCopyInto(&out.ListMeta)
- if in.Items != nil {
- in, out := &in.Items, &out.Items
- *out = make([]ResourceTopology, len(*in))
- for i := range *in {
- (*in)[i].DeepCopyInto(&(*out)[i])
- }
- }
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceTopologyList.
-func (in *ResourceTopologyList) DeepCopy() *ResourceTopologyList {
- if in == nil {
- return nil
- }
- out := new(ResourceTopologyList)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *ResourceTopologyList) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ResourceTopologySpec) DeepCopyInto(out *ResourceTopologySpec) {
- *out = *in
- if in.Tags != nil {
- in, out := &in.Tags, &out.Tags
- *out = make([]Tag, len(*in))
- copy(*out, *in)
- }
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceTopologySpec.
-func (in *ResourceTopologySpec) DeepCopy() *ResourceTopologySpec {
- if in == nil {
- return nil
- }
- out := new(ResourceTopologySpec)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ResourceTopologyStatus) DeepCopyInto(out *ResourceTopologyStatus) {
- *out = *in
- if in.Tags != nil {
- in, out := &in.Tags, &out.Tags
- *out = make([]Tag, len(*in))
- copy(*out, *in)
- }
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceTopologyStatus.
-func (in *ResourceTopologyStatus) DeepCopy() *ResourceTopologyStatus {
- if in == nil {
- return nil
- }
- out := new(ResourceTopologyStatus)
- in.DeepCopyInto(out)
- return out
-}
-
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StorageBackendClaim) DeepCopyInto(out *StorageBackendClaim) {
*out = *in
@@ -417,24 +297,6 @@ func (in *StorageBackendContentStatus) DeepCopy() *StorageBackendContentStatus {
return out
}
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *Tag) DeepCopyInto(out *Tag) {
- *out = *in
- out.ResourceInfo = in.ResourceInfo
- out.Owner = in.Owner
- return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tag.
-func (in *Tag) DeepCopy() *Tag {
- if in == nil {
- return nil
- }
- out := new(Tag)
- in.DeepCopyInto(out)
- return out
-}
-
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeModifyClaim) DeepCopyInto(out *VolumeModifyClaim) {
*out = *in
diff --git a/cmd/huawei-csi-extender/main.go b/cmd/huawei-csi-extender/main.go
index b39ee2b0..d1b91ffd 100644
--- a/cmd/huawei-csi-extender/main.go
+++ b/cmd/huawei-csi-extender/main.go
@@ -49,7 +49,7 @@ func main() {
if err := app.NewCommand().Execute(); err != nil {
logrus.Fatalf("Execute app command failed. error: %v", err)
}
- err := log.InitLogging(&log.LoggingRequest{
+ err := log.InitLogging(&log.Config{
LogName: containerName,
LogFileSize: app.GetGlobalConfig().LogFileSize,
LoggingModule: app.GetGlobalConfig().LoggingModule,
diff --git a/cmd/storage-backend-controller/main.go b/cmd/storage-backend-controller/main.go
index 37d3f75d..199c2255 100644
--- a/cmd/storage-backend-controller/main.go
+++ b/cmd/storage-backend-controller/main.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -45,13 +45,17 @@ const (
containerName = "storage-backend-controller"
eventComponentName = "XuanWu-StorageBackend-Mngt"
leaderLockObjectName = "storage-backend-controller"
+
+ backoffDuration = 100 * time.Millisecond
+ backoffFactor = 1.5
+ backoffSteps = 10
)
func main() {
if err := app.NewCommand().Execute(); err != nil {
logrus.Fatalf("Execute app command failed. error: %v", err)
}
- err := log.InitLogging(&log.LoggingRequest{
+ err := log.InitLogging(&log.Config{
LogName: containerName,
LogFileSize: app.GetGlobalConfig().LogFileSize,
LoggingModule: app.GetGlobalConfig().LoggingModule,
@@ -169,9 +173,9 @@ func ensureCRDExist(ctx context.Context, client *clientSet.Clientset) error {
}
backoff := wait.Backoff{
- Duration: 100 * time.Millisecond,
- Factor: 1.5,
- Steps: 10,
+ Duration: backoffDuration,
+ Factor: backoffFactor,
+ Steps: backoffSteps,
}
if err := wait.ExponentialBackoff(backoff, exist); err != nil {
return err
diff --git a/cmd/storage-backend-sidecar/main.go b/cmd/storage-backend-sidecar/main.go
index d96054d9..fe567598 100644
--- a/cmd/storage-backend-sidecar/main.go
+++ b/cmd/storage-backend-sidecar/main.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -50,6 +50,7 @@ const (
eventComponentName = "XuanWu-StorageBackend-Mngt"
leaderLockObjectName = "sb-sidecar-"
+ backoffDuration = 100 * time.Millisecond
)
var (
@@ -62,7 +63,7 @@ func main() {
logrus.Fatalf("Execute app command failed. error: %v", err)
}
- err := log.InitLogging(&log.LoggingRequest{
+ err := log.InitLogging(&log.Config{
LogName: containerName,
LogFileSize: app.GetGlobalConfig().LogFileSize,
LoggingModule: app.GetGlobalConfig().LoggingModule,
@@ -200,7 +201,7 @@ func ensureCRDExist(ctx context.Context, client *clientSet.Clientset) error {
}
backoff := wait.Backoff{
- Duration: 100 * time.Millisecond,
+ Duration: backoffDuration,
Factor: 1.5,
Steps: 10,
}
diff --git a/connector/connector.go b/connector/connector.go
index 0844add4..61a001da 100644
--- a/connector/connector.go
+++ b/connector/connector.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -82,11 +82,11 @@ const (
var (
// connectors is the global map
- connectors = map[string]Connector{}
+ connectors = map[string]VolumeConnector{}
)
-// Connector defines the behavior that the connector should have
-type Connector interface {
+// VolumeConnector defines the behavior that the connector should have
+type VolumeConnector interface {
// ConnectVolume to mount the source to target path, the source path can be block or nfs
// Example:
// mount /dev/sdb /
@@ -98,18 +98,18 @@ type Connector interface {
// DisConnectInfo defines the fields of disconnect volume
type DisConnectInfo struct {
- Conn Connector
+ Conn VolumeConnector
TgtLun string
}
// ConnectInfo defines the fields of connect volume
type ConnectInfo struct {
- Conn Connector
+ Conn VolumeConnector
MappingInfo map[string]interface{}
}
// GetConnector can get a connector by its type from the global connector map
-func GetConnector(ctx context.Context, cType string) Connector {
+func GetConnector(ctx context.Context, cType string) VolumeConnector {
if cnt, exist := connectors[cType]; exist {
return cnt
}
@@ -118,8 +118,8 @@ func GetConnector(ctx context.Context, cType string) Connector {
return nil
}
-// RegisterConnector is used to register the specific Connector to the global connector map
-func RegisterConnector(cType string, cnt Connector) error {
+// RegisterConnector is used to register the specific VolumeConnector to the global connector map
+func RegisterConnector(cType string, cnt VolumeConnector) error {
if _, exist := connectors[cType]; exist {
return fmt.Errorf("connector %s already exists", cType)
}
diff --git a/connector/connector_test.go b/connector/connector_test.go
index 86c37820..9da3a6df 100644
--- a/connector/connector_test.go
+++ b/connector/connector_test.go
@@ -43,18 +43,18 @@ func (s *stubConnector) DisConnectVolume(ctx context.Context, tgtLunWWN string)
return nil
}
-var testConnector Connector = &stubConnector{}
+var testConnector VolumeConnector = &stubConnector{}
func TestRegisterConnector(t *testing.T) {
defer func() {
- connectors = map[string]Connector{}
+ connectors = map[string]VolumeConnector{}
}()
connectors["fibreChannel"] = testConnector
type args struct {
cType string
- cnt Connector
+ cnt VolumeConnector
}
tests := []struct {
name string
@@ -83,7 +83,7 @@ func TestGetConnector(t *testing.T) {
tests := []struct {
name string
args args
- want Connector
+ want VolumeConnector
}{
{"NoExist", args{context.Background(), FCDriver}, nil},
{"Existed", args{context.Background(), ISCSIDriver}, testConnector},
diff --git a/connector/connector_utils.go b/connector/connector_utils.go
index 5420520b..b962c35f 100644
--- a/connector/connector_utils.go
+++ b/connector/connector_utils.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -33,6 +33,7 @@ import (
"time"
"huawei-csi-driver/csi/app"
+ "huawei-csi-driver/pkg/constants"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
@@ -50,6 +51,12 @@ const (
maxListTries = 10
// Location of the mount file to use
procMountsPath = "/proc/mounts"
+
+ extendDMBlockWaitTime = 2 * time.Second
+ watchDeviceInterval = 100 * time.Millisecond
+ volumeRemovalRetryTimes = 30
+
+ devLineSplitSegment = 2
)
var (
@@ -93,7 +100,7 @@ func getDevice(findDeviceMap map[string]string, deviceLink string) string {
devLines := strings.Split(deviceLink, "\n")
for _, line := range devLines {
splits := strings.Split(line, "../../")
- if len(splits) >= 2 {
+ if len(splits) >= devLineSplitSegment {
name := splits[1]
if strings.HasPrefix(name, "dm") {
@@ -121,7 +128,7 @@ func getDevices(deviceLink string) []string {
devLines := strings.Split(deviceLink, "\n")
for _, line := range devLines {
splits := strings.Split(line, "../../")
- if len(splits) < 2 || utils.IsContain(splits[1], devices) {
+ if len(splits) < devLineSplitSegment || utils.IsContain(splits[1], devices) {
continue
}
@@ -371,7 +378,7 @@ func removeSCSIDevice(ctx context.Context, sd string) error {
func waitVolumeRemoval(ctx context.Context, devPaths []string) {
existPath := devPaths
- for index := 0; index <= 30; index++ {
+ for index := 0; index <= volumeRemovalRetryTimes; index++ {
var exist []string
for _, dev := range existPath {
_, err := os.Stat(dev)
@@ -387,7 +394,7 @@ func waitVolumeRemoval(ctx context.Context, devPaths []string) {
return
}
- if index < 30 {
+ if index < volumeRemovalRetryTimes {
time.Sleep(time.Second)
}
}
@@ -458,7 +465,7 @@ func WatchDMDevice(ctx context.Context, lunWWN string, expectPathNumber int) (DM
case <-timeout:
return dm, err
default:
- time.Sleep(100 * time.Millisecond)
+ time.Sleep(watchDeviceInterval)
}
dm, err = findDMDeviceByWWN(ctx, lunWWN)
@@ -767,7 +774,7 @@ func ResizeBlock(ctx context.Context, tgtLunWWN string, requiredBytes int64) err
return utils.WaitUntil(func() (bool, error) {
curSize := showDeviceSize(ctx, virtualDevice)
- if curSize != "" && strconv.FormatInt(requiredBytes, 10) == curSize {
+ if curSize != "" && strconv.FormatInt(requiredBytes, constants.DefaultIntBase) == curSize {
return true, nil
}
return false, nil
@@ -1028,7 +1035,7 @@ func extendDMBlock(ctx context.Context, device string) error {
}
log.AddContext(ctx).Infof("Original size of block %s is %s", device, oldSize)
- time.Sleep(time.Second * 2)
+ time.Sleep(extendDMBlockWaitTime)
result, err := multiPathResizeMap(ctx, device)
if err != nil || strings.Contains(result, "fail") {
msg := fmt.Sprintf("Resize device %s err, output: %s, err: %v", device, result, err)
@@ -1145,9 +1152,10 @@ func findMultiPathWWN(ctx context.Context, mPath string) (string, error) {
return "", err
}
+ const pathMapsLen = 3
for _, out := range strings.Split(output, "\n") {
pathMaps := strings.Fields(out)
- if len(pathMaps) == 3 && pathMaps[1] == mPath {
+ if len(pathMaps) == pathMapsLen && pathMaps[1] == mPath {
return pathMaps[2], nil
}
}
@@ -1412,7 +1420,7 @@ var GetDeviceSize = func(ctx context.Context, hostDevice string) (int64, error)
if line == "" {
continue
}
- size, err := strconv.ParseInt(line, 10, 64)
+ size, err := strconv.ParseInt(line, constants.DefaultIntBase, constants.DefaultIntBitSize)
if err != nil {
log.AddContext(ctx).Errorf("Failed to get device size %s, err is %v", line, err)
return 0, err
@@ -1576,7 +1584,7 @@ var RemoveAllDevice = func(ctx context.Context,
}
// ClearResidualPath used to clear residual path
-func ClearResidualPath(ctx context.Context, lunWWN string, volumeMode interface{}) error {
+func ClearResidualPath(ctx context.Context, lunWWN string, volumeMode any, multiPathType string) error {
log.AddContext(ctx).Infof("Enter func: ClearResidualPath. lunWWN:[%s]. volumeMode:[%v]", lunWWN, volumeMode)
v, ok := volumeMode.(string)
@@ -1585,6 +1593,10 @@ func ClearResidualPath(ctx context.Context, lunWWN string, volumeMode interface{
return nil
}
+ if err := clearUltraPathResidualPathByWwn(ctx, multiPathType, lunWWN); err != nil {
+ return err
+ }
+
devInfos, err := getDevicesInfosByGUID(ctx, lunWWN)
if err != nil {
return err
@@ -1598,6 +1610,25 @@ func ClearResidualPath(ctx context.Context, lunWWN string, volumeMode interface{
return clearResidualPath(ctx, devInfos)
}
+func clearUltraPathResidualPathByWwn(ctx context.Context, multiPathType, lunWWN string) error {
+ if multiPathType != HWUltraPath {
+ return nil
+ }
+
+ log.AddContext(ctx).Infoln("start to clear ultrapath specific residual paths by device WWN.")
+ vLun, err := GetUltrapathVLunByWWN(ctx, UltraPathCommand, lunWWN)
+ if err != nil {
+ return err
+ }
+
+ if vLun == nil {
+ log.AddContext(ctx).Infoln("no ultrapath specific residual path to clear.")
+ return nil
+ }
+
+ return vLun.CleanResidualPath(ctx)
+}
+
func isPartitionDevice(ctx context.Context, dev string) (bool, error) {
if strings.HasPrefix(dev, "dm") {
// dm-* should convert to mpath* to determine whether it is a partition disk.
@@ -1873,11 +1904,12 @@ func ReadMountPoints(ctx context.Context) (map[string]string, error) {
return nil, err
}
+ const splitLength = 2
mountMap := make(map[string]string)
for _, line := range strings.Split(string(data), "\n") {
if strings.TrimSpace(line) != "" {
splitValue := strings.Split(line, " ")
- if len(splitValue) >= 2 && splitValue[0] != "#" {
+ if len(splitValue) >= splitLength && splitValue[0] != "#" {
mountMap[splitValue[1]] = splitValue[0]
}
}
diff --git a/connector/fibrechannel/fc.go b/connector/fibrechannel/fc.go
index 6799fdea..0438a511 100644
--- a/connector/fibrechannel/fc.go
+++ b/connector/fibrechannel/fc.go
@@ -24,19 +24,20 @@ import (
"huawei-csi-driver/utils/log"
)
-// FibreChannel implements the Connector interface for FC protocol
-type FibreChannel struct {
+// Connector implements the connector.VolumeConnector for FC protocol
+type Connector struct {
}
func init() {
- connector.RegisterConnector(connector.FCDriver, &FibreChannel{})
+ connector.RegisterConnector(connector.FCDriver, &Connector{})
}
// ConnectVolume to mount the source to target path, the source path can be block or nfs
// Example:
-// mount /dev/sdb /
-// mount /
-func (fc *FibreChannel) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
+//
+// mount /dev/sdb /
+// mount /
+func (fc *Connector) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
log.AddContext(ctx).Infof("FC Start to connect volume ==> connect info: %v", conn)
tgtLunWWN, exist := conn["tgtLunWWN"].(string)
if !exist {
@@ -46,7 +47,7 @@ func (fc *FibreChannel) ConnectVolume(ctx context.Context, conn map[string]inter
}
// DisConnectVolume to unmount the target path
-func (fc *FibreChannel) DisConnectVolume(ctx context.Context, tgtLunWWN string) error {
+func (fc *Connector) DisConnectVolume(ctx context.Context, tgtLunWWN string) error {
log.AddContext(ctx).Infof("FC Start to disconnect volume ==> volume wwn is: %v", tgtLunWWN)
return connector.DisConnectVolumeCommon(ctx, tgtLunWWN, connector.FCDriver, tryDisConnectVolume)
}
diff --git a/connector/fibrechannel/fc_helper.go b/connector/fibrechannel/fc_helper.go
index 162c9f8c..361d9043 100644
--- a/connector/fibrechannel/fc_helper.go
+++ b/connector/fibrechannel/fc_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -33,6 +33,18 @@ import (
"huawei-csi-driver/utils/log"
)
+const (
+ channelTargetLunLength = 2
+ hostDeviceLength = 4
+ waitDeviceDiscoveryTimeout = 60 * time.Second
+ waitDeviceDiscoveryInterval = 2 * time.Second
+ lunIdHighBits = 16
+ lunIdAndOperationBits = 0xffff
+ wholeLunIdLength = 256
+ devPathLength = 3
+ attrWwnIndex = 2
+)
+
type target struct {
tgtWWN string
tgtHostLun string
@@ -106,7 +118,11 @@ func constructFCInfo(conn *connectorInfo) {
if index >= len(conn.tgtHostLUNs) {
continue
}
- conn.tgtTargets = append(conn.tgtTargets, target{conn.tgtWWNs[index], conn.tgtHostLUNs[index]})
+ tgt := target{
+ tgtWWN: conn.tgtWWNs[index],
+ tgtHostLun: conn.tgtHostLUNs[index],
+ }
+ conn.tgtTargets = append(conn.tgtTargets, tgt)
}
}
@@ -134,7 +150,7 @@ func tryConnectVolume(ctx context.Context, connMap map[string]interface{}) (stri
}
if devInfo.realDeviceName == "" {
- log.AddContext(ctx).Warningln("No FibreChannel volume device found")
+ log.AddContext(ctx).Warningln("No Connector volume device found")
return "", errors.New(connector.VolumeNotFound)
}
@@ -168,7 +184,8 @@ func checkPathAvailable(ctx context.Context, conn connectorInfo, devInfo deviceI
switch conn.multiPathType {
case connector.DMMultiPath:
- return connector.VerifyDeviceAvailableOfDM(ctx, conn.tgtLunWWN, conn.pathCount, []string{devInfo.realDeviceName}, tryDisConnectVolume)
+ return connector.VerifyDeviceAvailableOfDM(ctx,
+ conn.tgtLunWWN, conn.pathCount, []string{devInfo.realDeviceName}, tryDisConnectVolume)
case connector.HWUltraPath:
return connector.GetDiskPathAndCheckStatus(ctx, connector.UltraPathCommand, conn.tgtLunWWN)
case connector.HWUltraPathNVMe:
@@ -210,7 +227,7 @@ func getHostAttrName(ctx context.Context, host, portAttr string) (string, error)
if !strings.HasPrefix(line, "0x") {
continue
}
- attrWwn := line[2:]
+ attrWwn := line[attrWwnIndex:]
return attrWwn, nil
}
@@ -350,7 +367,12 @@ func getPossibleDeices(hbas []map[string]string, targets []target) []rawDevice {
if pciNum != "" {
for _, target := range targets {
targetWWN := fmt.Sprintf("0x%s", strings.ToLower(target.tgtWWN))
- rawDev := rawDevice{platform, pciNum, targetWWN, target.tgtHostLun}
+ rawDev := rawDevice{
+ platform: platform,
+ pciNum: pciNum,
+ wwn: targetWWN,
+ lun: target.tgtHostLun,
+ }
rawDevices = append(rawDevices, rawDev)
}
}
@@ -361,7 +383,7 @@ func getPossibleDeices(hbas []map[string]string, targets []target) []rawDevice {
func getPci(devPath []string) (string, string) {
var platform string
- if len(devPath) <= 3 {
+ if len(devPath) <= devPathLength {
return "", ""
}
platformSupport := devPath[3] == "platform"
@@ -398,10 +420,11 @@ func formatLunId(lunId string) string {
log.Warningf("formatLunId failed, lunId: %v, err: %v", lunId, err)
}
- if intLunId < 256 {
+ if intLunId < wholeLunIdLength {
return lunId
} else {
- return fmt.Sprintf("0x%04x%04x00000000", intLunId&0xffff, intLunId>>16&0xffff)
+ return fmt.Sprintf("0x%04x%04x00000000",
+ intLunId&lunIdAndOperationBits, intLunId>>lunIdHighBits&lunIdAndOperationBits)
}
}
@@ -415,7 +438,8 @@ func getHostDevices(ctx context.Context, possibleDevices []rawDevice) []string {
platform = ""
}
- hostDevice := fmt.Sprintf("/dev/disk/by-path/%spci-%s-fc-%s-lun-%s", platform, value.pciNum, value.wwn, formatLunId(value.lun))
+ hostDevice := fmt.Sprintf("/dev/disk/by-path/%spci-%s-fc-%s-lun-%s",
+ platform, value.pciNum, value.wwn, formatLunId(value.lun))
hostDevices = append(hostDevices, hostDevice)
}
log.AddContext(ctx).Infof("Get host devices are %v", hostDevices)
@@ -456,14 +480,14 @@ func waitDeviceDiscovery(ctx context.Context,
info.tries += 1
return false, nil
- }, time.Second*60, time.Second*2)
+ }, waitDeviceDiscoveryTimeout, waitDeviceDiscoveryInterval)
return info, err
}
func getHBAChannelSCSITargetLun(ctx context.Context, hba map[string]string, targets []target) ([][]string, []string) {
hostDevice := hba["host_device"]
- if hostDevice != "" && len(hostDevice) > 4 {
- hostDevice = hostDevice[4:]
+ if hostDevice != "" && len(hostDevice) > hostDeviceLength {
+ hostDevice = hostDevice[hostDeviceLength:]
}
path := fmt.Sprintf("/sys/class/fc_transport/target%s:", hostDevice)
@@ -483,10 +507,10 @@ func getHBAChannelSCSITargetLun(ctx context.Context, hba map[string]string, targ
for _, line := range lines {
if strings.HasPrefix(line, path) {
lineList := strings.Split(line, "/")
- if len(lineList) <= 4 {
+ if len(lineList) <= hostDeviceLength {
continue
}
- ctlStr := lineList[4]
+ ctlStr := lineList[hostDeviceLength]
ctlList := strings.Split(ctlStr, ":")
if len(ctlList) <= 1 {
continue
@@ -503,8 +527,7 @@ func getHBAChannelSCSITargetLun(ctx context.Context, hba map[string]string, targ
}
func rescanHosts(ctx context.Context, hbas []map[string]string, conn *connectorInfo) {
- var process []interface{}
- var skipped []interface{}
+ var process, skipped []interface{}
for _, hba := range hbas {
ctls, lunWildCards := getHBAChannelSCSITargetLun(ctx, hba, conn.tgtTargets)
if ctls != nil {
@@ -522,10 +545,6 @@ func rescanHosts(ctx context.Context, hbas []map[string]string, conn *connectorI
process = skipped
}
- var pathCount int
- defer func() {
- conn.pathCount = pathCount
- }()
for _, p := range process {
pro, ok := p.([]interface{})
if !ok {
@@ -552,7 +571,7 @@ func rescanHosts(ctx context.Context, hbas []map[string]string, conn *connectorI
for _, c := range ctls {
scanFC(ctx, c, hba["host_device"])
- pathCount++
+ conn.pathCount++
if !conn.volumeUseMultiPath {
break
}
@@ -564,7 +583,7 @@ func rescanHosts(ctx context.Context, hbas []map[string]string, conn *connectorI
}
func scanFC(ctx context.Context, channelTargetLun []string, hostDevice string) {
- if len(channelTargetLun) <= 2 {
+ if len(channelTargetLun) <= channelTargetLunLength {
return
}
scanCommand := fmt.Sprintf("echo \"%s %s %s\" > /sys/class/scsi_host/%s/scan",
diff --git a/connector/host/host_helper_test.go b/connector/host/host_helper_test.go
index cbb75f5f..72eb5008 100644
--- a/connector/host/host_helper_test.go
+++ b/connector/host/host_helper_test.go
@@ -25,7 +25,7 @@ import (
"github.com/agiledragon/gomonkey/v2"
"github.com/prashantv/gostub"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -84,23 +84,23 @@ func TestNewNodeHostInfo(t *testing.T) {
})
defer getRoCEInitiator.Reset()
- convey.Convey("TestNewNodeHostInfoSuccessful", t, func() {
+ t.Run("TestNewNodeHostInfoSuccessful", func(t *testing.T) {
execShellCmd := gostub.StubFunc(&utils.ExecShellCmd, want.HostName, nil)
defer execShellCmd.Reset()
nodeHostInfo, err := NewNodeHostInfo(context.Background())
if !reflect.DeepEqual(nodeHostInfo, want) {
t.Errorf("NewNodeHostInfo() got = %v, want %v", nodeHostInfo, want)
}
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("TestNewNodeHostInfoWithQueryHostNameFail", t, func() {
+ t.Run("TestNewNodeHostInfoWithQueryHostNameFail", func(t *testing.T) {
execShellCmd := gostub.StubFunc(&utils.ExecShellCmd, nil, errors.New("timeout"))
defer execShellCmd.Reset()
nodeHostInfo, err := NewNodeHostInfo(context.Background())
- convey.So(nodeHostInfo, convey.ShouldBeNil)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
+ require.Nil(t, nodeHostInfo)
})
}
@@ -111,9 +111,9 @@ func TestSaveNodeHostInfoToSecretWithGetSecretError(t *testing.T) {
})
defer getSecret.Reset()
- convey.Convey("TestSaveNodeHostInfoToSecretWithGetSecretError", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithGetSecretError", func(t *testing.T) {
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
isNotFound := gomonkey.ApplyFunc(apiErrors.IsNotFound, func(err error) bool {
@@ -121,7 +121,7 @@ func TestSaveNodeHostInfoToSecretWithGetSecretError(t *testing.T) {
})
defer isNotFound.Reset()
- convey.Convey("TestSaveNodeHostInfoToSecretWithIsNotFoundError", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithIsNotFoundError", func(t *testing.T) {
createSecretFunc := func(_ *k8sutils.KubeClient, ctx context.Context, secret *corev1.Secret) (*corev1.Secret,
error) {
return secret, nil
@@ -134,7 +134,7 @@ func TestSaveNodeHostInfoToSecretWithGetSecretError(t *testing.T) {
defer newNodeHostInfo.Reset()
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
createSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "CreateSecret",
@@ -148,19 +148,19 @@ func TestSaveNodeHostInfoToSecretWithGetSecretError(t *testing.T) {
})
defer isAlreadyExists.Reset()
- convey.Convey("TestSaveNodeHostInfoToSecretWithSecretNotExistAndCreateFail", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithSecretNotExistAndCreateFail", func(t *testing.T) {
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("TestSaveNodeHostInfoToSecretWithSecretNotExistAndCreateReturnExists", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithSecretNotExistAndCreateReturnExists", func(t *testing.T) {
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestSaveNodeHostInfoToSecretWithSecretNotExist(t *testing.T) {
- convey.Convey("TestSaveNodeHostInfoToSecretWithGetSecretNotExist", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithGetSecretNotExist", func(t *testing.T) {
getSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "GetSecret",
func(_ *k8sutils.KubeClient, ctx context.Context, secretName, namespace string) (*corev1.Secret, error) {
return &corev1.Secret{}, nil
@@ -190,12 +190,12 @@ func TestSaveNodeHostInfoToSecretWithSecretNotExist(t *testing.T) {
defer updateSecret.Reset()
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
}
func TestSaveNodeHostInfoToSecretWithNewHostInfoError(t *testing.T) {
- convey.Convey("TestSaveNodeHostInfoToSecretWithNewHostInfoError", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithNewHostInfoError", func(t *testing.T) {
getSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "GetSecret",
func(_ *k8sutils.KubeClient, ctx context.Context, secretName, namespace string) (*corev1.Secret, error) {
return &corev1.Secret{}, nil
@@ -208,7 +208,7 @@ func TestSaveNodeHostInfoToSecretWithNewHostInfoError(t *testing.T) {
defer newNodeHostInfo.Reset()
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
@@ -224,7 +224,7 @@ func TestSaveNodeHostInfoToSecretWithUpdateSecret(t *testing.T) {
})
defer newNodeHostInfo.Reset()
- convey.Convey("TestSaveNodeHostInfoToSecretWithMarshalError", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithMarshalError", func(t *testing.T) {
errorMsg := "marshal error"
marshal := gomonkey.ApplyFunc(json.Marshal, func(v any) ([]byte, error) {
return nil, errors.New(errorMsg)
@@ -232,11 +232,11 @@ func TestSaveNodeHostInfoToSecretWithUpdateSecret(t *testing.T) {
defer marshal.Reset()
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
- convey.So(err, convey.ShouldEqual, errorMsg)
+ require.Error(t, err)
+ require.ErrorContains(t, err, errorMsg)
})
- convey.Convey("TestSaveNodeHostInfoToSecretWithUpdateSecretFail", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithUpdateSecretFail", func(t *testing.T) {
updateSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "UpdateSecret",
func(_ *k8sutils.KubeClient, ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) {
return nil, errors.New("error")
@@ -244,10 +244,10 @@ func TestSaveNodeHostInfoToSecretWithUpdateSecret(t *testing.T) {
defer updateSecret.Reset()
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("TestSaveNodeHostInfoToSecretWithUpdateSecretSuccess", t, func() {
+ t.Run("TestSaveNodeHostInfoToSecretWithUpdateSecretSuccess", func(t *testing.T) {
updateSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "UpdateSecret",
func(_ *k8sutils.KubeClient, ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) {
return secret, nil
@@ -255,12 +255,12 @@ func TestSaveNodeHostInfoToSecretWithUpdateSecret(t *testing.T) {
defer updateSecret.Reset()
err := SaveNodeHostInfoToSecret(context.Background())
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
}
func TestGetNodeHostInfosFromSecret(t *testing.T) {
- convey.Convey("TestGetNodeHostInfosFromSecretAndGetSecretError", t, func() {
+ t.Run("TestGetNodeHostInfosFromSecretAndGetSecretError", func(t *testing.T) {
getSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "GetSecret",
func(_ *k8sutils.KubeClient, ctx context.Context, secretName, namespace string) (*corev1.Secret, error) {
return nil, errors.New(" get secret error")
@@ -268,10 +268,10 @@ func TestGetNodeHostInfosFromSecret(t *testing.T) {
defer getSecret.Reset()
nodeHostInfos, err := GetNodeHostInfosFromSecret(context.Background(), testNodeInfo.HostName)
- convey.So(err, convey.ShouldBeError)
- convey.So(nodeHostInfos, convey.ShouldBeNil)
+ require.Error(t, err)
+ require.Nil(t, nodeHostInfos)
})
- convey.Convey("TestGetNodeHostInfosFromSecretAndDataIsNil", t, func() {
+ t.Run("TestGetNodeHostInfosFromSecretAndDataIsNil", func(t *testing.T) {
getSecret := gomonkey.ApplyMethod(reflect.TypeOf(testK8sUtils), "GetSecret",
func(_ *k8sutils.KubeClient, ctx context.Context, secretName, namespace string) (*corev1.Secret, error) {
return &corev1.Secret{}, nil
@@ -279,11 +279,11 @@ func TestGetNodeHostInfosFromSecret(t *testing.T) {
defer getSecret.Reset()
nodeHostInfos, err := GetNodeHostInfosFromSecret(context.Background(), testNodeInfo.HostName)
- convey.So(err, convey.ShouldBeError)
- convey.So(err.Error(), convey.ShouldEqual, "secret data is empty")
- convey.So(nodeHostInfos, convey.ShouldBeNil)
+ require.Error(t, err)
+ require.EqualError(t, err, "secret data is empty")
+ require.Nil(t, nodeHostInfos)
})
- convey.Convey("TestGetNodeHostInfosFromSecretSuccess", t, func() {
+ t.Run("TestGetNodeHostInfosFromSecretSuccess", func(t *testing.T) {
secretData, err := json.Marshal(testNodeInfo)
if err != nil {
t.Errorf("TestGetNodeHostInfosFromSecretSuccess() json marshal %v", err)
@@ -301,7 +301,7 @@ func TestGetNodeHostInfosFromSecret(t *testing.T) {
defer getSecret.Reset()
testReturnData, err := GetNodeHostInfosFromSecret(context.Background(), testNodeInfo.HostName)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
if !reflect.DeepEqual(testReturnData, testNodeInfo) {
t.Errorf("TestGetNodeHostInfosFromSecretSuccess() got = %v, want %v", testReturnData, testNodeInfo)
}
diff --git a/connector/iscsi/iscsi.go b/connector/iscsi/iscsi.go
index 9aaf03c4..2482b9f5 100644
--- a/connector/iscsi/iscsi.go
+++ b/connector/iscsi/iscsi.go
@@ -24,12 +24,12 @@ import (
"huawei-csi-driver/utils/log"
)
-// ISCSI implements the Connector interface for ISCSI protocol
-type ISCSI struct {
+// Connector implements the connector.VolumeConnector for Connector protocol
+type Connector struct {
}
func init() {
- connector.RegisterConnector(connector.ISCSIDriver, &ISCSI{})
+ connector.RegisterConnector(connector.ISCSIDriver, &Connector{})
}
// ConnectVolume to mount the source to target path, the source path can be block or nfs
@@ -37,7 +37,7 @@ func init() {
//
// mount /dev/sdb /
// mount /
-func (isc *ISCSI) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
+func (isc *Connector) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
log.AddContext(ctx).Infof("ISCSI Start to connect volume ==> connect info: %v",
utils.MaskConnSensitiveInfo(conn))
tgtLunWWN, exist := conn["tgtLunWWN"].(string)
@@ -48,7 +48,7 @@ func (isc *ISCSI) ConnectVolume(ctx context.Context, conn map[string]interface{}
}
// DisConnectVolume to unmount the target path
-func (isc *ISCSI) DisConnectVolume(ctx context.Context, tgtLunWWN string) error {
+func (isc *Connector) DisConnectVolume(ctx context.Context, tgtLunWWN string) error {
log.AddContext(ctx).Infof("ISCSI Start to disconnect volume ==> volume wwn is: %v", tgtLunWWN)
return connector.DisConnectVolumeCommon(ctx, tgtLunWWN, connector.ISCSIDriver, tryDisConnectVolume)
}
diff --git a/connector/iscsi/iscsi_helper.go b/connector/iscsi/iscsi_helper.go
index 20fd0baf..eb0b4a63 100644
--- a/connector/iscsi/iscsi_helper.go
+++ b/connector/iscsi/iscsi_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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,13 +20,14 @@ import (
"context"
"errors"
"fmt"
- "io/ioutil"
"math"
+ "os"
"path/filepath"
"runtime/debug"
"sort"
"strings"
"sync"
+ "sync/atomic"
"time"
"huawei-csi-driver/connector"
@@ -37,6 +38,24 @@ import (
"huawei-csi-driver/utils/log"
)
+const (
+ timeForDMAppear = 15
+ wwnTypeIndex = 4
+ wwidIndex = 6
+ scanSingleTimes = 15
+ scanExponent = 2.0
+ scanStep = 2
+ hostChannelTargetLunLength = 4
+ splitPathSegment = 2
+ hostIndexOfTargetPath = 26
+ splitInfoSegment = 4
+ targetInfoSegment = 2
+
+ scanSingleInterval = 2 * time.Second
+ iscsiSessionLoginInterval = 2 * time.Second
+ iscsiSessionLoginTimes = 60
+)
+
var singleGroup = concurrent.NewSingleGroup[connectResult]()
type connectResult struct {
@@ -68,12 +87,12 @@ type singleConnectorInfo struct {
}
type shareData struct {
- stopConnecting bool
- numLogin int64
- failedLogin int64
- stoppedThreads int64
- foundDevices []string
- justAddedDevices []string
+ stopConnecting atomic.Bool
+ numLogin atomic.Int64
+ failedLogin atomic.Int64
+ stoppedThreads atomic.Int64
+ foundDevices concurrent.Slice[string]
+ justAddedDevices concurrent.Slice[string]
}
type scanRequest struct {
@@ -184,7 +203,7 @@ func updateISCSIAdminWithExitCode(ctx context.Context,
return runISCSIAdmin(ctx, tgtPortal, targetIQN, iscsiCMD, checkExitCode)
}
-func iscsiCMD(propertyKey, propertyValue string) string {
+func genIscsiOpArgs(propertyKey, propertyValue string) string {
return fmt.Sprintf("--op update -n %s -v %s", propertyKey, propertyValue)
}
@@ -236,14 +255,14 @@ func getAllISCSISession(ctx context.Context) [][]string {
for _, iscsi := range strings.Split(allSessions, "\n") {
if iscsi != "" {
splitInfo := strings.Split(iscsi, " ")
- if len(splitInfo) < 4 {
+ if len(splitInfo) < splitInfoSegment {
log.AddContext(ctx).Warningf("iscsi session %s error", splitInfo)
continue
}
sid := splitInfo[1][1 : len(splitInfo[1])-1]
tgtInfo := strings.Split(splitInfo[2], ",")
- if len(tgtInfo) < 2 {
+ if len(tgtInfo) < targetInfoSegment {
continue
}
portal, tpgt := tgtInfo[0], tgtInfo[1]
@@ -289,8 +308,7 @@ func connectISCSIPortal(ctx context.Context,
var manualScan bool
err = updateISCSIAdmin(ctx, tgtPortal, targetIQN, "node.session.scan", "manual")
if err != nil {
- log.AddContext(ctx).Warningf("Update node session scan mode to manual error, reason: %v",
- tgtPortal, err)
+ log.AddContext(ctx).Warningf("Update node session scan mode to manual error, reason: %v", err)
}
manualScan = err == nil
@@ -301,7 +319,7 @@ func connectISCSIPortal(ctx context.Context,
return "", false
}
- for i := 0; i < 60; i++ {
+ for i := 0; i < iscsiSessionLoginTimes; i++ {
sessions := getAllISCSISession(ctx)
for _, s := range sessions {
if s[0] == "tcp:" && strings.ToLower(tgtPortal) == strings.ToLower(s[2]) && targetIQN == s[4] {
@@ -323,7 +341,7 @@ func connectISCSIPortal(ctx context.Context,
return "", false
}
- time.Sleep(time.Second * 2)
+ time.Sleep(iscsiSessionLoginInterval)
}
return "", false
}
@@ -340,7 +358,7 @@ func getHostChannelTargetLun(session, tgtLun string) []string {
if paths != nil {
_, file := filepath.Split(paths[0])
splitPath := strings.Split(file, ":")
- if len(splitPath) <= 2 {
+ if len(splitPath) <= splitPathSegment {
return nil
}
channel = splitPath[1]
@@ -354,14 +372,14 @@ func getHostChannelTargetLun(session, tgtLun string) []string {
}
}
- index := strings.Index(paths[0][26:], "/")
- host = paths[0][26:][:index]
+ index := strings.Index(paths[0][hostIndexOfTargetPath:], "/")
+ host = paths[0][hostIndexOfTargetPath:][:index]
hostChannelTargetLun = append(hostChannelTargetLun, host, channel, target, tgtLun)
return hostChannelTargetLun
}
func scanISCSI(ctx context.Context, hostChannelTargetLun []string) {
- if len(hostChannelTargetLun) <= 3 {
+ if len(hostChannelTargetLun) < hostChannelTargetLunLength {
return
}
channelTargetLun := fmt.Sprintf("%s %s %s", hostChannelTargetLun[1], hostChannelTargetLun[2],
@@ -419,7 +437,7 @@ func (s *deviceScan) scan(ctx context.Context,
if s.secondNextScan <= 0 {
s.numRescans++
scanISCSI(ctx, req.hostChannelTargetLun)
- s.secondNextScan = int(math.Pow(float64(s.numRescans+2), 2.0))
+ s.secondNextScan = int(math.Pow(float64(s.numRescans+scanStep), scanExponent))
}
device = getDeviceByHCTL(req.sessionId, req.hostChannelTargetLun)
@@ -429,7 +447,8 @@ func (s *deviceScan) scan(ctx context.Context,
device = connector.ClearUnavailableDevice(ctx, device, req.tgtLunWWN)
}
- doScans = s.numRescans <= deviceScanAttemptsDefault && !(device != "" || req.iSCSIShareData.stopConnecting)
+ doScans = s.numRescans <= deviceScanAttemptsDefault &&
+ !(device != "" || req.iSCSIShareData.stopConnecting.Load())
if doScans {
time.Sleep(time.Second)
s.secondNextScan--
@@ -456,7 +475,7 @@ func connectVol(ctx context.Context,
secondNextScan = 4
}
- iSCSIShareData.numLogin += 1
+ iSCSIShareData.numLogin.Add(1)
dScan := deviceScan{
numRescans: numRescans,
secondNextScan: secondNextScan,
@@ -469,15 +488,15 @@ func connectVol(ctx context.Context,
log.AddContext(ctx).Debugf("LUN %s on iSCSI portal %s not found on sysfs after logging in.",
tgt.tgtHostLun, tgt.tgtPortal)
} else {
- iSCSIShareData.foundDevices = append(iSCSIShareData.foundDevices, device)
- iSCSIShareData.justAddedDevices = append(iSCSIShareData.justAddedDevices, device)
+ iSCSIShareData.foundDevices.Append(device)
+ iSCSIShareData.justAddedDevices.Append(device)
}
} else {
log.AddContext(ctx).Warningf("build iSCSI session %s error", tgt.tgtPortal)
- iSCSIShareData.failedLogin += 1
+ iSCSIShareData.failedLogin.Add(1)
}
- iSCSIShareData.stoppedThreads += 1
+ iSCSIShareData.stoppedThreads.Add(1)
return
}
@@ -524,10 +543,10 @@ func tryConnectVolume(ctx context.Context, connMap map[string]interface{}) (stri
if err != nil {
log.AddContext(ctx).Errorf("failed to find a disk. %v", err)
}
- iSCSIShareData.stopConnecting = true
+ iSCSIShareData.stopConnecting.Store(true)
wait.Wait()
- return checkDeviceAvailable(ctx, conn, iSCSIShareData, diskName, int(iSCSIShareData.numLogin))
+ return checkDeviceAvailable(ctx, conn, iSCSIShareData, diskName, int(iSCSIShareData.numLogin.Load()))
}
func catchConnectError(ctx context.Context) {
@@ -588,10 +607,10 @@ func checkDeviceAvailable(ctx context.Context,
}
if diskName == "" {
- err := connector.RemoveDevices(ctx, iSCSIShareData.foundDevices)
+ err := connector.RemoveDevices(ctx, iSCSIShareData.foundDevices.Values())
if err != nil {
log.AddContext(ctx).Warningf("Remove devices %v error: %v",
- iSCSIShareData.foundDevices, err)
+ iSCSIShareData.foundDevices.Values(), err)
}
return "", utils.Errorln(ctx, connector.VolumeNotFound)
}
@@ -599,7 +618,7 @@ func checkDeviceAvailable(ctx context.Context,
switch conn.multiPathType {
case connector.DMMultiPath:
return connector.VerifyDeviceAvailableOfDM(ctx, conn.tgtLunWWN,
- expectPathNumber, iSCSIShareData.foundDevices, tryDisConnectVolume)
+ expectPathNumber, iSCSIShareData.foundDevices.Values(), tryDisConnectVolume)
case connector.HWUltraPath:
return connector.VerifyDeviceAvailableOfUltraPath(ctx, connector.UltraPathCommand, diskName)
case connector.HWUltraPathNVMe:
@@ -610,11 +629,11 @@ func checkDeviceAvailable(ctx context.Context,
}
func checkSinglePathAvailable(ctx context.Context, iSCSIShareData *shareData, tgtLunWWN string) (string, error) {
- if len(iSCSIShareData.foundDevices) == 0 {
+ if iSCSIShareData.foundDevices.Len() == 0 {
return "", errors.New(connector.VolumeNotFound)
}
- device := fmt.Sprintf("/dev/%s", iSCSIShareData.foundDevices[0])
+ device := fmt.Sprintf("/dev/%s", iSCSIShareData.foundDevices.Get(0))
err := connector.VerifySingleDevice(ctx, device, tgtLunWWN,
connector.VolumeNotFound, tryDisConnectVolume)
if err != nil {
@@ -624,25 +643,25 @@ func checkSinglePathAvailable(ctx context.Context, iSCSIShareData *shareData, tg
}
func scanSingle(iSCSIShareData *shareData) {
- for i := 0; i < 15; i++ {
- if len(iSCSIShareData.foundDevices) != 0 {
+ for i := 0; i < scanSingleTimes; i++ {
+ if iSCSIShareData.foundDevices.Len() != 0 {
break
}
- time.Sleep(time.Second * 2)
+ time.Sleep(scanSingleInterval)
}
}
func getSYSfsWwn(ctx context.Context, foundDevices []string, mPath string) (string, error) {
if mPath != "" {
dmFile := fmt.Sprintf("/sys/block/%s/dm/uuid", mPath)
- data, err := ioutil.ReadFile(dmFile)
+ data, err := os.ReadFile(dmFile)
if err != nil {
msg := fmt.Sprintf("Read dm file %s error: %v", dmFile, err)
log.AddContext(ctx).Errorln(msg)
return "", errors.New(msg)
}
- if wwid := data[6:]; wwid != nil {
+ if wwid := data[wwidIndex:]; wwid != nil {
return string(wwid), nil
}
}
@@ -652,19 +671,19 @@ func getSYSfsWwn(ctx context.Context, foundDevices []string, mPath string) (stri
}
for _, device := range foundDevices {
deviceFile := fmt.Sprintf("/sys/block/%s/device/wwid", device)
- data, err := ioutil.ReadFile(deviceFile)
+ data, err := os.ReadFile(deviceFile)
if err != nil {
msg := fmt.Sprintf("Read device file %s error: %v", deviceFile, err)
log.AddContext(ctx).Errorln(msg)
continue
}
- wwnType, exist := wwnTypes[string(data[:4])]
+ wwnType, exist := wwnTypes[string(data[:wwnTypeIndex])]
if !exist {
wwnType = "8"
}
- wwid := wwnType + string(data[4:])
+ wwid := wwnType + string(data[wwnTypeIndex:])
return wwid, nil
}
@@ -703,19 +722,21 @@ func addMultiPath(ctx context.Context, devPath string) error {
}
func tryScanMultiDevice(ctx context.Context, mPath string, iSCSIShareData *shareData) string {
- for mPath == "" && len(iSCSIShareData.justAddedDevices) != 0 {
- devicePath := "/dev/" + iSCSIShareData.justAddedDevices[0]
- iSCSIShareData.justAddedDevices = iSCSIShareData.justAddedDevices[1:]
+ for mPath == "" && iSCSIShareData.justAddedDevices.Len() != 0 {
+ devicePath := "/dev/" + iSCSIShareData.justAddedDevices.Get(0)
+ if err := iSCSIShareData.justAddedDevices.Cut(1, iSCSIShareData.justAddedDevices.Len()); err != nil {
+ log.AddContext(ctx).Warningln(err)
+ }
err := addMultiPath(ctx, devicePath)
if err != nil {
log.AddContext(ctx).Warningf("Add multiPath path failed, error: %s", err)
}
var isClear bool
- mPath, isClear = connector.FindAvailableMultiPath(ctx, iSCSIShareData.foundDevices)
+ mPath, isClear = connector.FindAvailableMultiPath(ctx, iSCSIShareData.foundDevices.Values())
if isClear {
- iSCSIShareData.foundDevices = nil
- iSCSIShareData.justAddedDevices = nil
+ iSCSIShareData.foundDevices.Reset()
+ iSCSIShareData.justAddedDevices.Reset()
}
}
return mPath
@@ -726,12 +747,12 @@ func scanMultiDevice(ctx context.Context,
iSCSIShareData *shareData,
wwnAdded bool) (string, bool) {
var err error
- if mPath == "" && len(iSCSIShareData.foundDevices) != 0 {
+ if mPath == "" && iSCSIShareData.foundDevices.Len() != 0 {
var isClear bool
- mPath, isClear = connector.FindAvailableMultiPath(ctx, iSCSIShareData.foundDevices)
+ mPath, isClear = connector.FindAvailableMultiPath(ctx, iSCSIShareData.foundDevices.Values())
if isClear {
- iSCSIShareData.foundDevices = nil
- iSCSIShareData.justAddedDevices = nil
+ iSCSIShareData.foundDevices.Reset()
+ iSCSIShareData.justAddedDevices.Reset()
}
if wwn != "" && !(mPath != "" || wwnAdded) {
@@ -750,8 +771,8 @@ func scanMultiDevice(ctx context.Context,
func findDiskOfUltraPath(ctx context.Context, lenIndex int, iSCSIShareData *shareData, upType, lunWWN string) string {
var diskName string
var err error
- for !((int64(lenIndex) == iSCSIShareData.stoppedThreads && len(iSCSIShareData.foundDevices) == 0) ||
- (diskName != "" && int64(lenIndex) == iSCSIShareData.numLogin+iSCSIShareData.failedLogin)) {
+ for !((int64(lenIndex) == iSCSIShareData.stoppedThreads.Load() && iSCSIShareData.foundDevices.Len() == 0) ||
+ (diskName != "" && int64(lenIndex) == iSCSIShareData.numLogin.Load()+iSCSIShareData.failedLogin.Load())) {
diskName, err = connector.GetDiskNameByWWN(ctx, upType, lunWWN)
if err == nil {
@@ -768,10 +789,10 @@ func findDiskOfDM(ctx context.Context, lenIndex int, LunWWN string, iSCSIShareDa
var lastTryOn int64
var mPath, wwn string
var err error
- for !((int64(lenIndex) == iSCSIShareData.stoppedThreads && len(iSCSIShareData.foundDevices) == 0) ||
- (mPath != "" && int64(lenIndex) == iSCSIShareData.numLogin+iSCSIShareData.failedLogin)) {
- if wwn == "" && len(iSCSIShareData.foundDevices) != 0 {
- wwn, err = getSYSfsWwn(ctx, iSCSIShareData.foundDevices, mPath)
+ for !((int64(lenIndex) == iSCSIShareData.stoppedThreads.Load() && iSCSIShareData.foundDevices.Len() == 0) ||
+ (mPath != "" && int64(lenIndex) == iSCSIShareData.numLogin.Load()+iSCSIShareData.failedLogin.Load())) {
+ if wwn == "" && iSCSIShareData.foundDevices.Len() != 0 {
+ wwn, err = getSYSfsWwn(ctx, iSCSIShareData.foundDevices.Values(), mPath)
if err != nil {
break
}
@@ -782,10 +803,10 @@ func findDiskOfDM(ctx context.Context, lenIndex int, LunWWN string, iSCSIShareDa
}
mPath, wwnAdded = scanMultiDevice(ctx, mPath, wwn, iSCSIShareData, wwnAdded)
- if lastTryOn == 0 && len(iSCSIShareData.foundDevices) != 0 && int64(
- lenIndex) == iSCSIShareData.stoppedThreads {
+ if lastTryOn == 0 && iSCSIShareData.foundDevices.Len() != 0 &&
+ int64(lenIndex) == iSCSIShareData.stoppedThreads.Load() {
log.AddContext(ctx).Infoln("All connection threads finished, giving 15 seconds for dm to appear.")
- lastTryOn = time.Now().Unix() + 15
+ lastTryOn = time.Now().Unix() + timeForDMAppear
} else if lastTryOn != 0 && lastTryOn < time.Now().Unix() {
break
}
@@ -815,7 +836,7 @@ func getISCSISession(ctx context.Context, devSessionIds []string) []singleConnec
func disconnectFromISCSIPortal(ctx context.Context, tgtPortal, targetIQN string) {
checkExitCode := []string{"exit status 0", "exit status 15", "exit status 255"}
err := updateISCSIAdminWithExitCode(ctx, tgtPortal, targetIQN,
- iscsiCMD("node.startup", "manual"),
+ genIscsiOpArgs("node.startup", "manual"),
checkExitCode)
if err != nil {
log.AddContext(ctx).Warningf("Update node startUp error, reason: %v", err)
diff --git a/connector/local/local.go b/connector/local/local.go
index 7d90f30a..0dd45060 100644
--- a/connector/local/local.go
+++ b/connector/local/local.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -19,26 +19,23 @@ package local
import (
"context"
- "time"
"huawei-csi-driver/connector"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
-// Local to define a local lock when connect or disconnect, in order to preventing connect and disconnect confusion
-type Local struct {
+// Connector to define a local lock when connect or disconnect, in order to preventing connect and disconnect confusion
+type Connector struct {
}
-var waitDevOnlineTimeInterval = 2 * time.Second
-
func init() {
- connector.RegisterConnector(connector.LocalDriver, &Local{})
+ connector.RegisterConnector(connector.LocalDriver, &Connector{})
}
// ConnectVolume to connect local volume, such as /dev/disk/by-id/wwn-0x*
-func (loc *Local) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
- log.AddContext(ctx).Infof("Local Start to connect volume ==> connect info: %v", conn)
+func (loc *Connector) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
+ log.AddContext(ctx).Infof("Local connector Start to connect volume ==> connect info: %v", conn)
tgtLunWWN, exist := conn["tgtLunWWN"].(string)
if !exist {
return "", utils.Errorln(ctx, "key tgtLunWWN does not exist in connection properties")
@@ -47,7 +44,7 @@ func (loc *Local) ConnectVolume(ctx context.Context, conn map[string]interface{}
}
// DisConnectVolume to remove the local lun path
-func (loc *Local) DisConnectVolume(ctx context.Context, tgtLunWWN string) error {
- log.AddContext(ctx).Infof("Local Start to disconnect volume ==> volume wwn is: %v", tgtLunWWN)
+func (loc *Connector) DisConnectVolume(ctx context.Context, tgtLunWWN string) error {
+ log.AddContext(ctx).Infof("Local Connector Start to disconnect volume ==> volume wwn is: %v", tgtLunWWN)
return connector.DisConnectVolumeCommon(ctx, tgtLunWWN, connector.LocalDriver, tryDisConnectVolume)
}
diff --git a/connector/local/local_helper.go b/connector/local/local_helper.go
index e4391d43..eb53f4a7 100644
--- a/connector/local/local_helper.go
+++ b/connector/local/local_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -29,6 +29,8 @@ import (
"huawei-csi-driver/utils/log"
)
+const waitDevOnlineTimeInterval = 2 * time.Second
+
func waitDevOnline(ctx context.Context, tgtLunWWN string) string {
devPath := fmt.Sprintf("/dev/disk/by-id/wwn-0x%s", tgtLunWWN)
for i := 0; i < 30; i++ {
diff --git a/connector/local/local_test.go b/connector/local/local_test.go
index 2c90094f..1c733cc6 100644
--- a/connector/local/local_test.go
+++ b/connector/local/local_test.go
@@ -77,7 +77,8 @@ func TestConnectVolume(t *testing.T) {
},
}
- stubs := gostub.Stub(&waitDevOnlineTimeInterval, time.Millisecond)
+ var interval = waitDevOnlineTimeInterval
+ stubs := gostub.Stub(&interval, time.Millisecond)
defer stubs.Reset()
stubs.Stub(&utils.ExecShellCmd, func(ctx context.Context, format string, args ...interface{}) (string, error) {
@@ -90,7 +91,7 @@ func TestConnectVolume(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- loc := &Local{}
+ loc := &Connector{}
got, err := loc.ConnectVolume(tt.args.ctx, tt.args.conn)
if (err != nil) != tt.wantErr {
t.Errorf("ConnectVolume() error = %v, wantErr %v", err, tt.wantErr)
@@ -156,7 +157,7 @@ func TestDisConnectVolume(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- loc := &Local{}
+ loc := &Connector{}
err := loc.DisConnectVolume(tt.args.ctx, tt.args.tgtLunWWN)
if (err != nil) != tt.wantErr {
t.Errorf("DisConnectVolume() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/connector/nfs/nfs.go b/connector/nfs/nfs.go
index d915538d..3d052464 100644
--- a/connector/nfs/nfs.go
+++ b/connector/nfs/nfs.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -24,8 +24,8 @@ import (
"huawei-csi-driver/utils/log"
)
-// NFS to define a local lock when connect or disconnect, in order to preventing mounting and unmounting confusion
-type NFS struct {
+// Connector to define a local lock when connect or disconnect, in order to preventing mounting and unmounting confusion
+type Connector struct {
}
const (
@@ -38,20 +38,21 @@ const (
)
func init() {
- connector.RegisterConnector(connector.NFSDriver, &NFS{})
+ connector.RegisterConnector(connector.NFSDriver, &Connector{})
}
// ConnectVolume to mount the source to target path, the source path can be block or nfs
// Example:
-// mount /dev/sdb /
-// mount /
-func (nfs *NFS) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
- log.AddContext(ctx).Infof("NFS Start to connect volume ==> connect info: %v", conn)
+//
+// mount /dev/sdb /
+// mount /
+func (nfs *Connector) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
+ log.AddContext(ctx).Infof("Nfs Start to connect volume ==> connect info: %v", conn)
return tryConnectVolume(ctx, conn)
}
// DisConnectVolume to unmount the target path
-func (nfs *NFS) DisConnectVolume(ctx context.Context, targetPath string) error {
+func (nfs *Connector) DisConnectVolume(ctx context.Context, targetPath string) error {
log.AddContext(ctx).Infof("NFS Start to disconnect volume ==> target path is: %v", targetPath)
return tryDisConnectVolume(ctx, targetPath)
}
diff --git a/connector/nfs/nfs_helper.go b/connector/nfs/nfs_helper.go
index 559da9bb..4e73bd70 100644
--- a/connector/nfs/nfs_helper.go
+++ b/connector/nfs/nfs_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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,6 +35,12 @@ import (
"huawei-csi-driver/utils/log"
)
+const (
+ fsInfoSegment = 2
+ targetMountPathPermission = 0750
+ unformattedFsCode = 2
+)
+
type connectorInfo struct {
srcType string
sourcePath string
@@ -109,7 +115,7 @@ func tryConnectVolume(ctx context.Context, connMap map[string]interface{}) (stri
return "", err
}
- err = mountDisk(ctx, conn.sourcePath, conn.targetPath, conn.fsType, conn.mntFlags, conn.accessMode)
+ err = mountDisk(ctx, conn)
if err != nil {
return "", err
}
@@ -132,7 +138,7 @@ func preMount(sourcePath, targetPath string, checkSourcePath bool) error {
}
if _, err := os.Stat(targetPath); err != nil && os.IsNotExist(err) {
- if err := os.MkdirAll(targetPath, 0750); err != nil {
+ if err := os.MkdirAll(targetPath, targetMountPathPermission); err != nil {
return errors.New("can not create a target path")
}
}
@@ -278,7 +284,7 @@ func getFSType(ctx context.Context, sourcePath string) (string, error) {
output, err := utils.ExecShellCmd(ctx, "blkid -o udev %s", sourcePath)
if err != nil {
- if errCode, ok := err.(*exec.ExitError); ok && errCode.ExitCode() == 2 {
+ if errCode, ok := err.(*exec.ExitError); ok && errCode.ExitCode() == unformattedFsCode {
log.AddContext(ctx).Infof("Query fs of %s, output: %s, error: %s", sourcePath, output, err)
if formatted, err := connector.IsDeviceFormatted(ctx, sourcePath); err != nil {
return "", fmt.Errorf("check device %s formatted failed, error: %v", sourcePath, err)
@@ -294,7 +300,7 @@ func getFSType(ctx context.Context, sourcePath string) (string, error) {
for _, out := range strings.Split(output, "\n") {
fsInfo := strings.Split(out, "=")
- if len(fsInfo) == 2 && fsInfo[0] == "ID_FS_TYPE" {
+ if len(fsInfo) == fsInfoSegment && fsInfo[0] == "ID_FS_TYPE" {
return fsInfo[1], nil
}
}
@@ -361,60 +367,59 @@ func getDiskSizeType(ctx context.Context, sourcePath string) (string, error) {
return "", errors.New("the disk size does not support")
}
-func mountDisk(ctx context.Context, sourcePath, targetPath, fsType string, flags mountParam,
- accessMode csi.VolumeCapability_AccessMode_Mode) error {
+func mountDisk(ctx context.Context, conn *connectorInfo) error {
var err error
- existFsType, err := getFSType(ctx, sourcePath)
+ existFsType, err := getFSType(ctx, conn.sourcePath)
if err != nil {
return err
}
if existFsType == "" {
// check this disk is in formatting
- inFormatting, err := connector.IsInFormatting(ctx, sourcePath, fsType)
+ inFormatting, err := connector.IsInFormatting(ctx, conn.sourcePath, conn.fsType)
if err != nil {
return err
}
if inFormatting {
- log.AddContext(ctx).Infof("Device %s is in formatting, no need format again. Wait 10 seconds", sourcePath)
+ log.AddContext(ctx).Infof("Device %s is in formatting, no need format again. Wait 10 seconds", conn.sourcePath)
time.Sleep(time.Second * formatWaitInternal)
return errors.New("the disk is in formatting, please wait")
}
- diskSizeType, err := getDiskSizeType(ctx, sourcePath)
+ diskSizeType, err := getDiskSizeType(ctx, conn.sourcePath)
if err != nil {
return err
}
- err = formatDisk(ctx, sourcePath, fsType, diskSizeType)
+ err = formatDisk(ctx, conn.sourcePath, conn.fsType, diskSizeType)
if err != nil {
return err
}
- err = mountUnix(ctx, sourcePath, targetPath, flags, true)
+ err = mountUnix(ctx, conn.sourcePath, conn.targetPath, conn.mntFlags, true)
if err != nil {
return err
}
} else {
- err = mountUnix(ctx, sourcePath, targetPath, flags, true)
+ err = mountUnix(ctx, conn.sourcePath, conn.targetPath, conn.mntFlags, true)
if err != nil {
return err
}
- if accessMode == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER {
+ if conn.accessMode == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER {
log.AddContext(ctx).Infoln("PVC accessMode is ReadWriteMany, not support to expend filesystem")
return nil
}
- if accessMode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY {
+ if conn.accessMode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY {
log.AddContext(ctx).Infoln("PVC accessMode is ReadOnlyMany, no need to expend filesystem")
return nil
}
- err = connector.ResizeMountPath(ctx, targetPath)
+ err = connector.ResizeMountPath(ctx, conn.targetPath)
if err != nil {
- log.AddContext(ctx).Errorf("Resize mount path %s err %s", targetPath, err)
+ log.AddContext(ctx).Errorf("Resize mount path %s err %s", conn.targetPath, err)
return err
}
}
diff --git a/connector/nfs/nfs_test.go b/connector/nfs/nfs_test.go
index 05400198..edcd32ea 100644
--- a/connector/nfs/nfs_test.go
+++ b/connector/nfs/nfs_test.go
@@ -130,7 +130,7 @@ func TestConnectVolume(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- nfs := &NFS{}
+ nfs := &Connector{}
got, err := nfs.ConnectVolume(tt.args.ctx, tt.args.conn)
if (err != nil) != tt.wantErr {
t.Errorf("ConnectVolume() error = %v, wantErr %v", err, tt.wantErr)
@@ -174,7 +174,7 @@ func TestDisConnectVolume(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- nfs := &NFS{}
+ nfs := &Connector{}
if err := nfs.DisConnectVolume(tt.args.ctx, tt.args.targetPath); (err != nil) != tt.wantErr {
t.Errorf("DisConnectVolume() error = %v, wantErr %v", err, tt.wantErr)
}
diff --git a/connector/nfsplus/nfs_plus.go b/connector/nfsplus/nfs_plus.go
new file mode 100644
index 00000000..e92081da
--- /dev/null
+++ b/connector/nfsplus/nfs_plus.go
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. 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 nfsplus to mount or unmount filesystem
+package nfsplus
+
+import (
+ "context"
+
+ "huawei-csi-driver/connector"
+ "huawei-csi-driver/utils/log"
+)
+
+// Connector to define a local lock when connect or disconnect, in order to preventing mounting and unmounting confusion
+type Connector struct {
+}
+
+func init() {
+ connector.RegisterConnector(connector.NFSPlusDriver, &Connector{})
+}
+
+// ConnectVolume to mount the source to target path, the source path can be block or nfs
+// Example:
+//
+// mount /dev/sdb /
+// mount /
+func (nfsPlus *Connector) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
+ log.AddContext(ctx).Infof("NFS+ Start to connect volume ==> connect info: %v", conn)
+ return tryConnectVolume(ctx, conn)
+}
+
+// DisConnectVolume to unmount the target path
+func (nfsPlus *Connector) DisConnectVolume(ctx context.Context, targetPath string) error {
+ log.AddContext(ctx).Infof("NFS+ Start to disconnect volume ==> target path is: %v", targetPath)
+ return tryDisConnectVolume(ctx, targetPath)
+}
diff --git a/connector/nfsplus/nfs_plus_helper.go b/connector/nfsplus/nfs_plus_helper.go
new file mode 100644
index 00000000..dc9857ba
--- /dev/null
+++ b/connector/nfsplus/nfs_plus_helper.go
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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 nfsplus to mount or unmount filesystem
+package nfsplus
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "path"
+ "strings"
+
+ "huawei-csi-driver/connector"
+ "huawei-csi-driver/connector/nfs"
+ "huawei-csi-driver/csi/backend/plugin"
+ pkgUtils "huawei-csi-driver/pkg/utils"
+ "huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/log"
+)
+
+const (
+ nfsPlusMountCommand = "mount %s %s %s %s"
+ mountPathPermission = 0750
+)
+
+type connectorInfo struct {
+ sourcePath string
+ targetPath string
+ portals []string
+ localAdds string
+ remoteAdds string
+ mntFlags mountParam
+}
+
+type mountParam struct {
+ dashT string
+ dashO string
+}
+
+func tryConnectVolume(ctx context.Context, connMap map[string]interface{}) (string, error) {
+ connInfo, err := parseNFSPlusInfo(ctx, connMap)
+ if err != nil {
+ log.AddContext(ctx).Errorf("parse nfs plus info failed, connMap: %+v err: %v", connMap, err)
+ return "", err
+ }
+
+ err = mountNFSPlus(ctx, connInfo)
+ if err != nil {
+ log.AddContext(ctx).Errorf("mount plus info failed, connMap: %+v err: %v", connInfo, err)
+ return "", err
+ }
+
+ return "", nil
+}
+
+func parseNFSPlusInfo(ctx context.Context, connectionProperties map[string]interface{}) (*connectorInfo, error) {
+ var con connectorInfo
+ sourcePath, srcPathExist := connectionProperties["sourcePath"].(string)
+ if !srcPathExist || sourcePath == "" {
+ return nil, pkgUtils.Errorln(ctx, "there are no source path in the connection info")
+ }
+
+ targetPath, tgtPathExist := connectionProperties["targetPath"].(string)
+ if !tgtPathExist || targetPath == "" {
+ return nil, pkgUtils.Errorln(ctx, "there are no target path in the connection info")
+ }
+
+ portals, portalsExist := connectionProperties["portals"].([]string)
+ if !portalsExist || len(portals) == 0 {
+ return nil, pkgUtils.Errorln(ctx, "there are no portals in the connection info")
+ }
+
+ // format mount flags : remoteAdds
+ con.remoteAdds = strings.Join(portals, "~")
+ // format mount flags : mountFlag
+ mountFlags, _ := connectionProperties["mountFlags"].(string)
+ mountFlagsArr := make([]string, 0)
+ mountFlagsArr = append(mountFlagsArr, fmt.Sprintf("remoteaddrs=%s", con.remoteAdds))
+ mountFlagsArr = append(mountFlagsArr, mountFlags)
+
+ con.sourcePath = sourcePath
+ con.targetPath = targetPath
+ con.mntFlags = mountParam{dashO: strings.TrimSpace(strings.Join(mountFlagsArr, ",")), dashT: plugin.ProtocolNfs}
+
+ log.AddContext(ctx).Infof("parseNFSPlusInfo success, data: %+v", con)
+ return &con, nil
+}
+
+func checkMountPath(ctx context.Context, targetPath string) error {
+ if _, err := os.Stat(targetPath); err != nil && os.IsNotExist(err) {
+ if err := os.MkdirAll(targetPath, mountPathPermission); err != nil {
+ return pkgUtils.Errorln(ctx, "can not create a target path")
+ }
+ }
+
+ return nil
+}
+
+func mountNFSPlus(ctx context.Context, conn *connectorInfo) error {
+ var output string
+ var err error
+ err = checkMountPath(ctx, conn.targetPath)
+ if err != nil {
+ return pkgUtils.Errorf(ctx, "check mount path failed, err: %v", err)
+ }
+
+ mountMap, err := connector.ReadMountPoints(ctx)
+ if err != nil {
+ return pkgUtils.Errorf(ctx, "get mount point map failed, err: %v", err)
+ }
+
+ value, exist := mountMap[conn.targetPath]
+ if exist {
+ // check the filesystem by comparing the sourcePath and mountPath
+ if value == conn.sourcePath || path.Base(path.Dir(conn.targetPath)) == path.Base(path.Dir(conn.sourcePath)) ||
+ nfs.ContainSourceDevice(ctx, conn.sourcePath, value) {
+ log.AddContext(ctx).Infof("Mount %s to %s is already exist", conn.sourcePath, conn.targetPath)
+ return nil
+ }
+
+ return pkgUtils.Errorf(ctx, "The mount %s is already exist, source: %s realSource: %s",
+ conn.targetPath, conn.sourcePath, value)
+ }
+
+ if conn.mntFlags.dashT != "" {
+ conn.mntFlags.dashT = fmt.Sprintf("-t %s", conn.mntFlags.dashT)
+ }
+ if conn.mntFlags.dashO != "" {
+ conn.mntFlags.dashO = fmt.Sprintf("-o %s", conn.mntFlags.dashO)
+ }
+
+ output, err = utils.ExecShellCmd(ctx, fmt.Sprintf(nfsPlusMountCommand, conn.mntFlags.dashT, conn.mntFlags.dashO,
+ conn.sourcePath, conn.targetPath))
+ if err != nil {
+ log.AddContext(ctx).Errorf("Mount %s to %s failed, error res: %s, error: %s",
+ conn.sourcePath, conn.targetPath, output, err)
+ return err
+ }
+
+ return nil
+}
+
+func tryDisConnectVolume(ctx context.Context, targetPath string) error {
+ err := unmountUnix(ctx, targetPath)
+ if err != nil {
+ return err
+ }
+
+ return removeTargetPath(targetPath)
+}
+
+func unmountUnix(ctx context.Context, targetPath string) error {
+ _, err := os.Stat(targetPath)
+ if err != nil && os.IsNotExist(err) {
+ return nil
+ }
+
+ output, err := utils.ExecShellCmd(ctx, "umount %s", targetPath)
+ if err != nil && !(strings.Contains(output, "not mounted") ||
+ strings.Contains(output, "not found")) {
+ log.AddContext(ctx).Errorf("Unmount %s error: %s", targetPath, output)
+ return err
+ }
+
+ return nil
+}
+
+func removeTargetPath(targetPath string) error {
+ _, err := os.Stat(targetPath)
+ if err != nil && os.IsNotExist(err) {
+ return nil
+ }
+
+ if err != nil && !os.IsNotExist(err) {
+ msg := fmt.Sprintf("get target path %s state error %v", targetPath, err)
+ log.Errorln(msg)
+ return errors.New(msg)
+ }
+
+ if err := os.RemoveAll(targetPath); err != nil {
+ msg := fmt.Sprintf("remove target path %s error %v", targetPath, err)
+ log.Errorln(msg)
+ return errors.New(msg)
+ }
+ return nil
+}
diff --git a/connector/nvme/nvme.go b/connector/nvme/nvme.go
index 8ab9a891..53270309 100644
--- a/connector/nvme/nvme.go
+++ b/connector/nvme/nvme.go
@@ -25,7 +25,7 @@ import (
"huawei-csi-driver/utils/log"
)
-// FCNVMe implements the Connector interface for FCNVMe protocol
+// FCNVMe implements the connector.VolumeConnector for FCNVMe protocol
type FCNVMe struct {
mutex sync.Mutex
}
@@ -36,8 +36,9 @@ func init() {
// ConnectVolume to mount the source to target path, the source path can be block or nfs
// Example:
-// mount /dev/sdb /
-// mount /
+//
+// mount /dev/sdb /
+// mount /
func (fc *FCNVMe) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
log.AddContext(ctx).Infof("FC-NVMe Start to connect volume ==> connect info: %v", conn)
tgtLunGuid, exist := conn["tgtLunGuid"].(string)
diff --git a/connector/nvme/nvme_helper.go b/connector/nvme/nvme_helper.go
index d5ffdfd2..d5bf8752 100644
--- a/connector/nvme/nvme_helper.go
+++ b/connector/nvme/nvme_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -28,7 +28,7 @@ import (
"huawei-csi-driver/utils/log"
)
-var flushTimeInterval = 3 * time.Second
+const flushTimeInterval = 3 * time.Second
// PortWWNPair contains initiator wwn and target wwn
type PortWWNPair struct {
diff --git a/connector/nvme/nvme_test.go b/connector/nvme/nvme_test.go
index bb698e81..19272a30 100644
--- a/connector/nvme/nvme_test.go
+++ b/connector/nvme/nvme_test.go
@@ -152,7 +152,8 @@ func TestDisConnectVolume(t *testing.T) {
stubs.StubFunc(&connector.GetNVMePhysicalDevices, []string{}, nil)
stubs.StubFunc(&connector.RemoveAllDevice, "test", nil)
stubs.StubFunc(&connector.FlushDMDevice, nil)
- stubs.Stub(&flushTimeInterval, time.Microsecond)
+ var interval = flushTimeInterval
+ stubs.Stub(&interval, time.Microsecond)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/connector/roce/roce.go b/connector/roce/roce.go
index 12d07e2c..abd694f7 100644
--- a/connector/roce/roce.go
+++ b/connector/roce/roce.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -24,8 +24,8 @@ import (
"huawei-csi-driver/utils/log"
)
-// RoCE implements the Connector interface for RoCE protocol
-type RoCE struct {
+// Connector implements the connector.VolumeConnector for Connector protocol
+type Connector struct {
}
const (
@@ -34,14 +34,15 @@ const (
)
func init() {
- connector.RegisterConnector(connector.RoCEDriver, &RoCE{})
+ connector.RegisterConnector(connector.RoCEDriver, &Connector{})
}
// ConnectVolume to mount the source to target path, the source path can be block or nfs
// Example:
-// mount /dev/sdb /
-// mount /
-func (roce *RoCE) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
+//
+// mount /dev/sdb /
+// mount /
+func (roce *Connector) ConnectVolume(ctx context.Context, conn map[string]interface{}) (string, error) {
log.AddContext(ctx).Infof("RoCE Start to connect volume ==> connect info: %v", conn)
tgtLunGUID, exist := conn["tgtLunGuid"].(string)
if !exist {
@@ -51,7 +52,7 @@ func (roce *RoCE) ConnectVolume(ctx context.Context, conn map[string]interface{}
}
// DisConnectVolume to unmount the target path
-func (roce *RoCE) DisConnectVolume(ctx context.Context, tgtLunGuid string) error {
+func (roce *Connector) DisConnectVolume(ctx context.Context, tgtLunGuid string) error {
log.AddContext(ctx).Infof("RoCE Start to disconnect volume ==> Volume Guid info: %v", tgtLunGuid)
return connector.DisConnectVolumeCommon(ctx, tgtLunGuid, connector.RoCEDriver, tryDisConnectVolume)
}
diff --git a/connector/roce/roce_common.go b/connector/roce/roce_common.go
index 4753ca63..40af1312 100644
--- a/connector/roce/roce_common.go
+++ b/connector/roce/roce_common.go
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// Package roce provide the way to connect/disconnect volume within NVMe over RoCE protocol
+// Package roce provide the way to connect/disconnect volume within NVMe over Connector protocol
package roce
import (
@@ -69,7 +69,7 @@ func disconnectSessions(ctx context.Context, sessionPorts []string) error {
"'{if($1>1) print 1; else print 0}'", nvmePort)
output, err := utils.ExecShellCmd(ctx, cmd)
if err != nil {
- return utils.Errorf(ctx, "Disconnect RoCE target path %s failed, err: %v", nvmePort, err)
+ return utils.Errorf(ctx, "Disconnect Connector target path %s failed, err: %v", nvmePort, err)
}
outputSplit := strings.Split(output, "\n")
diff --git a/connector/roce/roce_constants.go b/connector/roce/roce_constants.go
index eeb80e0b..c488adc0 100644
--- a/connector/roce/roce_constants.go
+++ b/connector/roce/roce_constants.go
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// Package roce provide the way to connect/disconnect volume within NVMe over RoCE protocol
+// Package roce provide the way to connect/disconnect volume within NVMe over Connector protocol
package roce
const sleepInternal = 2
diff --git a/connector/roce/roce_helper.go b/connector/roce/roce_helper.go
index f7a5ee8c..bfbbea91 100644
--- a/connector/roce/roce_helper.go
+++ b/connector/roce/roce_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -24,11 +24,13 @@ import (
"runtime/debug"
"strings"
"sync"
+ "sync/atomic"
"time"
"huawei-csi-driver/connector"
connutils "huawei-csi-driver/connector/utils"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/concurrent"
"huawei-csi-driver/utils/log"
)
@@ -40,16 +42,17 @@ type connectorInfo struct {
}
type shareData struct {
- stopConnecting bool
- numLogin int64
- failedLogin int64
- stoppedThreads int64
- foundDevices []string
- justAddedDevices []string
+ stopConnecting atomic.Bool
+ numLogin atomic.Int64
+ failedLogin atomic.Int64
+ stoppedThreads atomic.Int64
+ foundDevices concurrent.Slice[string]
+ justAddedDevices concurrent.Slice[string]
}
const (
- connectTimeOut = 15
+ connectTimeOut = 15
+ subNqnSegmentCount = 2
)
func parseRoCEInfo(ctx context.Context, connectionProperties map[string]interface{}) (connectorInfo, error) {
@@ -97,8 +100,8 @@ func getTargetNQN(ctx context.Context, tgtPortal string) (string, error) {
lines := strings.Split(output, "\n")
for _, line := range lines {
if strings.Contains(line, "subnqn") {
- splits := strings.SplitN(line, ":", 2)
- if len(splits) == 2 && splits[0] == "subnqn" {
+ splits := strings.SplitN(line, ":", subNqnSegmentCount)
+ if len(splits) == subNqnSegmentCount && splits[0] == "subnqn" {
tgtNqn = strings.Trim(splits[1], " ")
break
}
@@ -150,20 +153,20 @@ func connectVol(ctx context.Context,
targetNQN, err := getTargetNQN(ctx, tgtPortal)
if err != nil {
log.AddContext(ctx).Errorf("Cannot discover nvme target %s, reason: %v", tgtPortal, err)
- nvmeShareData.failedLogin += 1
- nvmeShareData.stoppedThreads += 1
+ nvmeShareData.failedLogin.Add(1)
+ nvmeShareData.stoppedThreads.Add(1)
return
}
err = connectRoCEPortal(ctx, existSessions, tgtPortal, targetNQN)
if err != nil {
log.AddContext(ctx).Errorf("connect roce portal %s error, reason: %v", tgtPortal, err)
- nvmeShareData.failedLogin += 1
- nvmeShareData.stoppedThreads += 1
+ nvmeShareData.failedLogin.Add(1)
+ nvmeShareData.stoppedThreads.Add(1)
return
}
- nvmeShareData.numLogin += 1
+ nvmeShareData.numLogin.Add(1)
var device string
for i := 1; i < 4; i++ {
nvmeConnectInfo, err := connector.GetSubSysInfo(ctx)
@@ -177,7 +180,7 @@ func connectVol(ctx context.Context,
log.AddContext(ctx).Errorf("Get device of guid %s error: %v", tgtLunGUID, err)
break
}
- if device != "" || nvmeShareData.stopConnecting {
+ if device != "" || nvmeShareData.stopConnecting.Load() {
break
}
@@ -190,17 +193,18 @@ func connectVol(ctx context.Context,
}
if device != "" {
- nvmeShareData.foundDevices = append(nvmeShareData.foundDevices, device)
- nvmeShareData.justAddedDevices = append(nvmeShareData.justAddedDevices, device)
+ nvmeShareData.foundDevices.Append(device)
+ nvmeShareData.justAddedDevices.Append(device)
}
- nvmeShareData.stoppedThreads += 1
+
+ nvmeShareData.stoppedThreads.Add(1)
return
}
func scanSingle(ctx context.Context, nvmeShareData *shareData) {
log.AddContext(ctx).Infoln("Enter function:scanSingle")
for i := 0; i < 15; i++ {
- if len(nvmeShareData.foundDevices) != 0 {
+ if nvmeShareData.foundDevices.Len() != 0 {
break
}
time.Sleep(time.Second * intNumTwo)
@@ -285,7 +289,7 @@ func tryConnectVolume(ctx context.Context, connMap map[string]interface{}) (stri
mPath = scanDevice(ctx, conn, nvmeShareData)
- nvmeShareData.stopConnecting = true
+ nvmeShareData.stopConnecting.Store(true)
wait.Wait()
return verifyDevice(ctx, conn, nvmeShareData, mPath)
@@ -310,7 +314,7 @@ func scanUpNVMeMultiPath(ctx context.Context, conn connectorInfo, nvmeShareData
allThread := int64(len(conn.tgtPortals))
for isThreadNotStoppedOrFoundDevices(allThread, nvmeShareData) &&
isThreadNotFinishedOrDeviceNotObtained(device, allThread, nvmeShareData) {
- if timeout == 0 && len(nvmeShareData.foundDevices) != 0 && nvmeShareData.stoppedThreads == allThread {
+ if timeout == 0 && nvmeShareData.foundDevices.Len() != 0 && nvmeShareData.stoppedThreads.Load() == allThread {
log.AddContext(ctx).Infof("All connection threads finished, "+
"giving %d seconds for ultrapath* to appear.", connectTimeOut)
timeout = time.Now().Unix() + connectTimeOut
@@ -330,11 +334,11 @@ func scanUpNVMeMultiPath(ctx context.Context, conn connectorInfo, nvmeShareData
}
func isThreadNotStoppedOrFoundDevices(allThread int64, nvmeShareData *shareData) bool {
- return nvmeShareData.stoppedThreads != allThread || len(nvmeShareData.foundDevices) != 0
+ return nvmeShareData.stoppedThreads.Load() != allThread || nvmeShareData.foundDevices.Len() != 0
}
func isThreadNotFinishedOrDeviceNotObtained(device string, allThread int64, nvmeShareData *shareData) bool {
- return nvmeShareData.numLogin+nvmeShareData.failedLogin != allThread || device == ""
+ return nvmeShareData.numLogin.Load()+nvmeShareData.failedLogin.Load() != allThread || device == ""
}
func verifyDevice(ctx context.Context,
@@ -342,12 +346,12 @@ func verifyDevice(ctx context.Context,
nvmeShareData *shareData,
mPath string) (string, error) {
log.AddContext(ctx).Infof("Enter function:verifyDevice, mPath:%s", mPath)
- if nvmeShareData.foundDevices == nil {
+ if nvmeShareData.foundDevices.Values() == nil {
return "", utils.Errorf(ctx, connector.VolumeDeviceNotFound)
}
if !conn.volumeUseMultiPath {
- device := fmt.Sprintf("/dev/%s", nvmeShareData.foundDevices[0])
+ device := fmt.Sprintf("/dev/%s", nvmeShareData.foundDevices.Get(0))
err := connector.VerifySingleDevice(ctx, device, conn.tgtLunGUID,
connector.VolumeDeviceNotFound, tryDisConnectVolume)
if err != nil {
diff --git a/connector/roce/roce_models.go b/connector/roce/roce_models.go
index 801416a1..61cab8f8 100644
--- a/connector/roce/roce_models.go
+++ b/connector/roce/roce_models.go
@@ -14,5 +14,5 @@
* limitations under the License.
*/
-// Package roce provide the way to connect/disconnect volume within NVMe over RoCE protocol
+// Package roce provide the way to connect/disconnect volume within NVMe over Connector protocol
package roce
diff --git a/connector/roce/roce_ultrapath_nvme_helper.go b/connector/roce/roce_ultrapath_nvme_helper.go
index 801416a1..61cab8f8 100644
--- a/connector/roce/roce_ultrapath_nvme_helper.go
+++ b/connector/roce/roce_ultrapath_nvme_helper.go
@@ -14,5 +14,5 @@
* limitations under the License.
*/
-// Package roce provide the way to connect/disconnect volume within NVMe over RoCE protocol
+// Package roce provide the way to connect/disconnect volume within NVMe over Connector protocol
package roce
diff --git a/connector/ultrapath.go b/connector/ultrapath.go
index cb998ed6..aec73a0f 100644
--- a/connector/ultrapath.go
+++ b/connector/ultrapath.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -34,6 +34,15 @@ const (
nxupLunMapFile = "/proc/nxup_lun_map_a"
nxupLunMapFileWithUpgrade = "/proc/nxup_lun_map_b"
deviceDelete = "deleted"
+ waitingDeletePeriod = 5 * time.Second
+
+ splitSegment = 5
+ hctlIndex = 3
+ hctlLength = 4
+ lunIdKeyIndex = 2
+ lunIdIndex = 3
+ devTupleKeyIndex = 2
+ devTupleNameIndex = 4
)
type ultrapathDeviceInfo struct {
@@ -101,7 +110,7 @@ func CleanDeviceByLunId(ctx context.Context, lunId string, targets []string) err
if needCleanDevice.deviceName == deviceDelete {
log.AddContext(ctx).Infoln("device name deleted, waiting 5s")
- time.Sleep(5 * time.Second)
+ time.Sleep(waitingDeletePeriod)
}
return nil
@@ -178,12 +187,14 @@ func parseDevice(line string, lunId string, deviceHctlMap map[string][]string,
if deviceTupleMap == nil {
return
}
+
splitValue := strings.Split(line, "=")
- if len(splitValue) > 5 {
- hctl := strings.Split(splitValue[3], ":")
- if len(hctl) == 4 && hctl[3] == lunId {
- addToMap(deviceHctlMap, splitValue[2], splitValue[3])
- deviceTupleMap[splitValue[2]] = ultrapathDeviceTuple{name: splitValue[4], id: splitValue[1]}
+ if len(splitValue) > splitSegment {
+ hctl := strings.Split(splitValue[hctlIndex], ":")
+ if len(hctl) == hctlLength && hctl[lunIdIndex] == lunId {
+ addToMap(deviceHctlMap, splitValue[lunIdKeyIndex], splitValue[lunIdIndex])
+ deviceTupleMap[splitValue[devTupleKeyIndex]] = ultrapathDeviceTuple{
+ name: splitValue[devTupleNameIndex], id: splitValue[1]}
}
}
}
diff --git a/connector/ultrapath_vlun.go b/connector/ultrapath_vlun.go
new file mode 100644
index 00000000..497ac1bb
--- /dev/null
+++ b/connector/ultrapath_vlun.go
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. 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 connector
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "strings"
+ "time"
+
+ "huawei-csi-driver/utils/log"
+)
+
+const (
+ fieldsLengthToGetVLun = 5
+ diskNameDeleted = "deleted"
+)
+
+const (
+ idIndex = iota
+ diskIndex
+ nameIndex
+ wwnIndex
+ statusIndex
+)
+
+// reHctl is the regex to find the physical device, for example "6:0:0:2".
+var reHctl = regexp.MustCompile("\\d:\\d:\\d:\\d")
+
+// UltrapathVLun is the vLun of ultrapath.
+type UltrapathVLun struct {
+ ID string
+ Disk string
+ Name string
+ WWN string
+ Status string
+ upType string
+}
+
+// GetUltrapathVLunByWWN gets the ultraPath vLun by the WWN of device.
+func GetUltrapathVLunByWWN(ctx context.Context, upType, wwn string) (*UltrapathVLun, error) {
+ res, err := GetUltraPathInfoByDevName(ctx, upType, wwn)
+ if err != nil {
+ if err.Error() == exitStatus1 && res == "" {
+ return nil, nil
+ }
+
+ return nil, err
+ }
+
+ fields := strings.Fields(res)
+ if len(fields) < fieldsLengthToGetVLun {
+ return nil, fmt.Errorf("want [%d] fields to get vLun, but got [%d]", fieldsLengthToGetVLun, len(fields))
+ }
+
+ return &UltrapathVLun{
+ ID: fields[idIndex],
+ Disk: fields[diskIndex],
+ Name: fields[nameIndex],
+ WWN: fields[wwnIndex],
+ Status: fields[statusIndex],
+ upType: upType,
+ }, nil
+}
+
+// CleanResidualPath clean residual path of vLun.
+func (vLun *UltrapathVLun) CleanResidualPath(ctx context.Context) error {
+ if vLun.Disk != diskNameDeleted {
+ return nil
+ }
+
+ log.AddContext(ctx).Infof("the disk [%s] status is [%s], need to clean", vLun.Disk, vLun.Status)
+ if err := vLun.cleanPhysicalPaths(ctx); err != nil {
+ return err
+ }
+
+ log.AddContext(ctx).Infoln("device deleted, waiting 5s")
+ time.Sleep(waitingDeletePeriod)
+
+ return nil
+}
+
+func (vLun *UltrapathVLun) cleanPhysicalPaths(ctx context.Context) error {
+ paths, err := vLun.getPhysicalPaths(ctx)
+ if err != nil {
+ log.AddContext(ctx).Warningf("get physical paths failed, error: %v", err)
+ return err
+ }
+
+ for _, p := range paths {
+ if err := p.clean(ctx); err != nil {
+ log.AddContext(ctx).Warningf("clean path [%s] failed, error: %v", p.hctl, err)
+ }
+ }
+
+ return nil
+}
+
+func (vLun *UltrapathVLun) getPhysicalPaths(ctx context.Context) ([]physicalPath, error) {
+ output, err := runUpCommand(ctx, vLun.upType, "show vlun id=%s | grep -w Path", vLun.ID)
+ if err != nil {
+ return nil, err
+ }
+
+ var physicalPaths []physicalPath
+ for _, subPath := range strings.Split(output, "\n") {
+ if strings.TrimSpace(subPath) == "" {
+ continue
+ }
+
+ physicalPaths = append(physicalPaths, physicalPath{
+ hctl: reHctl.FindString(subPath),
+ status: getStatusFromPathResult(subPath),
+ })
+ }
+
+ return physicalPaths, nil
+}
+
+type physicalPath struct {
+ hctl string
+ status string
+}
+
+func (p physicalPath) clean(ctx context.Context) error {
+ log.AddContext(ctx).Infof("start to delete physical device [%s], status is [%s]", p.hctl, p.status)
+ if err := deletePhysicalDevice(ctx, p.hctl); err != nil {
+ log.AddContext(ctx).Warningf("delete physical device [%s] failed, error is %v", p.hctl, err)
+ }
+
+ return nil
+}
+
+func getStatusFromPathResult(result string) string {
+ segments := strings.Split(result, ":")
+ if len(segments) > 0 {
+ return strings.TrimSpace(segments[len(segments)-1])
+ }
+
+ return ""
+}
diff --git a/connector/ultrapath_vlun_test.go b/connector/ultrapath_vlun_test.go
new file mode 100644
index 00000000..74272f96
--- /dev/null
+++ b/connector/ultrapath_vlun_test.go
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. 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 connector
+
+import (
+ "context"
+ "errors"
+ "testing"
+
+ "github.com/agiledragon/gomonkey/v2"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetUltraPathVLunByWWN(t *testing.T) {
+ ctx := context.Background()
+ wwn := "testWwn"
+
+ t.Run("success", func(t *testing.T) {
+ // arrange
+ commandResult := " 1 deleted testDisk testWwn Fault 1.00GB " +
+ " 0B/0B Huawei.Storage 61 0/2"
+ expectedVLun := &UltrapathVLun{
+ ID: "1",
+ Disk: "deleted",
+ Name: "testDisk",
+ WWN: "testWwn",
+ Status: "Fault",
+ upType: UltraPathCommand,
+ }
+
+ // mock
+ //runUpCommand()
+ gomonkey.ApplyFuncReturn(runUpCommand, commandResult, nil)
+
+ // action
+ vLun, err := GetUltrapathVLunByWWN(ctx, UltraPathCommand, wwn)
+
+ // assert
+ require.NoError(t, err)
+ require.Equal(t, expectedVLun, vLun)
+ })
+
+ t.Run("no residual path", func(t *testing.T) {
+ // arrange
+
+ // mock
+ gomonkey.ApplyFuncReturn(runUpCommand, nil, errors.New(exitStatus1))
+
+ // action
+ vLun, err := GetUltrapathVLunByWWN(ctx, UltraPathCommand, "testWwn")
+
+ // assert
+ require.NoError(t, err)
+ require.Nil(t, vLun)
+ })
+}
+
+func TestUltraPathVLun_CleanResidualPath(t *testing.T) {
+ t.Run("doesn't need to clean", func(t *testing.T) {
+ // arrange
+ vLun := &UltrapathVLun{
+ ID: "1",
+ Disk: "sda",
+ Name: "testDisk",
+ WWN: "testWwn",
+ Status: diskStatusNormal,
+ upType: UltraPathCommand,
+ }
+
+ // action
+ err := vLun.CleanResidualPath(context.Background())
+
+ // assert
+ require.NoError(t, err)
+ })
+
+ t.Run("clean successfully", func(t *testing.T) {
+ // arrange
+ vLun := &UltrapathVLun{
+ ID: "1",
+ Disk: "deleted",
+ Name: "testDisk",
+ WWN: "testWwn",
+ Status: "Fault",
+ upType: UltraPathCommand,
+ }
+ vLunDetails := "Path 62 [7:0:0:2] (up-270) : Fault\nPath 63 [6:0:0:2] (up-269) : Fault"
+ expectedPhyDevices := []string{"7:0:0:2", "6:0:0:2"}
+ var actualPhyDevices []string
+
+ // mock
+ gomonkey.ApplyFuncReturn(runUpCommand, vLunDetails, nil)
+ gomonkey.ApplyFunc(deletePhysicalDevice, func(ctx context.Context, phyDevice string) error {
+ actualPhyDevices = append(actualPhyDevices, phyDevice)
+ return nil
+ })
+
+ // action
+ err := vLun.CleanResidualPath(context.Background())
+
+ // assert
+ require.NoError(t, err)
+ require.Equal(t, expectedPhyDevices, actualPhyDevices)
+ })
+
+ t.Run("clean physical paths", func(t *testing.T) {
+ // arrange
+ vLun := &UltrapathVLun{
+ ID: "1",
+ Disk: "deleted",
+ Name: "testDisk",
+ WWN: "testWwn",
+ Status: "Fault",
+ upType: UltraPathCommand,
+ }
+ vLunDetails := "Path 62 [7:0:0:2] (up-270) : Fault\nPath 63 [6:0:0:2] (up-269) : Normal\n "
+ expectedPhyDevices := []string{"7:0:0:2", "6:0:0:2"}
+ var actualPhyDevices []string
+
+ // mock
+ gomonkey.ApplyFuncReturn(runUpCommand, vLunDetails, nil)
+ gomonkey.ApplyFunc(deletePhysicalDevice, func(ctx context.Context, phyDevice string) error {
+ actualPhyDevices = append(actualPhyDevices, phyDevice)
+ return nil
+ })
+
+ // action
+ err := vLun.CleanResidualPath(context.Background())
+
+ // assert
+ require.NoError(t, err)
+ require.Equal(t, expectedPhyDevices, actualPhyDevices)
+ })
+}
diff --git a/connector/utils/lock/lock.go b/connector/utils/lock/lock.go
index 8b63b1c9..7b645b20 100644
--- a/connector/utils/lock/lock.go
+++ b/connector/utils/lock/lock.go
@@ -21,7 +21,6 @@ import (
"context"
"fmt"
"path/filepath"
- "sync"
"time"
"huawei-csi-driver/csi/app"
@@ -29,18 +28,9 @@ import (
"huawei-csi-driver/utils/log"
)
-// Lock is a special lock interface provided for disk management
-type Lock interface {
- SyncLock(lockName string)
-
- SyncUnlock(lockName string)
-}
-
var (
- curDriverName string
- lockMutex sync.Mutex
- semaphoreMap map[string]*utils.Semaphore
- lockDir = fmt.Sprintf("%s/lock/", lockDirPrefix)
+ semaphoreMap map[string]*utils.Semaphore
+ dir = fmt.Sprintf("%s/lock/", lockDirPrefix)
)
const (
@@ -64,7 +54,7 @@ const (
// InitLock provide three semaphores for device connect, disconnect and expand
func InitLock(driverName string) error {
- err := checkLockPath(lockDir)
+ err := checkLockPath(dir)
if err != nil {
return err
}
@@ -74,7 +64,6 @@ func InitLock(driverName string) error {
disConnectVolume: utils.NewSemaphore(app.GetGlobalConfig().ConnectorThreads),
extendVolume: utils.NewSemaphore(app.GetGlobalConfig().ConnectorThreads),
}
- curDriverName = driverName
log.Infoln("Init lock success.")
return nil
}
@@ -83,19 +72,19 @@ func InitLock(driverName string) error {
func SyncLock(ctx context.Context, lockName, operationType string) error {
startTime := time.Now()
- err := createLockDir(filepath.Dir(lockDir))
+ err := createLockDir(filepath.Dir(dir))
if err != nil {
return fmt.Errorf("create dir failed, reason: %s", err)
}
- err = waitGetLock(ctx, lockDir, lockName)
+ err = waitGetLock(ctx, dir, lockName)
if err != nil {
return err
}
err = waitGetSemaphore(ctx, operationType)
if err != nil {
- newErr := deleteLockFile(ctx, lockDir, lockName)
+ newErr := deleteLockFile(ctx, dir, lockName)
if newErr != nil {
log.AddContext(ctx).Errorln("new error occurred when delete lock file")
return newErr
@@ -112,7 +101,7 @@ func SyncUnlock(ctx context.Context, lockName, operationType string) error {
startTime := time.Now()
releaseSemaphore(ctx, operationType)
- err := deleteLockFile(ctx, lockDir, lockName)
+ err := deleteLockFile(ctx, dir, lockName)
if err != nil {
return err
}
diff --git a/connector/utils/lock/lock_utils.go b/connector/utils/lock/lock_utils.go
index 51d94d81..bb97f72e 100644
--- a/connector/utils/lock/lock_utils.go
+++ b/connector/utils/lock/lock_utils.go
@@ -24,6 +24,7 @@ import (
"os"
"path/filepath"
"strings"
+ "sync"
"time"
pkgUtils "huawei-csi-driver/pkg/utils"
@@ -31,6 +32,8 @@ import (
"huawei-csi-driver/utils/log"
)
+var mutex sync.Mutex
+
func clearLockFile(fileDir string) error {
files, err := ioutil.ReadDir(fileDir)
if err != nil {
@@ -106,8 +109,8 @@ func createLockFile(ctx context.Context, filePath, lockName string) error {
func deleteLockFile(ctx context.Context, lockDir, lockName string) error {
log.AddContext(ctx).Infoln("DeleteLockFile start to get lock")
- lockMutex.Lock()
- defer lockMutex.Unlock()
+ mutex.Lock()
+ defer mutex.Unlock()
log.AddContext(ctx).Infoln("DeleteLockFile finish to get lock")
filePath := fmt.Sprintf("%s%s%s", lockDir, lockNamePrefix, lockName)
exist := isFileExist(filePath)
@@ -122,8 +125,8 @@ func waitGetLock(ctx context.Context, lockDir, lockName string) error {
filePath := fmt.Sprintf("%s%s%s", lockDir, lockNamePrefix, lockName)
log.AddContext(ctx).Infoln("WaitGetLock start to get lock")
err := utils.WaitUntil(func() (bool, error) {
- lockMutex.Lock()
- defer lockMutex.Unlock()
+ mutex.Lock()
+ defer mutex.Unlock()
exist := isFileExist(filePath)
if !exist {
diff --git a/csi/app/config/config.go b/csi/app/config/config.go
index 275af9f2..2c167eeb 100644
--- a/csi/app/config/config.go
+++ b/csi/app/config/config.go
@@ -37,7 +37,6 @@ type loggingConfig struct {
type serviceConfig struct {
Controller bool
EnableLeaderElection bool
- EnableLabel bool
Endpoint string
DrEndpoint string
@@ -93,8 +92,8 @@ type extenderConfig struct {
VolumeModifyReconcileDelay time.Duration
}
-// Config contains the configurations from env
-type Config struct {
+// AppConfig contains the configurations from env
+type AppConfig struct {
loggingConfig
serviceConfig
connectorConfig
@@ -104,13 +103,13 @@ type Config struct {
// CompletedConfig contains the env and config
type CompletedConfig struct {
- *Config
+ *AppConfig
K8sUtils k8sutils.Interface
BackendUtils clientSet.Interface
}
-// Complete the Config and return the CompletedConfig
-func (cfg *Config) Complete() (*CompletedConfig, error) {
+// Complete the AppConfig and return the CompletedConfig
+func (cfg *AppConfig) Complete() (*CompletedConfig, error) {
k8sUtils, err := k8sutils.NewK8SUtils(cfg.KubeConfig, cfg.VolumeNamePrefix,
map[string]string{"provisioner": cfg.DriverName})
if err != nil {
@@ -125,7 +124,7 @@ func (cfg *Config) Complete() (*CompletedConfig, error) {
}
return &CompletedConfig{
- Config: cfg,
+ AppConfig: cfg,
K8sUtils: k8sUtils,
BackendUtils: backendUtils,
}, nil
@@ -133,5 +132,5 @@ func (cfg *Config) Complete() (*CompletedConfig, error) {
// Print the configuration when before the service
func (cfg *CompletedConfig) Print() {
- logrus.Infof("Controller manager config %+v", cfg.Config)
+ logrus.Infof("Controller manager config %+v", cfg.AppConfig)
}
diff --git a/csi/app/config/config_mock.go b/csi/app/config/config_mock.go
index 93a065f9..d21a8ced 100644
--- a/csi/app/config/config_mock.go
+++ b/csi/app/config/config_mock.go
@@ -34,7 +34,7 @@ const (
// MockCompletedConfig for unit test
func MockCompletedConfig() *CompletedConfig {
return &CompletedConfig{
- Config: &Config{
+ AppConfig: &AppConfig{
mockLoggingConfig(),
mockServiceConfig(),
mockConnectorConfig(),
@@ -60,7 +60,6 @@ func mockServiceConfig() serviceConfig {
return serviceConfig{
Controller: false,
EnableLeaderElection: false,
- EnableLabel: false,
Endpoint: "",
DrEndpoint: "",
diff --git a/csi/app/options/connector.go b/csi/app/options/connector.go
index df376722..d38ac079 100644
--- a/csi/app/options/connector.go
+++ b/csi/app/options/connector.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -29,12 +29,19 @@ const (
hwUltraPath = "HW-UltraPath"
hwUltraPathNVMe = "HW-UltraPath-NVMe"
- defaultCleanupTimeout = 240
- defaultScanVolumeTimeout = 3
- defaultConnectorThreads = 4
+ defaultCleanupTimeout = 240
+ defaultScanVolumeTimeout = 3
+ defaultConnectorThreads = 4
+ defaultExecCommandTimeout = 30
minThreads = 1
maxThreads = 10
+
+ minScanVolumeTimeout = 1
+ maxScanVolumeTimeout = 600
+
+ minExecCommandTimeout = 1
+ maxExecCommandTimeout = 600
)
type connectorOptions struct {
@@ -73,24 +80,24 @@ func (opt *connectorOptions) AddFlags(ff *flag.FlagSet) {
hwUltraPathNVMe,
"Multipath software for roce/fc-nvme block volumes")
ff.IntVar(&opt.deviceCleanupTimeout, "deviceCleanupTimeout",
- 240,
+ defaultCleanupTimeout,
"Timeout interval in seconds for stale device cleanup")
ff.IntVar(&opt.scanVolumeTimeout, "scan-volume-timeout",
- 3,
+ defaultScanVolumeTimeout,
"The timeout for waiting for multipath aggregation when DM-multipath is used on the host")
ff.IntVar(&opt.connectorThreads, "connector-threads",
- 4,
+ defaultConnectorThreads,
"The concurrency supported during disk operations.")
ff.BoolVar(&opt.allPathOnline, "all-path-online",
false,
"Whether to check the number of online paths for DM-multipath aggregation, default false")
ff.IntVar(&opt.execCommandTimeout, "exec-command-timeout",
- 30,
+ defaultExecCommandTimeout,
"The timeout for running command on host")
}
// ApplyFlags assign the connector flags
-func (opt *connectorOptions) ApplyFlags(cfg *config.Config) {
+func (opt *connectorOptions) ApplyFlags(cfg *config.AppConfig) {
cfg.VolumeUseMultiPath = opt.volumeUseMultiPath
cfg.ScsiMultiPathType = opt.scsiMultiPathType
cfg.NvmeMultiPathType = opt.nvmeMultiPathType
@@ -141,7 +148,7 @@ func (opt *connectorOptions) validateNvmeMultiPathType() error {
}
func (opt *connectorOptions) validateScanVolumeTimeout() error {
- if opt.scanVolumeTimeout < 1 || opt.scanVolumeTimeout > 600 {
+ if opt.scanVolumeTimeout < minScanVolumeTimeout || opt.scanVolumeTimeout > maxScanVolumeTimeout {
return fmt.Errorf("the value of scanVolumeTimeout ranges from 1 to 600, current is: %d",
opt.scanVolumeTimeout)
}
@@ -156,7 +163,7 @@ func (opt *connectorOptions) validateConnectorThreads() error {
return nil
}
func (opt *connectorOptions) validateExecCommandTimeout() error {
- if opt.execCommandTimeout < 1 || opt.execCommandTimeout > 600 {
+ if opt.execCommandTimeout < minExecCommandTimeout || opt.execCommandTimeout > maxExecCommandTimeout {
return fmt.Errorf("the value of execCommandTimeout ranges from 1 to 600, current is: %d",
opt.scanVolumeTimeout)
}
diff --git a/csi/app/options/extender.go b/csi/app/options/extender.go
index 92d7aa65..a8b06f9e 100644
--- a/csi/app/options/extender.go
+++ b/csi/app/options/extender.go
@@ -65,7 +65,7 @@ func (opt *extenderOptions) AddFlags(ff *flag.FlagSet) {
}
// ApplyFlags assign the extender flags
-func (opt *extenderOptions) ApplyFlags(cfg *config.Config) {
+func (opt *extenderOptions) ApplyFlags(cfg *config.AppConfig) {
cfg.VolumeModifyRetryBaseDelay = opt.volumeModifyRetryBaseDelay
cfg.VolumeModifyRetryMaxDelay = opt.volumeModifyRetryMaxDelay
cfg.VolumeModifyReconcileDelay = opt.volumeModifyReconcileDelay
diff --git a/csi/app/options/k8s.go b/csi/app/options/k8s.go
index fd61cbd3..24ef5994 100644
--- a/csi/app/options/k8s.go
+++ b/csi/app/options/k8s.go
@@ -44,7 +44,7 @@ func (opt *k8sOptions) AddFlags(ff *flag.FlagSet) {
}
// ApplyFlags assign the connector flags
-func (opt *k8sOptions) ApplyFlags(cfg *config.Config) {
+func (opt *k8sOptions) ApplyFlags(cfg *config.AppConfig) {
cfg.Namespace = opt.namespace
}
diff --git a/csi/app/options/logging.go b/csi/app/options/logging.go
index 72da4955..5ddbfad1 100644
--- a/csi/app/options/logging.go
+++ b/csi/app/options/logging.go
@@ -73,7 +73,7 @@ func (opt *loggingOptions) AddFlags(ff *flag.FlagSet) {
}
// ApplyFlags assign the log flags
-func (opt *loggingOptions) ApplyFlags(cfg *config.Config) {
+func (opt *loggingOptions) ApplyFlags(cfg *config.AppConfig) {
cfg.MaxBackups = opt.maxBackups
cfg.LoggingModule = opt.loggingModule
cfg.LogFileDir = opt.logFileDir
diff --git a/csi/app/options/options.go b/csi/app/options/options.go
index a3c6a35d..9991c51c 100644
--- a/csi/app/options/options.go
+++ b/csi/app/options/options.go
@@ -54,7 +54,7 @@ func (opt *optionsManager) AddFlags(ff *flag.FlagSet) {
}
// ApplyFlags assign the flags
-func (opt *optionsManager) ApplyFlags(cfg *config.Config) {
+func (opt *optionsManager) ApplyFlags(cfg *config.AppConfig) {
opt.logOption.ApplyFlags(cfg)
opt.connectorOption.ApplyFlags(cfg)
opt.serviceOption.ApplyFlags(cfg)
@@ -81,12 +81,12 @@ func (opt *optionsManager) ValidateFlags() error {
}
// Config set all configuration
-func (opt *optionsManager) Config() (*config.Config, error) {
+func (opt *optionsManager) Config() (*config.AppConfig, error) {
if err := opt.ValidateFlags(); err != nil {
return nil, err
}
- cfg := config.Config{}
+ cfg := config.AppConfig{}
opt.ApplyFlags(&cfg)
return &cfg, nil
}
diff --git a/csi/app/options/options_test.go b/csi/app/options/options_test.go
index 0a3d6bc4..1fc2ce86 100644
--- a/csi/app/options/options_test.go
+++ b/csi/app/options/options_test.go
@@ -63,7 +63,7 @@ func TestConfig(t *testing.T) {
}
}
-func compareLogOptions(envCfg *config.Config) error {
+func compareLogOptions(envCfg *config.AppConfig) error {
expectLogOptions := NewLoggingOptions()
actuallyLogOptions := &loggingOptions{
logFileSize: envCfg.LogFileSize,
@@ -80,7 +80,7 @@ func compareLogOptions(envCfg *config.Config) error {
return nil
}
-func compareConnectorOptions(envCfg *config.Config) error {
+func compareConnectorOptions(envCfg *config.AppConfig) error {
expectConnectorOptions := NewConnectorOptions()
actuallyConnectorOptions := &connectorOptions{
volumeUseMultiPath: true,
diff --git a/csi/app/options/service.go b/csi/app/options/service.go
index 5c6c125a..4cede849 100644
--- a/csi/app/options/service.go
+++ b/csi/app/options/service.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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,11 +26,20 @@ import (
"huawei-csi-driver/pkg/constants"
)
+const (
+ defaultRpcTimeout = 1 * time.Minute
+ defaultWorkerThreads = 10
+ defaultReSyncPeriods = 2 * time.Minute
+ defaultLeaderRetryPeriod = 2 * time.Second
+ defaultLeaderRenewDeadline = 6 * time.Second
+ defaultLeaderLeaseDuration = 8 * time.Second
+ defaultBackendUpdateIntervalSeconds = 60
+)
+
// serviceOptions include service's configuration
type serviceOptions struct {
controller bool
enableLeaderElection bool
- enableLabel bool
driverName string
endpoint string
@@ -72,8 +81,8 @@ func (opt *serviceOptions) AddFlags(ff *flag.FlagSet) {
ff.StringVar(&opt.driverName, "driver-name",
constants.DefaultDriverName,
"CSI driver name")
- ff.IntVar(&opt.backendUpdateInterval, "backend-update-interval",
- 60, "The interval seconds to update backends status. Default is 60 seconds")
+ ff.IntVar(&opt.backendUpdateInterval, "backend-update-interval", defaultBackendUpdateIntervalSeconds,
+ "The interval seconds to update backends status. Default is 60 seconds")
ff.StringVar(&opt.kubeConfig, "kubeconfig", "",
"absolute path to the kubeconfig file")
ff.StringVar(&opt.nodeName, "nodename",
@@ -89,28 +98,25 @@ func (opt *serviceOptions) AddFlags(ff *flag.FlagSet) {
"The port of webhook server")
ff.StringVar(&opt.webHookAddress, "web-hook-address", "",
"The Address of webhook server")
- ff.BoolVar(&opt.enableLabel, "enable-label", false,
- "csi enable label")
ff.BoolVar(&opt.enableLeaderElection, "enable-leader-election", false,
"backend enable leader election")
- ff.DurationVar(&opt.leaderLeaseDuration, "leader-lease-duration", 8*time.Second,
+ ff.DurationVar(&opt.leaderLeaseDuration, "leader-lease-duration", defaultLeaderLeaseDuration,
"backend leader lease duration")
- ff.DurationVar(&opt.leaderRenewDeadline, "leader-renew-deadline", 6*time.Second,
+ ff.DurationVar(&opt.leaderRenewDeadline, "leader-renew-deadline", defaultLeaderRenewDeadline,
"backend leader renew deadline")
- ff.DurationVar(&opt.leaderRetryPeriod, "leader-retry-period", 2*time.Second,
+ ff.DurationVar(&opt.leaderRetryPeriod, "leader-retry-period", defaultLeaderRetryPeriod,
"backend leader retry period")
- ff.DurationVar(&opt.reSyncPeriod, "re-sync-period", 2*time.Minute, "reSync interval of the controller")
- ff.IntVar(&opt.workerThreads, "worker-threads", 10, "number of worker threads.")
- ff.DurationVar(&opt.timeout, "timeout", 1*time.Minute, "timeout for any RPCs")
+ ff.DurationVar(&opt.reSyncPeriod, "re-sync-period", defaultReSyncPeriods, "reSync interval of the controller")
+ ff.IntVar(&opt.workerThreads, "worker-threads", defaultWorkerThreads, "number of worker threads.")
+ ff.DurationVar(&opt.timeout, "timeout", defaultRpcTimeout, "timeout for any RPCs")
ff.StringVar(&opt.kubeletVolumeDevicesDirName, "kubelet-volume-devices-dir-name",
constants.DefaultKubeletVolumeDevicesDirName, "The dir name of volume devices")
}
// ApplyFlags assign the service flags
-func (opt *serviceOptions) ApplyFlags(cfg *config.Config) {
+func (opt *serviceOptions) ApplyFlags(cfg *config.AppConfig) {
cfg.Endpoint = opt.endpoint
cfg.DrEndpoint = opt.drEndpoint
- cfg.EnableLabel = opt.enableLabel
cfg.Controller = opt.controller
cfg.DriverName = opt.driverName
cfg.BackendUpdateInterval = opt.backendUpdateInterval
diff --git a/csi/backend/backend.go b/csi/backend/backend.go
index cca7211c..412aba2a 100644
--- a/csi/backend/backend.go
+++ b/csi/backend/backend.go
@@ -146,8 +146,7 @@ func BuildBackend(ctx context.Context, content v1.StorageBackendContent) (*model
config, err := GetStorageBackendInfo(ctx,
pkgUtils.MakeMetaWithNamespace(ns, name),
- content.Spec.ConfigmapMeta, content.Spec.SecretMeta,
- content.Spec.CertSecret, content.Spec.UseCert)
+ NewGetBackendInfoArgsFromContent(&content))
if err != nil {
return nil, err
}
@@ -208,6 +207,7 @@ func NewBackend(backendName string, config map[string]interface{}) (*model.Backe
replicaBackend, _ := config["replicaBackend"].(string)
metroBackend, _ := config["metroBackend"].(string)
accountName, _ := config["accountName"].(string)
+ contentName, _ := config["contentName"].(string)
// while config hyperMetro, the metroBackend must config, hyperMetroDomain or metrovStorePairID should be config
if ((metroDomain != "" || metrovStorePairID != "") && metroBackend == "") ||
@@ -217,6 +217,7 @@ func NewBackend(backendName string, config map[string]interface{}) (*model.Backe
return &model.Backend{
Name: backendName,
+ ContentName: contentName,
Storage: storage,
Available: false,
SupportedTopologies: supportedTopologies,
@@ -449,7 +450,7 @@ func updateSelectPool(requestSize int64, parameters map[string]interface{}, sele
// when the allocType is thin, do not change the FreeCapacity.
if allocType == "thick" {
freeCapacity := utils.ParseIntWithDefault(selectPool.Capacities["FreeCapacity"], 10, 64, 0)
- selectPool.Capacities["FreeCapacity"] = strconv.FormatInt(int64(rune(freeCapacity-requestSize)), 10)
+ selectPool.Capacities["FreeCapacity"] = strconv.FormatInt(freeCapacity-requestSize, 10)
}
}
diff --git a/csi/backend/backend_get.go b/csi/backend/backend_get.go
index 3235045c..628c1ce2 100644
--- a/csi/backend/backend_get.go
+++ b/csi/backend/backend_get.go
@@ -23,10 +23,12 @@ import (
"errors"
"fmt"
+ coreV1 "k8s.io/api/core/v1"
+
+ xuanwuV1 "huawei-csi-driver/client/apis/xuanwu/v1"
"huawei-csi-driver/csi/app"
pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/utils/log"
- coreV1 "k8s.io/api/core/v1"
)
// GetBackendConfigmap used to get Configmap
@@ -85,34 +87,64 @@ func addSecretInfo(secret *coreV1.Secret, storageConfig map[string]interface{})
return nil
}
+// GetBackendInfoArgs is the arguments to get backend info.
+type GetBackendInfoArgs struct {
+ contentName string
+ configmapMeta string
+ secretMeta string
+ certSecret string
+ useCert bool
+}
+
+// NewGetBackendInfoArgsFromClaim used to new get backend info arguments from StorageBackendClaim
+func NewGetBackendInfoArgsFromClaim(claim *xuanwuV1.StorageBackendClaim) GetBackendInfoArgs {
+ return GetBackendInfoArgs{
+ configmapMeta: claim.Spec.ConfigMapMeta,
+ secretMeta: claim.Spec.SecretMeta,
+ certSecret: claim.Spec.CertSecret,
+ useCert: claim.Spec.UseCert,
+ }
+}
+
+// NewGetBackendInfoArgsFromContent used to new get backend info arguments from StorageBackendContent
+func NewGetBackendInfoArgsFromContent(content *xuanwuV1.StorageBackendContent) GetBackendInfoArgs {
+ return GetBackendInfoArgs{
+ contentName: content.Name,
+ configmapMeta: content.Spec.ConfigmapMeta,
+ secretMeta: content.Spec.SecretMeta,
+ certSecret: content.Spec.CertSecret,
+ useCert: content.Spec.UseCert,
+ }
+}
+
// GetStorageBackendInfo used to get storage config info
-func GetStorageBackendInfo(ctx context.Context, backendID, configmapMeta, secretMeta, certSecret string,
- useCert bool) (map[string]interface{}, error) {
+func GetStorageBackendInfo(ctx context.Context, backendID string, args GetBackendInfoArgs) (map[string]any, error) {
log.AddContext(ctx).Infof("start GetStorageBackendInfo: %s.", backendID)
- backendMapData, err := GetBackendConfigmapMap(ctx, configmapMeta)
+ backendMapData, err := GetBackendConfigmapMap(ctx, args.configmapMeta)
if err != nil {
- msg := fmt.Sprintf("get backend %s failed, error %v", configmapMeta, err)
+ msg := fmt.Sprintf("get backend %s failed, error %v", args.configmapMeta, err)
log.AddContext(ctx).Errorln(msg)
return nil, errors.New(msg)
}
- secret, err := pkgUtils.GetBackendSecret(ctx, secretMeta)
+ secret, err := pkgUtils.GetBackendSecret(ctx, args.secretMeta)
if err != nil {
- msg := fmt.Sprintf("GetBackendSecret for secret %s failed, error %v", secretMeta, err)
+ msg := fmt.Sprintf("GetBackendSecret for secret %s failed, error %v", args.secretMeta, err)
log.AddContext(ctx).Errorln(msg)
return nil, errors.New(msg)
}
err = addSecretInfo(secret, backendMapData)
if err != nil {
- msg := fmt.Sprintf("addSecretInfo for secret %s failed, error %v", secretMeta, err)
+ msg := fmt.Sprintf("addSecretInfo for secret %s failed, error %v", args.secretMeta, err)
log.AddContext(ctx).Errorln(msg)
return nil, errors.New(msg)
}
backendMapData["backendID"] = backendID
- backendMapData["useCert"] = useCert
- backendMapData["certSecret"] = certSecret
+ backendMapData["useCert"] = args.useCert
+ backendMapData["certSecret"] = args.certSecret
+ backendMapData["contentName"] = args.contentName
return backendMapData, nil
}
diff --git a/csi/backend/backend_test.go b/csi/backend/backend_test.go
index a78d2c26..7c4dd5d9 100644
--- a/csi/backend/backend_test.go
+++ b/csi/backend/backend_test.go
@@ -24,6 +24,7 @@ import (
"github.com/agiledragon/gomonkey/v2"
"github.com/prashantv/gostub"
+ "github.com/stretchr/testify/require"
"huawei-csi-driver/csi/app"
cfg "huawei-csi-driver/csi/app/config"
@@ -869,3 +870,20 @@ func TestValidateBackend(t *testing.T) {
t.Errorf("test validateBackend error %v", err)
}
}
+
+func TestUpdateSelectPool(t *testing.T) {
+ // arrange
+ var (
+ requestSize = int64(16106127360)
+ parameters = map[string]any{"allocType": "thick"}
+ selectPool = &model.StoragePool{Capacities: map[string]string{"FreeCapacity": "1138971639808"}}
+
+ expectedCapacity = "1122865512448" // 1138971639808 - 16106127360
+ )
+
+ // act
+ updateSelectPool(requestSize, parameters, selectPool)
+
+ // assert
+ require.Equal(t, expectedCapacity, selectPool.Capacities["FreeCapacity"])
+}
diff --git a/csi/backend/handler/backend_cache_wrapper.go b/csi/backend/handler/backend_cache_wrapper.go
index 6db0dccc..ae49d42a 100644
--- a/csi/backend/handler/backend_cache_wrapper.go
+++ b/csi/backend/handler/backend_cache_wrapper.go
@@ -46,7 +46,7 @@ type CacheWrapper struct {
// NewCacheWrapper init instance of CacheWrapper
func NewCacheWrapper() *CacheWrapper {
- return &CacheWrapper{cache.BackendCacheProvider}
+ return &CacheWrapper{BackendCacheInterface: cache.BackendCacheProvider}
}
// AddBackendToCache init a backend and add to cache
diff --git a/csi/backend/handler/backend_register.go b/csi/backend/handler/backend_register.go
index 79d87537..8776fc73 100644
--- a/csi/backend/handler/backend_register.go
+++ b/csi/backend/handler/backend_register.go
@@ -31,6 +31,7 @@ type BackendRegisterInterface interface {
FetchAndRegisterAllBackend(ctx context.Context)
FetchAndRegisterOneBackend(ctx context.Context, name string, checkOnline bool) (*model.Backend, error)
LoadOrRegisterOneBackend(ctx context.Context, name string) (*model.Backend, error)
+ LoadOrRebuildOneBackend(ctx context.Context, name, contentName string) (*model.Backend, error)
RemoveRegisteredOneBackend(ctx context.Context, name string)
UpdateOrRegisterOneBackend(ctx context.Context, sbct *v1.StorageBackendContent) error
}
@@ -65,6 +66,24 @@ func (b *BackendRegister) LoadOrRegisterOneBackend(ctx context.Context, name str
return b.FetchAndRegisterOneBackend(ctx, name, true)
}
+// LoadOrRebuildOneBackend if the backend has been already in cache, and doesn't need to rebuild, return it directly.
+// Otherwise, fetch and register the backend again.
+func (b *BackendRegister) LoadOrRebuildOneBackend(ctx context.Context,
+ name, contentName string) (*model.Backend, error) {
+ bk, exists := b.cacheHandler.Load(name)
+ if exists && !bk.NeedRebuild(contentName) {
+ return &bk, nil
+ }
+
+ if bk.NeedRebuild(contentName) {
+ log.AddContext(ctx).Infof("The content name of backend [%s] has changed from [%s] to [%s], need rebuild",
+ bk.Name, bk.ContentName, contentName)
+ b.cacheHandler.Delete(ctx, name)
+ }
+
+ return b.FetchAndRegisterOneBackend(ctx, name, true)
+}
+
// FetchAndRegisterAllBackend fetch all backends in the kubernetes and register them to cache.
func (b *BackendRegister) FetchAndRegisterAllBackend(ctx context.Context) {
contents, err := b.fetchHandler.FetchAllBackends(ctx)
diff --git a/csi/backend/handler/backend_service.go b/csi/backend/handler/backend_service.go
index 0173fe14..a8a70d1e 100644
--- a/csi/backend/handler/backend_service.go
+++ b/csi/backend/handler/backend_service.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -21,6 +21,7 @@ import (
"strconv"
"huawei-csi-driver/lib/drcsi"
+ "huawei-csi-driver/pkg/constants"
pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/utils/log"
)
@@ -34,7 +35,7 @@ type StorageBackendDetails struct {
// StorageServiceInterface query backend operation set
type StorageServiceInterface interface {
- GetBackendDetails(ctx context.Context, name string) (StorageBackendDetails, error)
+ GetBackendDetails(ctx context.Context, name, contentName string) (StorageBackendDetails, error)
}
// StorageHandler backend query handler
@@ -54,8 +55,9 @@ func NewStorageHandler() *StorageHandler {
}
// GetBackendDetails query backend details
-func (s *StorageHandler) GetBackendDetails(ctx context.Context, name string) (StorageBackendDetails, error) {
- bk, err := s.register.LoadOrRegisterOneBackend(ctx, name)
+func (s *StorageHandler) GetBackendDetails(ctx context.Context,
+ name, contentName string) (StorageBackendDetails, error) {
+ bk, err := s.register.LoadOrRebuildOneBackend(ctx, name, contentName)
if err != nil {
log.AddContext(ctx).Warningf("load cache backend %s failed, error: %v", name, err)
return StorageBackendDetails{}, err
@@ -84,7 +86,7 @@ func (s *StorageHandler) GetBackendDetails(ctx context.Context, name string) (St
capacities := make(map[string]string)
poolCapabilityInt64Map := pkgUtils.ConvertToMapValueX[int64](ctx, poolCapabilityMap[pool.GetName()])
for k, v := range poolCapabilityInt64Map {
- capacities[k] = strconv.FormatInt(v, 10)
+ capacities[k] = strconv.FormatInt(v, constants.DefaultIntBase)
}
poolCapacities = append(poolCapacities, &drcsi.Pool{
Name: pool.Name,
diff --git a/csi/backend/model/model.go b/csi/backend/model/model.go
index bb7eeda6..f94e1a46 100644
--- a/csi/backend/model/model.go
+++ b/csi/backend/model/model.go
@@ -34,9 +34,10 @@ type StorageBackendTuple struct {
// Backend for storage
type Backend struct {
Name string
+ ContentName string
Storage string
Available bool
- Plugin plugin.Plugin
+ Plugin plugin.StoragePlugin
Pools []*StoragePool
Parameters map[string]interface{}
SupportedTopologies []map[string]string
@@ -67,6 +68,11 @@ func (b *Backend) UpdatePools(ctx context.Context, sbct *xuanwuV1.StorageBackend
}
}
+// NeedRebuild checks whether the backend needs rebuild
+func (b *Backend) NeedRebuild(contentName string) bool {
+ return b.ContentName != contentName
+}
+
// SelectPoolPair for pool pair
type SelectPoolPair struct {
Local *StoragePool
diff --git a/csi/backend/model/storage_pool.go b/csi/backend/model/storage_pool.go
index f04a79c5..4bd89142 100644
--- a/csi/backend/model/storage_pool.go
+++ b/csi/backend/model/storage_pool.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -32,7 +32,7 @@ type StoragePool struct {
Parent string
Capabilities map[string]bool
Capacities map[string]string
- Plugin plugin.Plugin
+ Plugin plugin.StoragePlugin
}
func (p *StoragePool) setCapacity(k string, v string) {
diff --git a/csi/backend/model/storage_pool_test.go b/csi/backend/model/storage_pool_test.go
index be452db2..d7a1c37a 100644
--- a/csi/backend/model/storage_pool_test.go
+++ b/csi/backend/model/storage_pool_test.go
@@ -147,7 +147,6 @@ func TestStoragePool_UpdatePoolCapabilities(t *testing.T) {
"SupportApplicationType": true,
"SupportClone": true,
"SupportMetroNAS": false,
- "SupportLabel": true,
}
// act
diff --git a/csi/backend/plugin/fusionstorage-nas.go b/csi/backend/plugin/fusionstorage-nas.go
index 09c80501..891cdede 100644
--- a/csi/backend/plugin/fusionstorage-nas.go
+++ b/csi/backend/plugin/fusionstorage-nas.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -27,7 +27,7 @@ import (
"huawei-csi-driver/utils/log"
)
-// FusionStorageNasPlugin implements storage Plugin interface
+// FusionStorageNasPlugin implements storage StoragePlugin interface
type FusionStorageNasPlugin struct {
FusionStoragePlugin
portal string
@@ -39,7 +39,7 @@ func init() {
}
// NewPlugin used to create new plugin
-func (p *FusionStorageNasPlugin) NewPlugin() Plugin {
+func (p *FusionStorageNasPlugin) NewPlugin() StoragePlugin {
return &FusionStorageNasPlugin{}
}
@@ -70,7 +70,8 @@ func (p *FusionStorageNasPlugin) Init(ctx context.Context, config map[string]int
return nil
}
-func (p *FusionStorageNasPlugin) updateNasCapacity(ctx context.Context, params, parameters map[string]interface{}) error {
+func (p *FusionStorageNasPlugin) updateNasCapacity(ctx context.Context,
+ params, parameters map[string]interface{}) error {
size, exist := parameters["size"].(int64)
if !exist {
return utils.Errorf(ctx, "the size does not exist in parameters %v", parameters)
@@ -134,7 +135,6 @@ func (p *FusionStorageNasPlugin) UpdateBackendCapabilities(ctx context.Context)
"SupportQoS": true,
"SupportQuota": true,
"SupportClone": false,
- "SupportLabel": false,
}
err := p.updateNFS4Capability(ctx, capabilities)
diff --git a/csi/backend/plugin/fusionstorage-san.go b/csi/backend/plugin/fusionstorage-san.go
index d94c0d84..bddc5e3b 100644
--- a/csi/backend/plugin/fusionstorage-san.go
+++ b/csi/backend/plugin/fusionstorage-san.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -33,7 +33,7 @@ import (
"huawei-csi-driver/utils/log"
)
-// FusionStorageSanPlugin implements storage Plugin interface
+// FusionStorageSanPlugin implements storage StoragePlugin interface
type FusionStorageSanPlugin struct {
FusionStoragePlugin
hosts map[string]string
@@ -51,7 +51,7 @@ func init() {
}
// NewPlugin used to create new plugin
-func (p *FusionStorageSanPlugin) NewPlugin() Plugin {
+func (p *FusionStorageSanPlugin) NewPlugin() StoragePlugin {
return &FusionStorageSanPlugin{
hosts: make(map[string]string),
}
@@ -119,7 +119,7 @@ func (p *FusionStorageSanPlugin) getParams(name string,
params := map[string]interface{}{
"name": name,
"description": parameters["description"].(string),
- "capacity": utils.RoundUpSize(parameters["size"].(int64), CAPACITY_UNIT),
+ "capacity": utils.RoundUpSize(parameters["size"].(int64), CapacityUnit),
}
paramKeys := []string{
@@ -146,9 +146,9 @@ func (p *FusionStorageSanPlugin) CreateVolume(ctx context.Context, name string,
size, ok := parameters["size"].(int64)
// for fusionStorage block, the unit is MiB
- if !ok || !utils.IsCapacityAvailable(size, CAPACITY_UNIT) {
+ if !ok || !utils.IsCapacityAvailable(size, CapacityUnit) {
msg := fmt.Sprintf("Create Volume: the capacity %d is not an integer or not multiple of %d.",
- size, CAPACITY_UNIT)
+ size, CapacityUnit)
log.AddContext(ctx).Errorln(msg)
return nil, errors.New(msg)
}
@@ -183,12 +183,12 @@ func (p *FusionStorageSanPlugin) DeleteVolume(ctx context.Context, name string)
// ExpandVolume used to expand volume
func (p *FusionStorageSanPlugin) ExpandVolume(ctx context.Context, name string, size int64) (bool, error) {
// for fusionStorage block, the unit is MiB
- if !utils.IsCapacityAvailable(size, CAPACITY_UNIT) {
+ if !utils.IsCapacityAvailable(size, CapacityUnit) {
return false, utils.Errorf(ctx, "Expand Volume: the capacity %d is not an integer multiple of %d.",
- size, CAPACITY_UNIT)
+ size, CapacityUnit)
}
san := volume.NewSAN(p.cli)
- newSize := utils.TransVolumeCapacity(size, CAPACITY_UNIT)
+ newSize := utils.TransVolumeCapacity(size, CapacityUnit)
isAttach, err := san.Expand(ctx, name, newSize)
return isAttach, err
}
@@ -196,7 +196,14 @@ func (p *FusionStorageSanPlugin) ExpandVolume(ctx context.Context, name string,
// AttachVolume attach volume to node and return storage mapping info.
func (p *FusionStorageSanPlugin) AttachVolume(ctx context.Context, name string,
parameters map[string]interface{}) (map[string]interface{}, error) {
- localAttacher := attacher.NewAttacher(p.cli, p.protocol, "csi", p.portals, p.hosts, p.alua)
+ localAttacher := attacher.NewAttacher(attacher.VolumeAttacherConfig{
+ Cli: p.cli,
+ Protocol: p.protocol,
+ Invoker: "csi",
+ Portals: p.portals,
+ Hosts: p.hosts,
+ Alua: p.alua,
+ })
mappingInfo, err := localAttacher.ControllerAttach(ctx, name, parameters)
if err != nil {
log.AddContext(ctx).Errorf("attach volume %s error: %v", name, err)
@@ -210,7 +217,14 @@ func (p *FusionStorageSanPlugin) AttachVolume(ctx context.Context, name string,
func (p *FusionStorageSanPlugin) DetachVolume(ctx context.Context,
name string,
parameters map[string]interface{}) error {
- localAttacher := attacher.NewAttacher(p.cli, p.protocol, "csi", p.portals, p.hosts, p.alua)
+ localAttacher := attacher.NewAttacher(attacher.VolumeAttacherConfig{
+ Cli: p.cli,
+ Protocol: p.protocol,
+ Invoker: "csi",
+ Portals: p.portals,
+ Hosts: p.hosts,
+ Alua: p.alua,
+ })
_, err := localAttacher.ControllerDetach(ctx, name, parameters)
if err != nil {
log.AddContext(ctx).Errorf("Detach volume %s error: %v", name, err)
@@ -222,7 +236,7 @@ func (p *FusionStorageSanPlugin) DetachVolume(ctx context.Context,
func (p *FusionStorageSanPlugin) mutexReleaseClient(ctx context.Context,
plugin *FusionStorageSanPlugin,
- cli *client.Client) {
+ cli *client.RestClient) {
plugin.clientMutex.Lock()
defer plugin.clientMutex.Unlock()
plugin.clientCount--
@@ -232,7 +246,7 @@ func (p *FusionStorageSanPlugin) mutexReleaseClient(ctx context.Context,
}
}
-func (p *FusionStorageSanPlugin) releaseClient(ctx context.Context, cli *client.Client) {
+func (p *FusionStorageSanPlugin) releaseClient(ctx context.Context, cli *client.RestClient) {
if p.storageOnline {
p.mutexReleaseClient(ctx, p, cli)
}
@@ -246,7 +260,6 @@ func (p *FusionStorageSanPlugin) UpdateBackendCapabilities(ctx context.Context)
"SupportThick": false,
"SupportQoS": true,
"SupportClone": true,
- "SupportLabel": false,
}
return capabilities, nil, nil
}
diff --git a/csi/backend/plugin/fusionstorage.go b/csi/backend/plugin/fusionstorage.go
index 8b86d428..93f5cc20 100644
--- a/csi/backend/plugin/fusionstorage.go
+++ b/csi/backend/plugin/fusionstorage.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -31,12 +31,12 @@ import (
)
const (
- // CAPACITY_UNIT unit of capacity
- CAPACITY_UNIT int64 = 1024 * 1024
+ // CapacityUnit unit of capacity
+ CapacityUnit int64 = 1024 * 1024
fileCapacityUnit int64 = 1024
- // PROTOCOL_DPC protocol DPC string
- PROTOCOL_DPC = "dpc"
+ // ProtocolDpc protocol DPC string
+ ProtocolDpc = "dpc"
)
const (
@@ -49,7 +49,7 @@ const (
// FusionStoragePlugin defines the plugin for Fusion storage
type FusionStoragePlugin struct {
basePlugin
- cli *client.Client
+ cli *client.RestClient
}
func (p *FusionStoragePlugin) init(ctx context.Context, config map[string]interface{}, keepLogin bool) error {
@@ -88,7 +88,7 @@ func (p *FusionStoragePlugin) getParams(name string,
params := map[string]interface{}{
"name": name,
"description": parameters["description"].(string),
- "capacity": utils.RoundUpSize(parameters["size"].(int64), CAPACITY_UNIT),
+ "capacity": utils.RoundUpSize(parameters["size"].(int64), CapacityUnit),
}
paramKeys := []string{
@@ -147,9 +147,9 @@ func (p *FusionStoragePlugin) updatePoolCapabilities(ctx context.Context, poolNa
totalCapacity := int64(pool["totalCapacity"].(float64))
usedCapacity := int64(pool["usedCapacity"].(float64))
capability := map[string]interface{}{
- string(xuanwuv1.FreeCapacity): (totalCapacity - usedCapacity) * CAPACITY_UNIT,
- string(xuanwuv1.TotalCapacity): totalCapacity * CAPACITY_UNIT,
- string(xuanwuv1.UsedCapacity): usedCapacity * CAPACITY_UNIT,
+ string(xuanwuv1.FreeCapacity): (totalCapacity - usedCapacity) * CapacityUnit,
+ string(xuanwuv1.TotalCapacity): totalCapacity * CapacityUnit,
+ string(xuanwuv1.UsedCapacity): usedCapacity * CapacityUnit,
}
capabilities[name] = capability
}
@@ -171,7 +171,8 @@ func (p *FusionStoragePlugin) Logout(ctx context.Context) {
}
}
-func (p *FusionStoragePlugin) getNewClientConfig(ctx context.Context, config map[string]interface{}) (*client.NewClientConfig, error) {
+func (p *FusionStoragePlugin) getNewClientConfig(ctx context.Context,
+ config map[string]interface{}) (*client.NewClientConfig, error) {
newClientConfig := &client.NewClientConfig{}
configUrls, exist := config["urls"].([]interface{})
if !exist || len(configUrls) <= 0 {
diff --git a/csi/backend/plugin/oceanstor-dtree.go b/csi/backend/plugin/oceanstor-dtree.go
index f7208ee8..77c8e5f1 100644
--- a/csi/backend/plugin/oceanstor-dtree.go
+++ b/csi/backend/plugin/oceanstor-dtree.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -36,7 +36,7 @@ const (
DTreeStorage = "oceanstor-dtree"
)
-// OceanstorDTreePlugin implements storage Plugin interface
+// OceanstorDTreePlugin implements storage StoragePlugin interface
type OceanstorDTreePlugin struct {
OceanstorPlugin
@@ -49,7 +49,7 @@ func init() {
}
// NewPlugin used to create new plugin
-func (p *OceanstorDTreePlugin) NewPlugin() Plugin {
+func (p *OceanstorDTreePlugin) NewPlugin() StoragePlugin {
return &OceanstorDTreePlugin{}
}
@@ -153,7 +153,7 @@ func (p *OceanstorDTreePlugin) ExpandDTreeVolume(ctx context.Context, params map
}
parentName, _ := utils.ToStringWithFlag(params["parentname"])
- err := dTree.Expand(ctx, parentName, dTreeName, p.vStoreId, 0, spaceHardQuota)
+ err := dTree.Expand(ctx, parentName, dTreeName, p.vStoreId, spaceHardQuota)
if err != nil {
log.AddContext(ctx).Errorf("expand dTree volume failed, ")
return false, err
@@ -258,7 +258,6 @@ func (p *OceanstorDTreePlugin) UpdateBackendCapabilities(ctx context.Context) (m
}
// close dTree pvc label switch
- capabilities[string(constants.SupportLabel)] = false
capabilities[string(constants.SupportMetro)] = false
capabilities[string(constants.SupportMetroNAS)] = false
capabilities[string(constants.SupportReplication)] = false
diff --git a/csi/backend/plugin/oceanstor-nas.go b/csi/backend/plugin/oceanstor-nas.go
index deac0865..3708c03c 100644
--- a/csi/backend/plugin/oceanstor-nas.go
+++ b/csi/backend/plugin/oceanstor-nas.go
@@ -50,9 +50,9 @@ const (
ConsistentSnapshotsSpecification = "128"
)
-var supportConsistentSnapshotsVersions = []string{"6.1.6"}
+var supportConsistentSnapshotsMinVersion = "6.1.6"
-// OceanstorNasPlugin implements storage Plugin interface
+// OceanstorNasPlugin implements storage StoragePlugin interface
type OceanstorNasPlugin struct {
OceanstorPlugin
portals []string
@@ -69,7 +69,7 @@ func init() {
}
// NewPlugin used to create new plugin
-func (p *OceanstorNasPlugin) NewPlugin() Plugin {
+func (p *OceanstorNasPlugin) NewPlugin() StoragePlugin {
return &OceanstorNasPlugin{}
}
@@ -97,10 +97,10 @@ func (p *OceanstorNasPlugin) Init(ctx context.Context, config map[string]interfa
return err
}
- if protocol == ProtocolNfsPlus && p.cli.GetStorageVersion() < constants.MinVersionSupportLabel {
+ if protocol == ProtocolNfsPlus && p.cli.GetStorageVersion() < constants.MinVersionSupportNfsPlus {
p.Logout(ctx)
- return errors.New("only oceanstor nas version gte 6.1.7 support nfs_plus")
+ return errors.New("only oceanstor nas version gte 6.1.7 support nfs plus")
}
return nil
@@ -236,10 +236,10 @@ func (p *OceanstorNasPlugin) getVstoreCapacity(ctx context.Context) (map[string]
var nasCapacityQuota, nasFreeCapacityQuota int64
if totalStr, ok := vStore["nasCapacityQuota"].(string); ok {
- nasCapacityQuota, err = strconv.ParseInt(totalStr, 10, 64)
+ nasCapacityQuota, err = strconv.ParseInt(totalStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
}
if freeStr, ok := vStore["nasFreeCapacityQuota"].(string); ok {
- nasFreeCapacityQuota, err = strconv.ParseInt(freeStr, 10, 64)
+ nasFreeCapacityQuota, err = strconv.ParseInt(freeStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
}
if err != nil {
log.AddContext(ctx).Warningf("parse vstore quota failed, error: %v", err)
@@ -254,14 +254,14 @@ func (p *OceanstorNasPlugin) getVstoreCapacity(ctx context.Context) (map[string]
}
return map[string]interface{}{
- string(xuanwuV1.FreeCapacity): nasFreeCapacityQuota * 512,
- string(xuanwuV1.TotalCapacity): nasCapacityQuota * 512,
- string(xuanwuV1.UsedCapacity): (nasCapacityQuota - nasFreeCapacityQuota) * 512,
+ string(xuanwuV1.FreeCapacity): nasFreeCapacityQuota * constants.AllocationUnitBytes,
+ string(xuanwuV1.TotalCapacity): nasCapacityQuota * constants.AllocationUnitBytes,
+ string(xuanwuV1.UsedCapacity): (nasCapacityQuota - nasFreeCapacityQuota) * constants.AllocationUnitBytes,
}, nil
}
// UpdateMetroRemotePlugin used to convert metroRemotePlugin to OceanstorSanPlugin
-func (p *OceanstorNasPlugin) UpdateMetroRemotePlugin(ctx context.Context, remote Plugin) {
+func (p *OceanstorNasPlugin) UpdateMetroRemotePlugin(ctx context.Context, remote StoragePlugin) {
var ok bool
p.metroRemotePlugin, ok = remote.(*OceanstorNasPlugin)
if !ok {
@@ -401,14 +401,14 @@ func (p *OceanstorNasPlugin) updateHyperMetroCapability(ctx context.Context,
return nil
}
-func (p *OceanstorNasPlugin) updateConsistentSnapshotCapability(
- capabilities, specifications map[string]interface{}) error {
- var supportConsistentSnapshot bool
- if utils.StringContain(p.cli.GetStorageVersion(), supportConsistentSnapshotsVersions) {
- supportConsistentSnapshot = true
- specifications["ConsistentSnapshotLimits"] = ConsistentSnapshotsSpecification
+func (p *OceanstorNasPlugin) updateConsistentSnapshotCapability(capabilities, specifications map[string]any) error {
+ if p.cli.GetStorageVersion() < supportConsistentSnapshotsMinVersion {
+ capabilities["SupportConsistentSnapshot"] = false
+ return nil
}
- capabilities["SupportConsistentSnapshot"] = supportConsistentSnapshot
+
+ capabilities["SupportConsistentSnapshot"] = true
+ specifications["ConsistentSnapshotLimits"] = ConsistentSnapshotsSpecification
return nil
}
diff --git a/csi/backend/plugin/oceanstor-nas_test.go b/csi/backend/plugin/oceanstor-nas_test.go
index 60e57b63..431eb6f2 100644
--- a/csi/backend/plugin/oceanstor-nas_test.go
+++ b/csi/backend/plugin/oceanstor-nas_test.go
@@ -22,9 +22,7 @@ import (
"reflect"
"testing"
- "bou.ke/monkey"
"github.com/agiledragon/gomonkey/v2"
- "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/require"
"huawei-csi-driver/storage/oceanstor/client"
@@ -62,17 +60,17 @@ func TestInit(t *testing.T) {
}
var cli *client.BaseClient
- monkey.PatchInstanceMethod(reflect.TypeOf(cli), "Logout",
- func(*client.BaseClient, context.Context) {})
- monkey.PatchInstanceMethod(reflect.TypeOf(cli), "Login",
- func(*client.BaseClient, context.Context) error {
- return nil
- })
- monkey.PatchInstanceMethod(reflect.TypeOf(cli), "SetSystemInfo",
- func(*client.BaseClient, context.Context) error {
- return nil
- })
- defer monkey.UnpatchAll()
+ p := gomonkey.ApplyMethod(reflect.TypeOf(cli), "Logout",
+ func(*client.BaseClient, context.Context) {}).
+ ApplyMethod(reflect.TypeOf(cli), "Login",
+ func(*client.BaseClient, context.Context) error {
+ return nil
+ }).
+ ApplyMethod(reflect.TypeOf(cli), "SetSystemInfo",
+ func(*client.BaseClient, context.Context) error {
+ return nil
+ })
+ defer p.Reset()
for _, tt := range tests {
var p = &OceanstorNasPlugin{}
@@ -85,12 +83,12 @@ func TestInit(t *testing.T) {
}
func TestValidate(t *testing.T) {
- convey.Convey("Empty", t, func() {
+ t.Run("Empty", func(t *testing.T) {
err := mockOceanstorNasPlugin.Validate(ctx, map[string]interface{}{})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("Normal", t, func() {
+ t.Run("Normal", func(t *testing.T) {
portals := []interface{}{"127.0.0.1"}
parameters := map[string]interface{}{
"protocol": "nfs",
@@ -113,7 +111,7 @@ func TestValidate(t *testing.T) {
defer m.Reset()
err := mockOceanstorNasPlugin.Validate(ctx, config)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
}
@@ -273,3 +271,35 @@ func TestGetLocal2HyperMetroParameters_EmptyParam(t *testing.T) {
// assert
require.Equal(t, nil, err)
}
+
+func TestOceanstorNasPluginUpdateConsistentSnapshotCapability(t *testing.T) {
+ // arrange
+ cases := []struct {
+ version string
+ supported bool
+ }{
+ {version: "6.0.1", supported: false},
+ {version: "6.1.0", supported: false},
+ {version: "6.1.2", supported: false},
+ {version: "6.1.3", supported: false},
+ {version: "6.1.5", supported: false},
+ {version: "6.1.6", supported: true},
+ {version: "6.1.7", supported: true},
+ {version: "6.1.8", supported: true},
+ }
+ var genNas = func(version string) *OceanstorNasPlugin {
+ return &OceanstorNasPlugin{OceanstorPlugin: OceanstorPlugin{cli: &client.BaseClient{StorageVersion: version}}}
+ }
+
+ for _, c := range cases {
+ capabilities := map[string]any{}
+ specifications := map[string]any{}
+
+ // act
+ err := genNas(c.version).updateConsistentSnapshotCapability(capabilities, specifications)
+
+ // assert
+ require.NoError(t, err)
+ require.Equal(t, c.supported, capabilities["SupportConsistentSnapshot"])
+ }
+}
diff --git a/csi/backend/plugin/oceanstor-san.go b/csi/backend/plugin/oceanstor-san.go
index 75175ab3..490f208a 100644
--- a/csi/backend/plugin/oceanstor-san.go
+++ b/csi/backend/plugin/oceanstor-san.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -42,7 +42,7 @@ const (
reflectResultLength = 2
)
-// OceanstorSanPlugin implements storage Plugin interface
+// OceanstorSanPlugin implements storage StoragePlugin interface
type OceanstorSanPlugin struct {
OceanstorPlugin
protocol string
@@ -69,7 +69,7 @@ func init() {
}
// NewPlugin used to create new plugin
-func (p *OceanstorSanPlugin) NewPlugin() Plugin {
+func (p *OceanstorSanPlugin) NewPlugin() StoragePlugin {
return &OceanstorSanPlugin{}
}
@@ -173,7 +173,7 @@ func (p *OceanstorSanPlugin) ExpandVolume(ctx context.Context, name string, size
return false, errors.New(msg)
}
san := p.getSanObj()
- newSize := utils.TransVolumeCapacity(size, 512)
+ newSize := utils.TransVolumeCapacity(size, constants.AllocationUnitBytes)
isAttach, err := san.Expand(ctx, name, newSize)
return isAttach, err
}
@@ -222,9 +222,22 @@ func (p *OceanstorSanPlugin) metroHandler(ctx context.Context, req handlerReques
}
}
- localAttacher := attacher.NewAttacher(p.product, req.localCli, p.protocol, "csi", p.portals, p.alua)
- remoteAttacher := attacher.NewAttacher(p.metroRemotePlugin.product, req.metroCli, p.metroRemotePlugin.protocol,
- "csi", p.metroRemotePlugin.portals, p.metroRemotePlugin.alua)
+ localAttacher := attacher.NewAttacher(attacher.VolumeAttacherConfig{
+ Product: p.product,
+ Cli: req.localCli,
+ Protocol: p.protocol,
+ Invoker: "csi",
+ Portals: p.portals,
+ Alua: p.alua,
+ })
+ remoteAttacher := attacher.NewAttacher(attacher.VolumeAttacherConfig{
+ Product: p.metroRemotePlugin.product,
+ Cli: req.metroCli,
+ Protocol: p.metroRemotePlugin.protocol,
+ Invoker: "csi",
+ Portals: p.metroRemotePlugin.portals,
+ Alua: p.metroRemotePlugin.alua,
+ })
metroAttacher := attacher.NewMetroAttacher(localAttacher, remoteAttacher, p.protocol)
lunName, ok := req.lun["NAME"].(string)
@@ -237,10 +250,15 @@ func (p *OceanstorSanPlugin) metroHandler(ctx context.Context, req handlerReques
}
func (p *OceanstorSanPlugin) commonHandler(ctx context.Context,
- plugin *OceanstorSanPlugin, lun, parameters map[string]interface{},
- method string) ([]reflect.Value, error) {
- commonAttacher := attacher.NewAttacher(plugin.product, plugin.cli, plugin.protocol, "csi",
- plugin.portals, plugin.alua)
+ plugin *OceanstorSanPlugin, lun, parameters map[string]any, method string) ([]reflect.Value, error) {
+ commonAttacher := attacher.NewAttacher(attacher.VolumeAttacherConfig{
+ Product: plugin.product,
+ Cli: plugin.cli,
+ Protocol: plugin.protocol,
+ Invoker: "csi",
+ Portals: plugin.portals,
+ Alua: plugin.alua,
+ })
lunName, ok := lun["NAME"].(string)
if !ok {
@@ -408,10 +426,10 @@ func (p *OceanstorSanPlugin) getVstoreCapacity(ctx context.Context) (map[string]
var sanCapacityQuota, sanFreeCapacityQuota int64
if totalStr, ok := vStore["sanCapacityQuota"].(string); ok {
- sanCapacityQuota, err = strconv.ParseInt(totalStr, 10, 64)
+ sanCapacityQuota, err = strconv.ParseInt(totalStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
}
if freeStr, ok := vStore["sanFreeCapacityQuota"].(string); ok {
- sanFreeCapacityQuota, err = strconv.ParseInt(freeStr, 10, 64)
+ sanFreeCapacityQuota, err = strconv.ParseInt(freeStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
}
if err != nil {
log.AddContext(ctx).Warningf("parse vstore quota failed, error: %v", err)
@@ -424,14 +442,14 @@ func (p *OceanstorSanPlugin) getVstoreCapacity(ctx context.Context) (map[string]
}
return map[string]interface{}{
- string(xuanwuV1.FreeCapacity): sanFreeCapacityQuota * 512,
- string(xuanwuV1.TotalCapacity): sanCapacityQuota * 512,
- string(xuanwuV1.UsedCapacity): (sanCapacityQuota - sanFreeCapacityQuota) * 512,
+ string(xuanwuV1.FreeCapacity): sanFreeCapacityQuota * constants.AllocationUnitBytes,
+ string(xuanwuV1.TotalCapacity): sanCapacityQuota * constants.AllocationUnitBytes,
+ string(xuanwuV1.UsedCapacity): (sanCapacityQuota - sanFreeCapacityQuota) * constants.AllocationUnitBytes,
}, nil
}
// UpdateMetroRemotePlugin used to convert metroRemotePlugin to OceanstorSanPlugin
-func (p *OceanstorSanPlugin) UpdateMetroRemotePlugin(ctx context.Context, remote Plugin) {
+func (p *OceanstorSanPlugin) UpdateMetroRemotePlugin(ctx context.Context, remote StoragePlugin) {
var ok bool
p.metroRemotePlugin, ok = remote.(*OceanstorSanPlugin)
if !ok {
@@ -484,7 +502,8 @@ func (p *OceanstorSanPlugin) mutexGetClient(ctx context.Context) (client.BaseCli
return p.cli, err
}
-func (p *OceanstorSanPlugin) getClient(ctx context.Context) (client.BaseClientInterface, client.BaseClientInterface, error) {
+func (p *OceanstorSanPlugin) getClient(ctx context.Context) (client.BaseClientInterface,
+ client.BaseClientInterface, error) {
cli, locErr := p.mutexGetClient(ctx)
var metroCli client.BaseClientInterface
var rmtErr error
diff --git a/csi/backend/plugin/oceanstor.go b/csi/backend/plugin/oceanstor.go
index 1be174b1..cf3549a0 100644
--- a/csi/backend/plugin/oceanstor.go
+++ b/csi/backend/plugin/oceanstor.go
@@ -24,7 +24,6 @@ import (
"strings"
xuanwuV1 "huawei-csi-driver/client/apis/xuanwu/v1"
- "huawei-csi-driver/csi/app"
"huawei-csi-driver/pkg/constants"
pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/storage/oceanstor/client"
@@ -174,11 +173,7 @@ func (p *OceanstorPlugin) updateBackendCapabilities(ctx context.Context) (map[st
supportClone := utils.IsSupportFeature(features, "HyperClone") || utils.IsSupportFeature(features, "HyperCopy")
supportApplicationType := p.product == "DoradoV6"
- supportLabel := app.GetGlobalConfig().EnableLabel &&
- p.cli.GetStorageVersion() >= constants.MinVersionSupportLabel &&
- p.cli.IsSupportContainer(ctx)
- log.AddContext(ctx).Debugf("enableLabel: %v, storageVersion: %v", app.GetGlobalConfig().EnableLabel,
- p.cli.GetStorageVersion())
+ log.AddContext(ctx).Debugf("storageVersion: %v", p.cli.GetStorageVersion())
capabilities := map[string]interface{}{
"SupportThin": supportThin,
@@ -189,7 +184,6 @@ func (p *OceanstorPlugin) updateBackendCapabilities(ctx context.Context) (map[st
"SupportApplicationType": supportApplicationType,
"SupportClone": supportClone,
"SupportMetroNAS": supportMetroNAS,
- "SupportLabel": supportLabel,
}
return capabilities, nil
@@ -367,6 +361,7 @@ func toLowerParams(source, target map[string]interface{}) {
"accesskrb5i",
"accesskrb5p",
"fileSystemMode",
+ "metroPairSyncSpeed",
} {
if v, exist := source[key]; exist && v != "" {
target[strings.ToLower(key)] = v
@@ -415,17 +410,17 @@ func (p *OceanstorPlugin) analyzePoolsCapacity(ctx context.Context, pools []map[
var err error
var freeCapacity, totalCapacity int64
if freeStr, ok := pool["USERFREECAPACITY"].(string); ok {
- freeCapacity, err = strconv.ParseInt(freeStr, 10, 64)
+ freeCapacity, err = strconv.ParseInt(freeStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
}
if totalStr, ok := pool["USERTOTALCAPACITY"].(string); ok {
- totalCapacity, err = strconv.ParseInt(totalStr, 10, 64)
+ totalCapacity, err = strconv.ParseInt(totalStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
}
if err != nil {
log.AddContext(ctx).Warningf("parse capacity failed, error: %v", err)
}
poolCapacityMap := map[string]interface{}{
- string(xuanwuV1.FreeCapacity): freeCapacity * 512,
- string(xuanwuV1.TotalCapacity): totalCapacity * 512,
+ string(xuanwuV1.FreeCapacity): freeCapacity * constants.AllocationUnitBytes,
+ string(xuanwuV1.TotalCapacity): totalCapacity * constants.AllocationUnitBytes,
string(xuanwuV1.UsedCapacity): totalCapacity - freeCapacity,
}
if len(vStoreQuotaMap) == 0 {
@@ -435,7 +430,7 @@ func (p *OceanstorPlugin) analyzePoolsCapacity(ctx context.Context, pools []map[
log.AddContext(ctx).Debugf("analyzePoolsCapacity poolName: %s, poolCapacity: %+v, vstoreQuota: %+v",
name, poolCapacityMap, vStoreQuotaMap)
free, ok := vStoreQuotaMap[string(xuanwuV1.FreeCapacity)].(int64)
- if ok && free < freeCapacity*512 {
+ if ok && free < freeCapacity*constants.AllocationUnitBytes {
capabilities[name] = vStoreQuotaMap
} else {
capabilities[name] = poolCapacityMap
@@ -476,7 +471,8 @@ func (p *OceanstorPlugin) switchClient(ctx context.Context, newClient client.Bas
return nil
}
-func (p *OceanstorPlugin) getNewClientConfig(ctx context.Context, param map[string]interface{}) (*client.NewClientConfig, error) {
+func (p *OceanstorPlugin) getNewClientConfig(ctx context.Context,
+ param map[string]interface{}) (*client.NewClientConfig, error) {
data := &client.NewClientConfig{}
configUrls, exist := param["urls"].([]interface{})
if !exist || len(configUrls) <= 0 {
diff --git a/csi/backend/plugin/plugin.go b/csi/backend/plugin/plugin.go
index 4491c414..31a9d47f 100644
--- a/csi/backend/plugin/plugin.go
+++ b/csi/backend/plugin/plugin.go
@@ -25,9 +25,9 @@ import (
"huawei-csi-driver/utils"
)
-// Plugin defines storage plugin interfaces
-type Plugin interface {
- NewPlugin() Plugin
+// StoragePlugin defines storage plugin interfaces
+type StoragePlugin interface {
+ NewPlugin() StoragePlugin
Init(context.Context, map[string]interface{}, map[string]interface{}, bool) error
CreateVolume(context.Context, string, map[string]interface{}) (utils.Volume, error)
QueryVolume(context.Context, string, map[string]interface{}) (utils.Volume, error)
@@ -39,7 +39,7 @@ type Plugin interface {
UpdateBackendCapabilities(context.Context) (map[string]interface{}, map[string]interface{}, error)
UpdatePoolCapabilities(context.Context, []string) (map[string]interface{}, error)
- UpdateMetroRemotePlugin(context.Context, Plugin)
+ UpdateMetroRemotePlugin(context.Context, StoragePlugin)
CreateSnapshot(context.Context, string, string) (map[string]interface{}, error)
DeleteSnapshot(context.Context, string, string) error
SmartXQoSQuery
@@ -58,12 +58,12 @@ type Plugin interface {
// SmartXQoSQuery provides Quality of Service(QoS) Query operations
type SmartXQoSQuery interface {
- // SupportQoSParameters checks requested QoS parameters support by Plugin
+ // SupportQoSParameters checks requested QoS parameters support by StoragePlugin
SupportQoSParameters(ctx context.Context, qos string) error
}
var (
- plugins = map[string]Plugin{}
+ plugins = map[string]StoragePlugin{}
)
const (
@@ -72,12 +72,12 @@ const (
)
// RegPlugin used to register plugin
-func RegPlugin(storageType string, plugin Plugin) {
+func RegPlugin(storageType string, plugin StoragePlugin) {
plugins[storageType] = plugin
}
// GetPlugin used to get plugin by storage type
-func GetPlugin(storageType string) Plugin {
+func GetPlugin(storageType string) StoragePlugin {
if plugin, exist := plugins[storageType]; exist {
return plugin.NewPlugin()
}
@@ -98,7 +98,7 @@ func (p *basePlugin) DetachVolume(context.Context, string, map[string]interface{
return nil
}
-func (p *basePlugin) UpdateMetroRemotePlugin(context.Context, Plugin) {
+func (p *basePlugin) UpdateMetroRemotePlugin(context.Context, StoragePlugin) {
}
// SetOnline sets the online status of plugin
diff --git a/csi/backend/plugin/plugin_test.go b/csi/backend/plugin/plugin_test.go
index 659da1a3..108369a9 100644
--- a/csi/backend/plugin/plugin_test.go
+++ b/csi/backend/plugin/plugin_test.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -33,7 +33,7 @@ const (
var (
ctx context.Context
- mockOceanstorNasPlugin Plugin
+ mockOceanstorNasPlugin StoragePlugin
)
func TestMain(m *testing.M) {
diff --git a/csi/driver/controller.go b/csi/driver/controller.go
index a4292a36..2f095d27 100644
--- a/csi/driver/controller.go
+++ b/csi/driver/controller.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -29,13 +29,12 @@ import (
"huawei-csi-driver/csi/app"
"huawei-csi-driver/csi/backend/plugin"
- pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
// CreateVolume used to create volume
-func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
+func (d *CsiDriver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
log.AddContext(ctx).Infof("Start to create volume %s", req.GetName())
@@ -70,7 +69,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
}
// DeleteVolume used to delete volume
-func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
+func (d *CsiDriver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
volumeId := req.GetVolumeId()
log.AddContext(ctx).Infof("Start to delete volume %s", volumeId)
@@ -100,15 +99,11 @@ func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest)
log.AddContext(ctx).Infof("Volume %s is deleted", volumeId)
- // Delete the topology after the volume is successfully deleted.
- // This prevents the DeleteLabel function from being repeatedly invoked when the volume fails to be deleted.
- go pkgUtils.DeletePVLabel(volumeId)
-
return &csi.DeleteVolumeResponse{}, nil
}
// ControllerExpandVolume used to controller expand volume
-func (d *Driver) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (
+func (d *CsiDriver) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (
*csi.ControllerExpandVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -155,7 +150,8 @@ func (d *Driver) ControllerExpandVolume(ctx context.Context, req *csi.Controller
return nil, status.Error(codes.Internal, err.Error())
}
- log.AddContext(ctx).Infof("Volume %s is expanded to %d, nodeExpansionRequired %t", volName, minSize, nodeExpansionRequired)
+ log.AddContext(ctx).Infof("Volume %s is expanded to %d, nodeExpansionRequired %t",
+ volName, minSize, nodeExpansionRequired)
return &csi.ControllerExpandVolumeResponse{
CapacityBytes: minSize,
NodeExpansionRequired: nodeExpansionRequired,
@@ -163,7 +159,7 @@ func (d *Driver) ControllerExpandVolume(ctx context.Context, req *csi.Controller
}
// ControllerPublishVolume used to controller publish volume
-func (d *Driver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (
+func (d *CsiDriver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (
*csi.ControllerPublishVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -209,7 +205,7 @@ func (d *Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controlle
}
// ControllerUnpublishVolume used to controller unpublish volume
-func (d *Driver) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (
+func (d *CsiDriver) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (
*csi.ControllerUnpublishVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -245,24 +241,24 @@ func (d *Driver) ControllerUnpublishVolume(ctx context.Context, req *csi.Control
}
// ValidateVolumeCapabilities used to validate volume capabilities
-func (d *Driver) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (
+func (d *CsiDriver) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (
*csi.ValidateVolumeCapabilitiesResponse, error) {
return nil, status.Error(codes.Unimplemented, "Not implemented")
}
// ListVolumes used to list volumes
-func (d *Driver) ListVolumes(ctx context.Context, req *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
+func (d *CsiDriver) ListVolumes(ctx context.Context, req *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
return nil, status.Error(codes.Unimplemented, "Not implemented")
}
// GetCapacity used to get volume capacity
-func (d *Driver) GetCapacity(ctx context.Context, req *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
+func (d *CsiDriver) GetCapacity(ctx context.Context, req *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
return nil, status.Error(codes.Unimplemented, "Not implemented")
}
// ControllerGetCapabilities used to controller get capabilities
-func (d *Driver) ControllerGetCapabilities(ctx context.Context, req *csi.ControllerGetCapabilitiesRequest) (
+func (d *CsiDriver) ControllerGetCapabilities(ctx context.Context, req *csi.ControllerGetCapabilitiesRequest) (
*csi.ControllerGetCapabilitiesResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -308,7 +304,7 @@ func (d *Driver) ControllerGetCapabilities(ctx context.Context, req *csi.Control
}
// CreateSnapshot used to create snapshot for volume
-func (d *Driver) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (
+func (d *CsiDriver) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (
*csi.CreateSnapshotResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -350,7 +346,7 @@ func (d *Driver) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequ
}
// DeleteSnapshot used to delete snapshot
-func (d *Driver) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (
+func (d *CsiDriver) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (
*csi.DeleteSnapshotResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -379,12 +375,13 @@ func (d *Driver) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequ
}
// ListSnapshots used to list snapshots
-func (d *Driver) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
+func (d *CsiDriver) ListSnapshots(ctx context.Context,
+ req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
// ControllerGetVolume is to get volume info, but unimplemented
-func (d *Driver) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (
+func (d *CsiDriver) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (
*csi.ControllerGetVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
diff --git a/csi/driver/controller_helper.go b/csi/driver/controller_helper.go
index 7ad87bdc..db5d8f60 100644
--- a/csi/driver/controller_helper.go
+++ b/csi/driver/controller_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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,7 +35,6 @@ import (
"huawei-csi-driver/csi/backend/model"
"huawei-csi-driver/csi/backend/plugin"
"huawei-csi-driver/pkg/constants"
- pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
@@ -50,9 +49,10 @@ const (
maxDescriptionLength = 255
- volumeTypeDTree = "dtree"
- volumeTypeFileSystem = "fs"
- volumeTypeLun = "lun"
+ volumeTypeDTree = "dtree"
+ volumeTypeFileSystem = "fs"
+ volumeTypeLun = "lun"
+ maxReservedSnapshotSpaceRatio = 50
)
var (
@@ -361,7 +361,7 @@ func checkReservedSnapshotSpaceRatio(ctx context.Context, parameters map[string]
return errors.New(errMsg)
}
- if reservedSnapshotSpaceRatio < 0 || reservedSnapshotSpaceRatio > 50 {
+ if reservedSnapshotSpaceRatio < 0 || reservedSnapshotSpaceRatio > maxReservedSnapshotSpaceRatio {
errMsg := fmt.Sprintf("reservedSnapshotSpaceRatio: [%v] must in range [0, 50], please check this "+
"parameter in storageclass.", reservedSnapshotSpaceRatioString)
log.AddContext(ctx).Errorln(errMsg)
@@ -446,7 +446,7 @@ func processCreateVolumeParametersAfterSelect(parameters map[string]interface{},
}
// createVolume used to create a lun/filesystem in huawei storage
-func (d *Driver) createVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
+func (d *CsiDriver) createVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
parameters, err := processCreateVolumeParameters(ctx, req)
if err != nil {
return nil, err
@@ -470,15 +470,12 @@ func (d *Driver) createVolume(ctx context.Context, req *csi.CreateVolumeRequest)
Volume: makeCreateVolumeResponse(ctx, req, vol, storagePoolPair.Local),
}
- // The topology creation result does not affect current task.
- go pkgUtils.CreatePVLabel(req.GetName(), res.GetVolume().GetVolumeId())
-
return res, nil
}
// In the volume import scenario, only the fields in the annotation are obtained.
// Other information are ignored (e.g. the capacity, backend, and QoS ...).
-func (d *Driver) manageVolume(ctx context.Context, req *csi.CreateVolumeRequest, volumeName, backendName string) (
+func (d *CsiDriver) manageVolume(ctx context.Context, req *csi.CreateVolumeRequest, volumeName, backendName string) (
*csi.CreateVolumeResponse, error) {
log.AddContext(ctx).Infof("Start to manage Volume %s for backend %s.", volumeName, backendName)
selectBackend, err := d.backendSelector.SelectBackend(ctx, helper.GetBackendName(backendName))
@@ -527,9 +524,6 @@ func (d *Driver) manageVolume(ctx context.Context, req *csi.CreateVolumeRequest,
req.GetCapacityRange().GetRequiredBytes()),
}
- // The topology creation result does not affect current task.
- go pkgUtils.CreatePVLabel(req.GetName(), res.GetVolume().GetVolumeId())
-
return res, nil
}
diff --git a/csi/driver/controller_helper_test.go b/csi/driver/controller_helper_test.go
index 0f52f3dd..ee1d9d1b 100644
--- a/csi/driver/controller_helper_test.go
+++ b/csi/driver/controller_helper_test.go
@@ -22,18 +22,18 @@ import (
"reflect"
"testing"
+ "github.com/stretchr/testify/require"
+
"huawei-csi-driver/csi/backend/model"
"github.com/agiledragon/gomonkey/v2"
"github.com/container-storage-interface/spec/lib/go/csi"
"github.com/prashantv/gostub"
- "github.com/smartystreets/goconvey/convey"
"huawei-csi-driver/csi/app"
cfg "huawei-csi-driver/csi/app/config"
"huawei-csi-driver/csi/backend/handler"
"huawei-csi-driver/csi/backend/plugin"
- pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
@@ -53,32 +53,32 @@ func TestMain(m *testing.M) {
}
func TestCheckReservedSnapshotSpaceRatio(t *testing.T) {
- convey.Convey("Normal", t, func() {
+ t.Run("Normal", func(t *testing.T) {
param := map[string]interface{}{
"reservedSnapshotSpaceRatio": "50",
}
- convey.So(checkReservedSnapshotSpaceRatio(context.TODO(), param), convey.ShouldBeNil)
+ require.NoError(t, checkReservedSnapshotSpaceRatio(context.TODO(), param))
})
- convey.Convey("Not int", t, func() {
+ t.Run("Not int", func(t *testing.T) {
param := map[string]interface{}{
"reservedSnapshotSpaceRatio": "20%",
}
- convey.So(checkReservedSnapshotSpaceRatio(context.TODO(), param), convey.ShouldBeError)
+ require.Error(t, checkReservedSnapshotSpaceRatio(context.TODO(), param))
})
- convey.Convey("Exceed the upper limit", t, func() {
+ t.Run("Exceed the upper limit", func(t *testing.T) {
param := map[string]interface{}{
"reservedSnapshotSpaceRatio": "60",
}
- convey.So(checkReservedSnapshotSpaceRatio(context.TODO(), param), convey.ShouldBeError)
+ require.Error(t, checkReservedSnapshotSpaceRatio(context.TODO(), param))
})
- convey.Convey("Below the lower limit", t, func() {
+ t.Run("Below the lower limit", func(t *testing.T) {
param := map[string]interface{}{
"reservedSnapshotSpaceRatio": "-10",
}
- convey.So(checkReservedSnapshotSpaceRatio(context.TODO(), param), convey.ShouldBeError)
+ require.Error(t, checkReservedSnapshotSpaceRatio(context.TODO(), param))
})
}
@@ -100,8 +100,8 @@ func mockCreateRequest() *csi.CreateVolumeRequest {
}
}
-func initDriver() *Driver {
- return NewDriver(app.GetGlobalConfig().DriverName,
+func initDriver() *CsiDriver {
+ return NewServer(app.GetGlobalConfig().DriverName,
"csiVersion",
app.GetGlobalConfig().K8sUtils,
app.GetGlobalConfig().NodeName)
@@ -121,9 +121,6 @@ func TestCreateVolumeWithoutBackend(t *testing.T) {
driver := initDriver()
req := mockCreateRequest()
- s := gostub.StubFunc(&pkgUtils.CreatePVLabel)
- defer s.Reset()
-
m := gomonkey.ApplyMethod(reflect.TypeOf(driver.backendSelector), "SelectPoolPair",
func(hander *handler.BackendSelector, ctx context.Context, requestSize int64,
parameters map[string]interface{}) (*model.SelectPoolPair, error) {
@@ -139,8 +136,6 @@ func TestCreateVolumeWithoutBackend(t *testing.T) {
func TestCreateVolume(t *testing.T) {
driver := initDriver()
- s := gostub.StubFunc(&pkgUtils.CreatePVLabel)
- defer s.Reset()
m := gomonkey.ApplyMethod(reflect.TypeOf(driver.backendSelector), "SelectPoolPair",
func(hander *handler.BackendSelector, ctx context.Context, requestSize int64,
parameters map[string]interface{}) (*model.SelectPoolPair, error) {
@@ -180,9 +175,6 @@ func TestImportVolumeWithoutBackend(t *testing.T) {
})
defer m.Reset()
- s := gostub.StubFunc(&pkgUtils.CreatePVLabel)
- defer s.Reset()
-
_, err := driver.manageVolume(context.TODO(), req, "fake-nfs", "fake-backend")
if err == nil {
t.Error("test import without backend failed")
@@ -193,8 +185,6 @@ func TestImportVolume(t *testing.T) {
plg := plugin.GetPlugin("oceanstor-nas")
localPool := initPool("local-pool")
- s := gostub.StubFunc(&pkgUtils.CreatePVLabel)
- defer s.Reset()
driver := initDriver()
m := gomonkey.ApplyMethod(reflect.TypeOf(driver.backendSelector), "SelectBackend",
func(hander *handler.BackendSelector, ctx context.Context, backendName string) (*model.Backend, error) {
diff --git a/csi/driver/driver.go b/csi/driver/driver.go
index 1e0bad77..95d61143 100644
--- a/csi/driver/driver.go
+++ b/csi/driver/driver.go
@@ -23,8 +23,8 @@ import (
"huawei-csi-driver/utils/k8sutils"
)
-// Driver defines csi driver
-type Driver struct {
+// CsiDriver defines csi driver
+type CsiDriver struct {
name string
version string
k8sUtils k8sutils.Interface
@@ -32,9 +32,9 @@ type Driver struct {
backendSelector handler.BackendSelectInterface
}
-// NewDriver used to inits a new driver
-func NewDriver(name, version string, k8sUtils k8sutils.Interface, nodeName string) *Driver {
- return &Driver{
+// NewServer used to inits a new driver
+func NewServer(name, version string, k8sUtils k8sutils.Interface, nodeName string) *CsiDriver {
+ return &CsiDriver{
name: name,
version: version,
k8sUtils: k8sUtils,
diff --git a/csi/driver/identity.go b/csi/driver/identity.go
index c37b11d3..822d172b 100644
--- a/csi/driver/identity.go
+++ b/csi/driver/identity.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -19,13 +19,14 @@ package driver
import (
"context"
- "huawei-csi-driver/utils/log"
-
"github.com/container-storage-interface/spec/lib/go/csi"
+
+ "huawei-csi-driver/utils/log"
)
// GetPluginInfo used to get plugin info
-func (d *Driver) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {
+func (d *CsiDriver) GetPluginInfo(ctx context.Context,
+ req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {
log.AddContext(ctx).Infof("Get plugin info %v", *d)
return &csi.GetPluginInfoResponse{
Name: d.name,
@@ -34,7 +35,8 @@ func (d *Driver) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoReques
}
// GetPluginCapabilities used to get plugin capabilities
-func (d *Driver) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) {
+func (d *CsiDriver) GetPluginCapabilities(ctx context.Context,
+ req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) {
log.AddContext(ctx).Infof("Get plugin capabilities of %v", *d)
return &csi.GetPluginCapabilitiesResponse{
@@ -58,7 +60,7 @@ func (d *Driver) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCa
}
// Probe used to health check
-func (d *Driver) Probe(ctx context.Context, req *csi.ProbeRequest) (*csi.ProbeResponse, error) {
+func (d *CsiDriver) Probe(ctx context.Context, req *csi.ProbeRequest) (*csi.ProbeResponse, error) {
log.AddContext(ctx).Debugf("Probe plugin %v", *d)
return &csi.ProbeResponse{}, nil
}
diff --git a/csi/driver/node.go b/csi/driver/node.go
index 991091aa..7f3cfdcf 100644
--- a/csi/driver/node.go
+++ b/csi/driver/node.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -36,8 +36,11 @@ import (
"huawei-csi-driver/utils/log"
)
+const checkSymlinkPathTimeout = 10 * time.Second
+
// NodeStageVolume used to stage volume
-func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
+func (d *CsiDriver) NodeStageVolume(ctx context.Context,
+ req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
volumeId := req.GetVolumeId()
@@ -61,7 +64,8 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
}
// NodeUnstageVolume used to unstage volume
-func (d *Driver) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
+func (d *CsiDriver) NodeUnstageVolume(ctx context.Context,
+ req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
volumeId := req.GetVolumeId()
targetPath := req.GetStagingTargetPath()
@@ -86,7 +90,7 @@ func (d *Driver) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolu
}
// NodePublishVolume used to node publish volume
-func (d *Driver) NodePublishVolume(ctx context.Context,
+func (d *CsiDriver) NodePublishVolume(ctx context.Context,
req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
volumeId := req.GetVolumeId()
@@ -105,14 +109,12 @@ func (d *Driver) NodePublishVolume(ctx context.Context,
}
}
- go nodeAddLabel(utils.NewContextWithRequestID(), volumeId, targetPath)
-
log.AddContext(ctx).Infof("Volume %s is node published from %s", volumeId, targetPath)
return &csi.NodePublishVolumeResponse{}, nil
}
// NodeUnpublishVolume used to node unpublish volume
-func (d *Driver) NodeUnpublishVolume(ctx context.Context,
+func (d *CsiDriver) NodeUnpublishVolume(ctx context.Context,
req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -137,7 +139,7 @@ func (d *Driver) NodeUnpublishVolume(ctx context.Context,
}
}
} else {
- symLink, err := utils.IsPathSymlinkWithTimeout(targetPath, 10*time.Second)
+ symLink, err := utils.IsPathSymlinkWithTimeout(targetPath, checkSymlinkPathTimeout)
if err != nil {
log.AddContext(ctx).Errorf("Failed to Access path %s, error: %v", targetPath, err)
return nil, status.Error(codes.Internal, err.Error())
@@ -152,14 +154,12 @@ func (d *Driver) NodeUnpublishVolume(ctx context.Context,
}
}
- go nodeDeleteLabel(utils.NewContextWithRequestID(), volumeId, targetPath)
-
log.AddContext(ctx).Infof("Volume %s is node unpublished from %s", volumeId, targetPath)
return &csi.NodeUnpublishVolumeResponse{}, nil
}
// NodeGetInfo used to get node info
-func (d *Driver) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
+func (d *CsiDriver) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
defer utils.RecoverPanic(ctx)
hostname, err := utils.GetHostName(ctx)
if err != nil {
@@ -202,7 +202,8 @@ func (d *Driver) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (
}
// NodeGetCapabilities used to get node capabilities
-func (d *Driver) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
+func (d *CsiDriver) NodeGetCapabilities(ctx context.Context,
+ req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
defer utils.RecoverPanic(ctx)
return &csi.NodeGetCapabilitiesResponse{
Capabilities: []*csi.NodeServiceCapability{
@@ -232,7 +233,8 @@ func (d *Driver) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabi
}
// NodeGetVolumeStats used to get node volume status
-func (d *Driver) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
+func (d *CsiDriver) NodeGetVolumeStats(ctx context.Context,
+ req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
defer utils.RecoverPanic(ctx)
volumeID := req.GetVolumeId()
if len(volumeID) == 0 {
@@ -317,7 +319,7 @@ func (d *Driver) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeS
}
// NodeExpandVolume used to node expand volume
-func (d *Driver) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (
+func (d *CsiDriver) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (
*csi.NodeExpandVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
diff --git a/csi/garbage_collector.go b/csi/garbage_collector.go
index 613e0afe..7af85658 100644
--- a/csi/garbage_collector.go
+++ b/csi/garbage_collector.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -42,7 +42,8 @@ const (
// deviceDirPath is a relative path inside kubelet root directory
relativeDevicePath = "/kubelet/plugins/kubernetes.io/csi/volumeDevices/*/data/vol_data.json"
// in case of block,the index of the last occurrence of the specified pv name
- // For example,the path is "/var/lib/kubelet/plugins/kubernetes.io/csi/volumeDevices/pvc-123/data/vol_data.json", we get pvc-123
+ // For example,the path is
+ // "/var/lib/kubelet/plugins/kubernetes.io/csi/volumeDevices/pvc-123/data/vol_data.json", we get pvc-123
deviceLastIndex = 3
)
diff --git a/csi/main.go b/csi/main.go
index 5a8a0c8f..b9963dcf 100644
--- a/csi/main.go
+++ b/csi/main.go
@@ -38,7 +38,6 @@ import (
"huawei-csi-driver/csi/driver"
"huawei-csi-driver/csi/provider"
"huawei-csi-driver/lib/drcsi"
- labelLock "huawei-csi-driver/pkg/utils/label_lock"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
"huawei-csi-driver/utils/notify"
@@ -50,7 +49,7 @@ const (
controllerLogFile = "huawei-csi-controller"
nodeLogFile = "huawei-csi-node"
- csiVersion = "4.4.0"
+ csiVersion = "4.5.0"
endpointDirPerm = 0755
)
@@ -102,9 +101,6 @@ func runCSIController(ctx context.Context) {
// register the kahu community DRCSI service
go registerDRCSIServer()
- // create and refresh lock for rt
- go labelLock.InitCmLock(ctx, labelLock.RTLockConfigMap)
-
// register the K8S community CSI service
registerCSIServer()
}
@@ -147,7 +143,7 @@ func main() {
}
// Init logger
- err := log.InitLogging(&log.LoggingRequest{
+ err := log.InitLogging(&log.Config{
LogName: getLogFileName(),
LogFileSize: app.GetGlobalConfig().LogFileSize,
LoggingModule: app.GetGlobalConfig().LoggingModule,
@@ -184,7 +180,7 @@ func registerDRCSIServer() {
}
func registerCSIServer() {
- d := driver.NewDriver(app.GetGlobalConfig().DriverName,
+ d := driver.NewServer(app.GetGlobalConfig().DriverName,
csiVersion,
app.GetGlobalConfig().K8sUtils,
app.GetGlobalConfig().NodeName)
@@ -217,7 +213,7 @@ func listenEndpoint(endpoint string) net.Listener {
return listener
}
-func registerServer(listener net.Listener, d *driver.Driver) {
+func registerServer(listener net.Listener, d *driver.CsiDriver) {
opts := []grpc.ServerOption{
grpc.UnaryInterceptor(log.EnsureGRPCContext),
}
diff --git a/csi/manage/manager_helper.go b/csi/manage/manager_helper.go
index ca6abdf1..7017db81 100644
--- a/csi/manage/manager_helper.go
+++ b/csi/manage/manager_helper.go
@@ -28,7 +28,7 @@ import (
"github.com/container-storage-interface/spec/lib/go/csi"
"huawei-csi-driver/connector"
- _ "huawei-csi-driver/connector/nfs_plus"
+ _ "huawei-csi-driver/connector/nfsplus"
"huawei-csi-driver/csi/app"
"huawei-csi-driver/csi/backend"
"huawei-csi-driver/csi/backend/plugin"
@@ -116,7 +116,7 @@ func WithPortals(publishContext map[string]string, protocol string, portals, met
}
// WithConnector build connector for the request parameters
-func WithConnector(conn connector.Connector) BuildParameterOption {
+func WithConnector(conn connector.VolumeConnector) BuildParameterOption {
return func(parameters map[string]interface{}) error {
parameters["connector"] = conn
return nil
@@ -231,7 +231,7 @@ func Unmount(ctx context.Context, targetPath string) error {
}
// NewManager build a manager instance, such as NasManager, SanManager
-func NewManager(ctx context.Context, backendName string) (Manager, error) {
+func NewManager(ctx context.Context, backendName string) (VolumeManager, error) {
backend, err := GetBackendConfig(ctx, backendName)
if err != nil {
log.AddContext(ctx).Errorf("nas manager get backend failed, backendName: %s err: %v", backendName, err)
@@ -249,7 +249,7 @@ func NewManager(ctx context.Context, backendName string) (Manager, error) {
return nil, utils.Errorf(ctx, "portals can not be blank when protocol is %s", plugin.ProtocolNfsPlus)
}
return NewNasManager(ctx, backend.protocol, backend.dTreeParentName, backend.portals, backend.metroPortals)
- case plugin.PROTOCOL_DPC:
+ case plugin.ProtocolDpc:
return NewNasManager(ctx, backend.protocol, backend.dTreeParentName, []string{}, []string{})
default:
return NewSanManager(ctx, backend.protocol)
@@ -401,10 +401,10 @@ func PublishFilesystem(ctx context.Context, req *csi.NodePublishVolumeRequest) e
return nil
}
-func getConnectorByProtocol(ctx context.Context, protocol string) connector.Connector {
- return map[string]connector.Connector{
+func getConnectorByProtocol(ctx context.Context, protocol string) connector.VolumeConnector {
+ return map[string]connector.VolumeConnector{
plugin.ProtocolNfs: connector.GetConnector(ctx, connector.NFSDriver),
- plugin.PROTOCOL_DPC: connector.GetConnector(ctx, connector.NFSDriver),
+ plugin.ProtocolDpc: connector.GetConnector(ctx, connector.NFSDriver),
plugin.ProtocolNfsPlus: connector.GetConnector(ctx, connector.NFSPlusDriver),
}[protocol]
}
diff --git a/csi/manage/manager_helper_test.go b/csi/manage/manager_helper_test.go
index 566e2266..4cceb56d 100644
--- a/csi/manage/manager_helper_test.go
+++ b/csi/manage/manager_helper_test.go
@@ -43,7 +43,7 @@ type testCaseStructForNewManager struct {
name string
protocol string
backendName string
- want Manager
+ want VolumeManager
wantErr bool
}
diff --git a/csi/manage/nas_manager.go b/csi/manage/nas_manager.go
index 755205c5..49426008 100644
--- a/csi/manage/nas_manager.go
+++ b/csi/manage/nas_manager.go
@@ -28,18 +28,18 @@ import (
"huawei-csi-driver/utils/log"
)
-// NasManager implements Manager interface
+// NasManager implements VolumeManager interface
type NasManager struct {
protocol string
portals []string
metroPortals []string
dTreeParentName string
- Conn connector.Connector
+ Conn connector.VolumeConnector
}
// NewNasManager build a nas manager instance according to the protocol
-func NewNasManager(ctx context.Context, protocol, dTreeParentName string, portals, metroPortals []string) (Manager,
- error) {
+func NewNasManager(ctx context.Context,
+ protocol, dTreeParentName string, portals, metroPortals []string) (VolumeManager, error) {
return &NasManager{
protocol: protocol,
portals: portals,
@@ -73,7 +73,7 @@ func (m *NasManager) StageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
var sourcePath string
switch m.protocol {
- case plugin.PROTOCOL_DPC:
+ case plugin.ProtocolDpc:
sourcePath = "/" + volumeName
case plugin.ProtocolNfs, plugin.ProtocolNfsPlus:
sourcePath = m.portals[0] + ":/" + volumeName
diff --git a/csi/manage/san_manager.go b/csi/manage/san_manager.go
index d7e6c7bc..45cdfb7b 100644
--- a/csi/manage/san_manager.go
+++ b/csi/manage/san_manager.go
@@ -24,19 +24,19 @@ import (
"huawei-csi-driver/connector"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
)
-// SanManager implements Manager interface
+// SanManager implements VolumeManager interface
type SanManager struct {
- Conn connector.Connector
+ Conn connector.VolumeConnector
protocol string
}
// NewSanManager build a san manager instance according to the protocol
-func NewSanManager(ctx context.Context, protocol string) (Manager, error) {
- var conn connector.Connector
+func NewSanManager(ctx context.Context, protocol string) (VolumeManager, error) {
+ var conn connector.VolumeConnector
switch protocol {
case "iscsi":
conn = connector.GetConnector(ctx, connector.ISCSIDriver)
@@ -74,7 +74,7 @@ func (m *SanManager) StageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
return err
}
- tasks := taskflow.NewTaskFlow(ctx, "StageVolume").
+ tasks := flow.NewTaskFlow(ctx, "StageVolume").
AddTaskWithOutRevert(clearResidualPathWithWwn).
AddTaskWithOutRevert(clearResidualPathWithLunId).
AddTaskWithOutRevert(connectVolume)
@@ -217,7 +217,12 @@ func clearResidualPathWithWwn(ctx context.Context, parameters map[string]interfa
return err
}
- return connector.ClearResidualPath(ctx, wwn, parameters["volumeMode"])
+ publishInfo, exist := parameters["publishInfo"].(*ControllerPublishInfo)
+ if !exist {
+ return errors.New("build multiPathType failed, caused by publishInfo is not exist")
+ }
+
+ return connector.ClearResidualPath(ctx, wwn, parameters["volumeMode"], publishInfo.MultiPathType)
}
func connectVolume(ctx context.Context, parameters map[string]interface{}) error {
@@ -228,7 +233,7 @@ func connectVolume(ctx context.Context, parameters map[string]interface{}) error
}
connectionParams := publishInfo.ReflectToMap()
- conn, exist := parameters["connector"].(connector.Connector)
+ conn, exist := parameters["connector"].(connector.VolumeConnector)
if !exist {
return errors.New("connector doesn't exist while connect volume")
}
diff --git a/csi/manage/san_manager_test.go b/csi/manage/san_manager_test.go
index 12d3b330..a2aa7232 100644
--- a/csi/manage/san_manager_test.go
+++ b/csi/manage/san_manager_test.go
@@ -41,7 +41,7 @@ func TestSanManagerStageFileSystemVolume(t *testing.T) {
tests := []struct {
name string
manager *SanManager
- connectVolumeFunc func(patch *gomonkey.Patches, conn connector.Connector)
+ connectVolumeFunc func(patch *gomonkey.Patches, conn connector.VolumeConnector)
wantErr bool
}{
{
@@ -139,9 +139,9 @@ func mockClearResidualPath(patch *gomonkey.Patches, protocol string) {
})
}
-func mockConnectIscsiVolume(patch *gomonkey.Patches, conn connector.Connector) {
+func mockConnectIscsiVolume(patch *gomonkey.Patches, conn connector.VolumeConnector) {
patch.ApplyMethod(reflect.TypeOf(conn), "ConnectVolume",
- func(_ *iscsi.ISCSI, ctx context.Context, params map[string]interface{}) (string, error) {
+ func(_ *iscsi.Connector, ctx context.Context, params map[string]interface{}) (string, error) {
want := map[string]interface{}{
"tgtPortals": []string{"mock_tgt_portal_1"},
"tgtIQNs": []string{"mock_tgt_iqn_1"},
@@ -160,9 +160,9 @@ func mockConnectIscsiVolume(patch *gomonkey.Patches, conn connector.Connector) {
})
}
-func mockConnectFcVolume(patch *gomonkey.Patches, conn connector.Connector) {
+func mockConnectFcVolume(patch *gomonkey.Patches, conn connector.VolumeConnector) {
patch.ApplyMethod(reflect.TypeOf(conn), "ConnectVolume",
- func(_ *fibrechannel.FibreChannel, ctx context.Context, params map[string]interface{}) (string, error) {
+ func(_ *fibrechannel.Connector, ctx context.Context, params map[string]interface{}) (string, error) {
want := map[string]interface{}{
"tgtWWNs": []string{"mock_wwn_1"},
"tgtHostLUNs": []string{"mock_host_lun_1"},
@@ -180,9 +180,9 @@ func mockConnectFcVolume(patch *gomonkey.Patches, conn connector.Connector) {
})
}
-func mockConnectRoceVolume(patch *gomonkey.Patches, conn connector.Connector) {
+func mockConnectRoceVolume(patch *gomonkey.Patches, conn connector.VolumeConnector) {
patch.ApplyMethod(reflect.TypeOf(conn), "ConnectVolume",
- func(_ *roce.RoCE, ctx context.Context, params map[string]interface{}) (string, error) {
+ func(_ *roce.Connector, ctx context.Context, params map[string]interface{}) (string, error) {
want := map[string]interface{}{
"tgtPortals": []string{"mock_tgt_portal_1"},
"tgtLunGuid": "mock_lun_guid_1",
@@ -199,7 +199,7 @@ func mockConnectRoceVolume(patch *gomonkey.Patches, conn connector.Connector) {
})
}
-func mockConnectFcNvmeVolume(patch *gomonkey.Patches, conn connector.Connector) {
+func mockConnectFcNvmeVolume(patch *gomonkey.Patches, conn connector.VolumeConnector) {
patch.ApplyMethod(reflect.TypeOf(conn), "ConnectVolume",
func(_ *nvme.FCNVMe, ctx context.Context, params map[string]interface{}) (string, error) {
want := map[string]interface{}{
diff --git a/csi/manage/types.go b/csi/manage/types.go
index 960dfb2c..866c018a 100644
--- a/csi/manage/types.go
+++ b/csi/manage/types.go
@@ -24,8 +24,8 @@ import (
"huawei-csi-driver/connector/nvme"
)
-// Manager defines the operations which storage manager should implement
-type Manager interface {
+// VolumeManager defines the operations which storage manager should implement
+type VolumeManager interface {
StageVolume(context.Context, *csi.NodeStageVolumeRequest) error
UnStageVolume(context.Context, *csi.NodeUnstageVolumeRequest) error
ExpandVolume(context.Context, *csi.NodeExpandVolumeRequest) error
diff --git a/csi/provider/backend.go b/csi/provider/backend.go
index 5b24ae1e..ef7fcd85 100644
--- a/csi/provider/backend.go
+++ b/csi/provider/backend.go
@@ -31,7 +31,7 @@ import (
)
// AddStorageBackend used to add storage backend, and return the backend ID
-func (p *Provider) AddStorageBackend(ctx context.Context, req *drcsi.AddStorageBackendRequest) (
+func (p *StorageProvider) AddStorageBackend(ctx context.Context, req *drcsi.AddStorageBackendRequest) (
*drcsi.AddStorageBackendResponse, error) {
log.AddContext(ctx).Infof("Start to add storage backend %s.", req.Name)
@@ -49,7 +49,7 @@ func (p *Provider) AddStorageBackend(ctx context.Context, req *drcsi.AddStorageB
}
// RemoveStorageBackend remove the backend id in current provider
-func (p *Provider) RemoveStorageBackend(ctx context.Context, req *drcsi.RemoveStorageBackendRequest) (
+func (p *StorageProvider) RemoveStorageBackend(ctx context.Context, req *drcsi.RemoveStorageBackendRequest) (
*drcsi.RemoveStorageBackendResponse, error) {
log.AddContext(ctx).Infof("Start to remove storage backend %s.", req.BackendId)
@@ -68,7 +68,7 @@ func (p *Provider) RemoveStorageBackend(ctx context.Context, req *drcsi.RemoveSt
}
// UpdateStorageBackend update the backend within backend id
-func (p *Provider) UpdateStorageBackend(ctx context.Context, req *drcsi.UpdateStorageBackendRequest) (
+func (p *StorageProvider) UpdateStorageBackend(ctx context.Context, req *drcsi.UpdateStorageBackendRequest) (
*drcsi.UpdateStorageBackendResponse, error) {
// In the current version, the CSI supports only password change, which is verified through webhook.
@@ -100,7 +100,7 @@ func (p *Provider) UpdateStorageBackend(ctx context.Context, req *drcsi.UpdateSt
}
// GetBackendStats used to update the storage backend status
-func (p *Provider) GetBackendStats(ctx context.Context, req *drcsi.GetBackendStatsRequest) (
+func (p *StorageProvider) GetBackendStats(ctx context.Context, req *drcsi.GetBackendStatsRequest) (
*drcsi.GetBackendStatsResponse, error) {
log.AddContext(ctx).Debugf("Start to get storage backend %s status.", req.BackendId)
@@ -120,7 +120,7 @@ func (p *Provider) GetBackendStats(ctx context.Context, req *drcsi.GetBackendSta
return nil, errors.New(msg)
}
- details, err := p.storageService.GetBackendDetails(ctx, backendName)
+ details, err := p.storageService.GetBackendDetails(ctx, backendName, req.Name)
if err != nil {
log.AddContext(ctx).Errorf("get backend details failed, error: %v", err)
return nil, err
@@ -140,7 +140,7 @@ func (p *Provider) GetBackendStats(ctx context.Context, req *drcsi.GetBackendSta
return response, nil
}
-func (p *Provider) registerOrUpdateOneBackend(ctx context.Context, name, backendId string,
+func (p *StorageProvider) registerOrUpdateOneBackend(ctx context.Context, name, backendId string,
response *drcsi.GetBackendStatsResponse) {
var err error
var sbct *v1.StorageBackendContent
diff --git a/csi/provider/identity.go b/csi/provider/identity.go
index a933225b..8bda1d86 100644
--- a/csi/provider/identity.go
+++ b/csi/provider/identity.go
@@ -25,7 +25,7 @@ import (
)
// GetProviderInfo is used to get provider info
-func (p *Provider) GetProviderInfo(ctx context.Context, req *drcsi.GetProviderInfoRequest) (
+func (p *StorageProvider) GetProviderInfo(ctx context.Context, req *drcsi.GetProviderInfoRequest) (
*drcsi.GetProviderInfoResponse, error) {
log.AddContext(ctx).Infof("Get provider info %v", *p)
@@ -36,7 +36,7 @@ func (p *Provider) GetProviderInfo(ctx context.Context, req *drcsi.GetProviderIn
}
// GetProviderCapabilities is used to get provider capabilities
-func (p *Provider) GetProviderCapabilities(ctx context.Context, req *drcsi.GetProviderCapabilitiesRequest) (
+func (p *StorageProvider) GetProviderCapabilities(ctx context.Context, req *drcsi.GetProviderCapabilitiesRequest) (
*drcsi.GetProviderCapabilitiesResponse, error) {
log.AddContext(ctx).Infof("Get plugin capabilities of %v", *p)
@@ -54,7 +54,7 @@ func (p *Provider) GetProviderCapabilities(ctx context.Context, req *drcsi.GetPr
}
// Probe is used to probe provider
-func (p *Provider) Probe(ctx context.Context, in *drcsi.ProbeRequest) (*drcsi.ProbeResponse, error) {
+func (p *StorageProvider) Probe(ctx context.Context, in *drcsi.ProbeRequest) (*drcsi.ProbeResponse, error) {
log.AddContext(ctx).Infof("Probe invoked of %v, request: %v", *p, in)
return &drcsi.ProbeResponse{}, nil
}
diff --git a/csi/provider/provider.go b/csi/provider/provider.go
index d6a18628..c0c1a1e9 100644
--- a/csi/provider/provider.go
+++ b/csi/provider/provider.go
@@ -19,8 +19,8 @@ package provider
import "huawei-csi-driver/csi/backend/handler"
-// Provider is for storage provider
-type Provider struct {
+// StorageProvider is for storage provider
+type StorageProvider struct {
name string
version string
storageService handler.StorageServiceInterface
@@ -31,8 +31,8 @@ type Provider struct {
}
// NewProvider is used to create storage provider
-func NewProvider(name, version string) *Provider {
- return &Provider{
+func NewProvider(name, version string) *StorageProvider {
+ return &StorageProvider{
name: name,
version: version,
storageService: handler.NewStorageHandler(),
diff --git a/csi/provider/volume.go b/csi/provider/volume.go
index fa01d329..a7175e6c 100644
--- a/csi/provider/volume.go
+++ b/csi/provider/volume.go
@@ -34,7 +34,7 @@ const (
)
// ModifyVolume is used to modify volume attribute
-func (p *Provider) ModifyVolume(ctx context.Context, req *drcsi.ModifyVolumeRequest) (
+func (p *StorageProvider) ModifyVolume(ctx context.Context, req *drcsi.ModifyVolumeRequest) (
*drcsi.ModifyVolumeResponse, error) {
defer utils.RecoverPanic(ctx)
@@ -49,7 +49,7 @@ func (p *Provider) ModifyVolume(ctx context.Context, req *drcsi.ModifyVolumeRequ
return &drcsi.ModifyVolumeResponse{}, nil
}
-func (p *Provider) modifyHyperMetro(ctx context.Context, req *drcsi.ModifyVolumeRequest) (
+func (p *StorageProvider) modifyHyperMetro(ctx context.Context, req *drcsi.ModifyVolumeRequest) (
*drcsi.ModifyVolumeResponse, error) {
// Determine whether the operation is a conversion between a local volume and a HyperMetro volume.
@@ -84,7 +84,7 @@ func (p *Provider) modifyHyperMetro(ctx context.Context, req *drcsi.ModifyVolume
return nil, errors.New(errMsg)
}
- params := pkgUtils.CombineMap(req.StorageClassParameters, req.MutableParameters)
+ params := pkgUtils.CombineMap(req.MutableParameters, req.StorageClassParameters)
remotePool, err := p.backendSelector.SelectRemotePool(ctx, thinVolumeRequestSize, backendName,
pkgUtils.ConvertMapString2MapInterface(params))
if err != nil {
@@ -94,9 +94,9 @@ func (p *Provider) modifyHyperMetro(ctx context.Context, req *drcsi.ModifyVolume
}
if remotePool != nil {
- req.StorageClassParameters["remoteStoragePool"] = remotePool.Name
+ params["remoteStoragePool"] = remotePool.Name
}
- err = bk.Plugin.ModifyVolume(ctx, req.VolumeId, modifyType, req.StorageClassParameters)
+ err = bk.Plugin.ModifyVolume(ctx, req.VolumeId, modifyType, params)
if err != nil {
errMsg := fmt.Sprintf("modify volume failed, volume name: %s, modify type: %v, error: %v",
volumeName, modifyType, err)
diff --git a/csi/provider/volume_test.go b/csi/provider/volume_test.go
index 9724ea84..a78496e7 100644
--- a/csi/provider/volume_test.go
+++ b/csi/provider/volume_test.go
@@ -85,6 +85,7 @@ func TestModifyVolume_HyperMetroTrue(t *testing.T) {
func(*handler.BackendSelector, context.Context, int64, string, map[string]interface{}) (*model.StoragePool, error) {
return &model.StoragePool{Name: "remotePoolName"}, nil
})
+ m.ApplyPrivateMethod(nasPlugin, "canModify", func() error { return nil })
m.ApplyMethod(reflect.TypeOf(nasPlugin), "GetLocal2HyperMetroParameters",
func(p *plugin.OceanstorNasPlugin, ctx context.Context, VolumeId string, parameters map[string]string) (
map[string]interface{}, error) {
diff --git a/docs/.keepdir b/docs/.keepdir
deleted file mode 100644
index e69de29b..00000000
diff --git a/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.4.0 User Guide 01.pdf b/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.4.0 User Guide 01.pdf
deleted file mode 100644
index f0f203af..00000000
Binary files a/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.4.0 User Guide 01.pdf and /dev/null differ
diff --git "a/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.4.0 \347\224\250\346\210\267\346\214\207\345\215\227 01.pdf" "b/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.4.0 \347\224\250\346\210\267\346\214\207\345\215\227 01.pdf"
deleted file mode 100644
index 6f33a6e3..00000000
Binary files "a/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.4.0 \347\224\250\346\210\267\346\214\207\345\215\227 01.pdf" and /dev/null differ
diff --git a/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.5.0 User Guide 01.pdf b/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.5.0 User Guide 01.pdf
new file mode 100644
index 00000000..6ad0ab22
Binary files /dev/null and b/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.5.0 User Guide 01.pdf differ
diff --git "a/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.5.0 \347\224\250\346\210\267\346\214\207\345\215\227 01.pdf" "b/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.5.0 \347\224\250\346\210\267\346\214\207\345\215\227 01.pdf"
new file mode 100644
index 00000000..bde09445
Binary files /dev/null and "b/docs/eSDK Huawei Storage Kubernetes CSI Plugins V4.5.0 \347\224\250\346\210\267\346\214\207\345\215\227 01.pdf" differ
diff --git a/go.mod b/go.mod
index f1c8d600..4394b63e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,29 +1,25 @@
module huawei-csi-driver
-go 1.20
+go 1.22
require (
- bou.ke/monkey v1.0.2
github.com/agiledragon/gomonkey/v2 v2.9.0
- github.com/container-storage-interface/spec v1.6.0
- github.com/ghodss/yaml v1.0.0
+ github.com/container-storage-interface/spec v1.8.0
github.com/golang/mock v1.4.4
github.com/golang/protobuf v1.5.3
github.com/kubernetes-csi/csi-lib-utils v0.11.0
github.com/prashantv/gostub v1.1.0
- github.com/sirupsen/logrus v1.8.0
- github.com/smartystreets/goconvey v1.7.2
- github.com/spf13/cobra v1.4.0
+ github.com/sirupsen/logrus v1.8.2
+ github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
- golang.org/x/sys v0.17.0
+ golang.org/x/sys v0.24.0
google.golang.org/grpc v1.57.2
google.golang.org/protobuf v1.32.0
- gopkg.in/yaml.v2 v2.4.0
+ gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/client-go v0.29.2
k8s.io/code-generator v0.27.6
- k8s.io/utils v0.0.0-20230726121419-3b25d923346b
)
require (
@@ -44,13 +40,10 @@ require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
- github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
github.com/imdario/mergo v0.3.6 // indirect
- github.com/inconshreveable/mousetrap v1.0.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
- github.com/jtolds/gls v4.20.0+incompatible // indirect
- github.com/magefile/mage v1.10.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -62,7 +55,6 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
- github.com/smartystreets/assertions v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.21.0 // indirect
@@ -74,11 +66,12 @@ require (
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/component-base v0.22.0 // indirect
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
+ k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
diff --git a/helm/esdk/Chart.yaml b/helm/esdk/Chart.yaml
index 26fae58b..1139424c 100644
--- a/helm/esdk/Chart.yaml
+++ b/helm/esdk/Chart.yaml
@@ -15,14 +15,14 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 4.4.0
+version: 4.5.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
# It is strongly recommended not to modify this parameter
-appVersion: "4.4.0"
+appVersion: "4.5.0"
home: https://github.com/Huawei/eSDK_K8S_Plugin
sources:
diff --git a/helm/esdk/templates/huawei-csi-controller.yaml b/helm/esdk/templates/huawei-csi-controller.yaml
index 93ea347c..678dbe04 100644
--- a/helm/esdk/templates/huawei-csi-controller.yaml
+++ b/helm/esdk/templates/huawei-csi-controller.yaml
@@ -429,9 +429,6 @@ rules:
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get" ]
- - apiGroups: [ "xuanwu.huawei.io" ]
- resources: [ "resourcetopologies" ]
- verbs: [ "create", "get", "update", "delete" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
@@ -763,7 +760,6 @@ spec:
- "--logging-module={{ .Values.csiDriver.controllerLogging.module }}"
- "--log-level={{ .Values.csiDriver.controllerLogging.level }}"
- "--volume-name-prefix={{ default "pvc" (.Values.controller).volumeNamePrefix }}"
- - "--enable-label={{ .Values.csiDriver.enableLabel }}"
{{ if eq .Values.csiDriver.controllerLogging.module "file" }}
- "--log-file-dir={{ .Values.csiDriver.controllerLogging.fileDir }}"
- "--log-file-size={{ .Values.csiDriver.controllerLogging.fileSize }}"
diff --git a/helm/esdk/templates/huawei-csi-node.yaml b/helm/esdk/templates/huawei-csi-node.yaml
index 5f5c1e23..ccb1edf9 100644
--- a/helm/esdk/templates/huawei-csi-node.yaml
+++ b/helm/esdk/templates/huawei-csi-node.yaml
@@ -56,9 +56,6 @@ rules:
- apiGroups: [ "xuanwu.huawei.io" ]
resources: [ "storagebackendclaims","storagebackendcontents" ]
verbs: [ "get" ]
- - apiGroups: [ "xuanwu.huawei.io" ]
- resources: [ "resourcetopologies" ]
- verbs: [ "create", "get", "update", "delete" ]
- apiGroups: [ "" ]
resources: [ "pods" ]
verbs: [ "list","get" ]
diff --git a/helm/esdk/values.yaml b/helm/esdk/values.yaml
index 3076b934..a9419130 100644
--- a/helm/esdk/values.yaml
+++ b/helm/esdk/values.yaml
@@ -1,9 +1,9 @@
images:
# Images provided by Huawei
- huaweiCSIService: huawei-csi:4.4.0
- storageBackendSidecar: storage-backend-sidecar:4.4.0
- storageBackendController: storage-backend-controller:4.4.0
- huaweiCSIExtender: huawei-csi-extender:4.4.0
+ huaweiCSIService: huawei-csi:4.5.0
+ storageBackendSidecar: storage-backend-sidecar:4.5.0
+ storageBackendController: storage-backend-controller:4.5.0
+ huaweiCSIExtender: huawei-csi-extender:4.5.0
# CSI-related sidecar images provided by the Kubernetes community.
# These must match the appropriate Kubernetes version.
@@ -217,8 +217,6 @@ csiDriver:
allPathOnline: false
# Interval for updating backend capabilities. support 60~600
backendUpdateInterval: 60
- # label enable
- enableLabel: false
# Huawei-csi-controller log configuration
controllerLogging:
# Log record type, support [file, console]
diff --git a/lib/drcsi/connection/connection.go b/lib/drcsi/connection/connection.go
index 8c468511..9ea6eb4e 100644
--- a/lib/drcsi/connection/connection.go
+++ b/lib/drcsi/connection/connection.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -27,7 +27,8 @@ import (
// Connect opens insecure gRPC connection to a CSI driver. Address must be either absolute path to UNIX domain socket
// file or have format '://', following gRPC name resolution mechanism at
// https://github.com/grpc/grpc/blob/master/doc/naming.md.
-func Connect(ctx context.Context, drCSIAddress string, metricsManager metrics.CSIMetricsManager) (conn *grpc.ClientConn, err error) {
+func Connect(ctx context.Context,
+ drCSIAddress string, metricsManager metrics.CSIMetricsManager) (conn *grpc.ClientConn, err error) {
var m sync.Mutex
var canceled bool
ready := make(chan bool)
diff --git a/manual/esdk/deploy/huawei-csi-controller-extender.yaml b/manual/esdk/deploy/huawei-csi-controller-extender.yaml
index 5e107e87..2a9c48f0 100644
--- a/manual/esdk/deploy/huawei-csi-controller-extender.yaml
+++ b/manual/esdk/deploy/huawei-csi-controller-extender.yaml
@@ -416,9 +416,6 @@ rules:
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get" ]
- - apiGroups: [ "xuanwu.huawei.io" ]
- resources: [ "resourcetopologies" ]
- verbs: [ "create", "get", "update", "delete" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
@@ -571,7 +568,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: storage-backend-controller
- image: storage-backend-controller:4.4.0
+ image: storage-backend-controller:4.5.0
imagePullPolicy: "IfNotPresent"
env:
- name: CSI_NAMESPACE
@@ -610,7 +607,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: storage-backend-sidecar
- image: storage-backend-sidecar:4.4.0
+ image: storage-backend-sidecar:4.5.0
imagePullPolicy: "IfNotPresent"
env:
- name: DRCSI_ENDPOINT
@@ -647,7 +644,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: huawei-csi-extender
- image: huawei-csi-extender:4.4.0
+ image: huawei-csi-extender:4.5.0
imagePullPolicy: "IfNotPresent"
env:
- name: DRCSI_ENDPOINT
@@ -686,7 +683,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: huawei-csi-driver
- image: huawei-csi:4.4.0
+ image: huawei-csi:4.5.0
imagePullPolicy: "IfNotPresent"
args:
- "--endpoint=$(CSI_ENDPOINT)"
@@ -697,7 +694,6 @@ spec:
- "--logging-module=file"
- "--log-level=info"
- "--volume-name-prefix=pvc"
- - "--enable-label=false"
- "--log-file-dir=/var/log/huawei"
- "--log-file-size=20M"
- "--max-backups=9"
diff --git a/manual/esdk/deploy/huawei-csi-controller.yaml b/manual/esdk/deploy/huawei-csi-controller.yaml
index ecff60c7..cd27f4b0 100644
--- a/manual/esdk/deploy/huawei-csi-controller.yaml
+++ b/manual/esdk/deploy/huawei-csi-controller.yaml
@@ -384,9 +384,6 @@ rules:
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get" ]
- - apiGroups: [ "xuanwu.huawei.io" ]
- resources: [ "resourcetopologies" ]
- verbs: [ "create", "get", "update", "delete" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
@@ -539,7 +536,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: storage-backend-controller
- image: storage-backend-controller:4.4.0
+ image: storage-backend-controller:4.5.0
imagePullPolicy: "IfNotPresent"
env:
- name: CSI_NAMESPACE
@@ -578,7 +575,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: storage-backend-sidecar
- image: storage-backend-sidecar:4.4.0
+ image: storage-backend-sidecar:4.5.0
imagePullPolicy: "IfNotPresent"
env:
- name: DRCSI_ENDPOINT
@@ -615,7 +612,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: huawei-csi-driver
- image: huawei-csi:4.4.0
+ image: huawei-csi:4.5.0
imagePullPolicy: "IfNotPresent"
args:
- "--endpoint=$(CSI_ENDPOINT)"
@@ -626,7 +623,6 @@ spec:
- "--logging-module=file"
- "--log-level=info"
- "--volume-name-prefix=pvc"
- - "--enable-label=false"
- "--log-file-dir=/var/log/huawei"
- "--log-file-size=20M"
- "--max-backups=9"
diff --git a/manual/esdk/deploy/huawei-csi-node.yaml b/manual/esdk/deploy/huawei-csi-node.yaml
index 57b21765..aaecb565 100644
--- a/manual/esdk/deploy/huawei-csi-node.yaml
+++ b/manual/esdk/deploy/huawei-csi-node.yaml
@@ -89,9 +89,6 @@ rules:
- storagebackendcontents
verbs:
- get
- - apiGroups: [ "xuanwu.huawei.io" ]
- resources: [ "resourcetopologies" ]
- verbs: [ "create", "get", "update", "delete" ]
- apiGroups:
- ""
resources:
@@ -169,7 +166,7 @@ spec:
cpu: 50m
memory: 128Mi
- name: huawei-csi-driver
- image: huawei-csi:4.4.0
+ image: huawei-csi:4.5.0
imagePullPolicy: "IfNotPresent"
args:
- "--endpoint=/csi/csi.sock"
diff --git a/pkg/client/clientset/versioned/typed/xuanwu/v1/fake/fake_xuanwu_client.go b/pkg/client/clientset/versioned/typed/xuanwu/v1/fake/fake_xuanwu_client.go
index d5834edb..4084aafc 100644
--- a/pkg/client/clientset/versioned/typed/xuanwu/v1/fake/fake_xuanwu_client.go
+++ b/pkg/client/clientset/versioned/typed/xuanwu/v1/fake/fake_xuanwu_client.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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,10 +26,6 @@ type FakeXuanwuV1 struct {
*testing.Fake
}
-func (c *FakeXuanwuV1) ResourceTopologies() v1.ResourceTopologyInterface {
- return &FakeResourceTopologies{c}
-}
-
func (c *FakeXuanwuV1) StorageBackendClaims(namespace string) v1.StorageBackendClaimInterface {
return &FakeStorageBackendClaims{c, namespace}
}
diff --git a/pkg/client/clientset/versioned/typed/xuanwu/v1/generated_expansion.go b/pkg/client/clientset/versioned/typed/xuanwu/v1/generated_expansion.go
index 7a6c4a2c..4f8b7ddd 100644
--- a/pkg/client/clientset/versioned/typed/xuanwu/v1/generated_expansion.go
+++ b/pkg/client/clientset/versioned/typed/xuanwu/v1/generated_expansion.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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,8 +15,6 @@
package v1
-type ResourceTopologyExpansion interface{}
-
type StorageBackendClaimExpansion interface{}
type StorageBackendContentExpansion interface{}
diff --git a/pkg/client/clientset/versioned/typed/xuanwu/v1/xuanwu_client.go b/pkg/client/clientset/versioned/typed/xuanwu/v1/xuanwu_client.go
index 7d7292ad..d7548580 100644
--- a/pkg/client/clientset/versioned/typed/xuanwu/v1/xuanwu_client.go
+++ b/pkg/client/clientset/versioned/typed/xuanwu/v1/xuanwu_client.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -25,7 +25,6 @@ import (
type XuanwuV1Interface interface {
RESTClient() rest.Interface
- ResourceTopologiesGetter
StorageBackendClaimsGetter
StorageBackendContentsGetter
VolumeModifyClaimsGetter
@@ -37,10 +36,6 @@ type XuanwuV1Client struct {
restClient rest.Interface
}
-func (c *XuanwuV1Client) ResourceTopologies() ResourceTopologyInterface {
- return newResourceTopologies(c)
-}
-
func (c *XuanwuV1Client) StorageBackendClaims(namespace string) StorageBackendClaimInterface {
return newStorageBackendClaims(c, namespace)
}
diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go
index 2d4e12ae..f7aaea89 100644
--- a/pkg/client/informers/externalversions/generic.go
+++ b/pkg/client/informers/externalversions/generic.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -50,8 +50,6 @@ func (f *genericInformer) Lister() cache.GenericLister {
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=xuanwu.huawei.io, Version=v1
- case v1.SchemeGroupVersion.WithResource("resourcetopologies"):
- return &genericInformer{resource: resource.GroupResource(), informer: f.Xuanwu().V1().ResourceTopologies().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("storagebackendclaims"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Xuanwu().V1().StorageBackendClaims().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("storagebackendcontents"):
diff --git a/pkg/client/informers/externalversions/xuanwu/v1/interface.go b/pkg/client/informers/externalversions/xuanwu/v1/interface.go
index 5c58552a..3506e218 100644
--- a/pkg/client/informers/externalversions/xuanwu/v1/interface.go
+++ b/pkg/client/informers/externalversions/xuanwu/v1/interface.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -21,8 +21,6 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
- // ResourceTopologies returns a ResourceTopologyInformer.
- ResourceTopologies() ResourceTopologyInformer
// StorageBackendClaims returns a StorageBackendClaimInformer.
StorageBackendClaims() StorageBackendClaimInformer
// StorageBackendContents returns a StorageBackendContentInformer.
@@ -44,11 +42,6 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
-// ResourceTopologies returns a ResourceTopologyInformer.
-func (v *version) ResourceTopologies() ResourceTopologyInformer {
- return &resourceTopologyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
-}
-
// StorageBackendClaims returns a StorageBackendClaimInformer.
func (v *version) StorageBackendClaims() StorageBackendClaimInformer {
return &storageBackendClaimInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
diff --git a/pkg/client/listers/xuanwu/v1/expansion_generated.go b/pkg/client/listers/xuanwu/v1/expansion_generated.go
index 144ee4c8..014589c6 100644
--- a/pkg/client/listers/xuanwu/v1/expansion_generated.go
+++ b/pkg/client/listers/xuanwu/v1/expansion_generated.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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,6 @@
package v1
-// ResourceTopologyListerExpansion allows custom methods to be added to
-// ResourceTopologyLister.
-type ResourceTopologyListerExpansion interface{}
-
// StorageBackendClaimListerExpansion allows custom methods to be added to
// StorageBackendClaimLister.
type StorageBackendClaimListerExpansion interface{}
diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go
index cfa93edb..c8f798e2 100644
--- a/pkg/constants/constants.go
+++ b/pkg/constants/constants.go
@@ -22,7 +22,7 @@ type FileType string
const (
// ProviderVersion defines provider version
- ProviderVersion = "4.4.0"
+ ProviderVersion = "4.5.0"
// ProviderVendorName defines provider vendor name
ProviderVendorName = "Huawei"
// EndpointDirPermission defines permission of endpoint dir
@@ -62,8 +62,6 @@ const (
PVKind = "PersistentVolume"
// PodKind is defined by k8s
PodKind = "Pod"
- // TopologyKind is topology resource kind
- TopologyKind = "ResourceTopology"
// KubernetesV1 is kubernetes v1 api version
KubernetesV1 = "v1"
@@ -78,6 +76,11 @@ const (
// AllocationUnitBytes default is 512
AllocationUnitBytes = 512
+
+ // DefaultIntBase is the default value of int base
+ DefaultIntBase = 10
+ // DefaultIntBitSize is the default value of bit size
+ DefaultIntBitSize = 64
)
var (
diff --git a/pkg/constants/storage.go b/pkg/constants/storage.go
index 44921011..f01d545d 100644
--- a/pkg/constants/storage.go
+++ b/pkg/constants/storage.go
@@ -30,10 +30,19 @@ const (
// DoradoV615 is Dorado V6.1.5
DoradoV615 = "6.1.5"
- // MinVersionSupportLabel version gte 6.1.7 support label function
- MinVersionSupportLabel = "6.1.7"
+ // MinVersionSupportNfsPlus version gte 6.1.7 support label function
+ MinVersionSupportNfsPlus = "6.1.7"
// OceanStorNas storage type is oceanstor-nas
OceanStorNas = "oceanstor-nas"
+
+ // CloneSpeedLevel1 means level1 of the clone speed
+ CloneSpeedLevel1 = 1
+ // CloneSpeedLevel2 means level2 of the clone speed
+ CloneSpeedLevel2 = 2
+ // CloneSpeedLevel3 means level3 of the clone speed
+ CloneSpeedLevel3 = 3
+ // CloneSpeedLevel4 means level4 of the clone speed
+ CloneSpeedLevel4 = 4
)
// BackendCapability backend capability
@@ -65,6 +74,3 @@ var SupportApplicationType BackendCapability = "SupportApplicationType"
// SupportMetroNAS defines backend capability SupportMetroNAS
var SupportMetroNAS BackendCapability = "SupportMetroNAS"
-
-// SupportLabel defines backend capability SupportLabel
-var SupportLabel BackendCapability = "SupportLabel"
diff --git a/pkg/modify/claim_worker.go b/pkg/modify/claim_worker.go
index 5cfbd267..1b0e2fd4 100644
--- a/pkg/modify/claim_worker.go
+++ b/pkg/modify/claim_worker.go
@@ -31,10 +31,10 @@ import (
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
- "k8s.io/utils/strings/slices"
xuanwuv1 "huawei-csi-driver/client/apis/xuanwu/v1"
pkgutils "huawei-csi-driver/pkg/utils"
+ "huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
@@ -49,6 +49,9 @@ const (
// HyperMetroFeatures hyperMetro features
HyperMetroFeatures = "hyperMetro"
+ // MetroPairSyncSpeed creates hyper metro pair synchronization speed
+ MetroPairSyncSpeed = "metroPairSyncSpeed"
+
// CreateFailedReason reason of created claim failed
CreateFailedReason = "CreatingFailed"
@@ -69,12 +72,15 @@ const (
)
var (
- supportFeatures = []string{HyperMetroFeatures}
+ supportFeatures = []string{HyperMetroFeatures, MetroPairSyncSpeed}
syncFuncList []func(context.Context, *xuanwuv1.VolumeModifyClaim) (*xuanwuv1.VolumeModifyClaim, error)
onceInitSyncFuncList sync.Once
deleteFuncList []func(context.Context, *xuanwuv1.VolumeModifyClaim) (*xuanwuv1.VolumeModifyClaim, error)
onceInitDeleteFuncList sync.Once
- featureValueCheckFunc = map[string]func(string) error{HyperMetroFeatures: checkHyperMetro}
+ featureCheckFunc = map[string]func(string, map[string]string) error{
+ HyperMetroFeatures: checkHyperMetro,
+ MetroPairSyncSpeed: checkMetroPairSyncSpeed,
+ }
)
func (ctrl *VolumeModifyController) syncClaimWork(ctx context.Context, name string) error {
@@ -157,7 +163,7 @@ func (ctrl *VolumeModifyController) setClaimFinalizers(ctx context.Context,
return claim, nil
}
- if slices.Contains(claim.Finalizers, ProtectClaimFinalizer) {
+ if utils.Contains(claim.Finalizers, ProtectClaimFinalizer) {
return claim, nil
}
@@ -446,7 +452,7 @@ func (ctrl *VolumeModifyController) findNoSupportFeatures(params map[string]stri
var notSupports []string
supports := make(map[string]string)
for key, value := range params {
- if !slices.Contains(supportFeatures, key) {
+ if !utils.Contains(supportFeatures, key) {
notSupports = append(notSupports, key)
continue
}
@@ -645,12 +651,12 @@ func checkParameters(params map[string]string) error {
var notSupports []string
supports := make(map[string]string)
for key, value := range params {
- if !slices.Contains(supportFeatures, key) {
+ if !utils.Contains(supportFeatures, key) {
notSupports = append(notSupports, key)
continue
}
- if check, ok := featureValueCheckFunc[key]; ok {
- if err := check(value); err != nil {
+ if check, ok := featureCheckFunc[key]; ok {
+ if err := check(value, params); err != nil {
return err
}
}
@@ -665,13 +671,33 @@ func checkParameters(params map[string]string) error {
strings.Join(notSupports, ","), strings.Join(supportFeatures, ","))
}
-func checkHyperMetro(value string) error {
+func checkHyperMetro(value string, _ map[string]string) error {
if value == "true" {
return nil
}
return fmt.Errorf("check spec failed: paramter hyperMetro can only be set to 'true'")
}
+func checkMetroPairSyncSpeed(value string, params map[string]string) error {
+ if hyperMetro, exist := params[HyperMetroFeatures]; !exist || hyperMetro != "true" {
+ return fmt.Errorf("check spec failed: " +
+ "parameter metroPairSyncSpeed can be configured only when hyperMetro is set to true")
+ }
+
+ speed, err := strconv.Atoi(value)
+ if err != nil {
+ return fmt.Errorf("check spec failed: paramter metroPairSyncSpeed can only be an integer")
+ }
+
+ if speed < client.MetroPairSyncSpeedLow || speed > client.MetroPairSyncSpeedHighest {
+ return fmt.Errorf(
+ "check spec failed: paramter metroPairSyncSpeed must be between %d and %d, but got [%d]",
+ client.MetroPairSyncSpeedLow, client.MetroPairSyncSpeedHighest, speed)
+ }
+
+ return nil
+}
+
func (ctrl *VolumeModifyController) updateClaimStatusWithRetry(ctx context.Context, claim *xuanwuv1.VolumeModifyClaim,
retryTimes int) (*xuanwuv1.VolumeModifyClaim, error) {
var err error
diff --git a/pkg/modify/claim_worker_test.go b/pkg/modify/claim_worker_test.go
index 5c8e67a7..efc11a62 100644
--- a/pkg/modify/claim_worker_test.go
+++ b/pkg/modify/claim_worker_test.go
@@ -27,12 +27,12 @@ import (
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
- "k8s.io/utils/strings/slices"
xuanwuv1 "huawei-csi-driver/client/apis/xuanwu/v1"
clientSet "huawei-csi-driver/pkg/client/clientset/versioned"
"huawei-csi-driver/pkg/client/clientset/versioned/fake"
backendInformers "huawei-csi-driver/pkg/client/informers/externalversions"
+ "huawei-csi-driver/utils"
)
func TestModifyClaimController_syncClaimWork_WhenGetClaimFromListerFailed(t *testing.T) {
@@ -92,7 +92,7 @@ func TestModifyClaimController_setClaimFinalizers_WhenStatusIsNil(t *testing.T)
"want nil, but got %v", err)
}
- if slices.Contains(claim.Finalizers, ProtectClaimFinalizer) {
+ if utils.Contains(claim.Finalizers, ProtectClaimFinalizer) {
t.Errorf("TestModifyClaimController_setClaimFinalizers_WhenStatusIsNil failed, "+
"want not finalzer, but got %v", claim.Finalizers)
}
@@ -126,7 +126,7 @@ func TestModifyClaimController_setClaimFinalizers_WhenPhaseIsPending(t *testing.
"want nil, but got %v", err)
}
- if !slices.Contains(claim.Finalizers, ProtectClaimFinalizer) {
+ if !utils.Contains(claim.Finalizers, ProtectClaimFinalizer) {
t.Errorf("TestModifyClaimController_setClaimFinalizers_WhenPhaseIsPending failed, "+
"want proctect finalzer, but got %v", claim.Finalizers)
}
diff --git a/pkg/modify/content_worker.go b/pkg/modify/content_worker.go
index f7d9356d..e40767b5 100644
--- a/pkg/modify/content_worker.go
+++ b/pkg/modify/content_worker.go
@@ -28,7 +28,6 @@ import (
corev1 "k8s.io/api/core/v1"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/utils/strings/slices"
xuanwuv1 "huawei-csi-driver/client/apis/xuanwu/v1"
"huawei-csi-driver/lib/drcsi"
@@ -145,7 +144,7 @@ func (ctrl *VolumeModifyController) setContentFinalizers(ctx context.Context,
defer log.AddContext(ctx).Infof("content %s added finalizer ", content.Name)
contentClone := content.DeepCopy()
- if slices.Contains(content.Finalizers, ProtectContentFinalizer) {
+ if utils.Contains(content.Finalizers, ProtectContentFinalizer) {
return contentClone, nil
}
contentClone.ObjectMeta.Finalizers = append(contentClone.ObjectMeta.Finalizers, ProtectContentFinalizer)
diff --git a/pkg/sidecar/controller/sidecar_controller.go b/pkg/sidecar/controller/sidecar_controller.go
index 42262e5b..33b2a180 100644
--- a/pkg/sidecar/controller/sidecar_controller.go
+++ b/pkg/sidecar/controller/sidecar_controller.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -12,7 +12,7 @@
limitations under the License.
*/
-// Package controller used deal with the backend backend content resources
+// Package controller used deal with the backend content resources
package controller
import (
@@ -31,30 +31,35 @@ import (
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
- "huawei-csi-driver/pkg/utils"
-
xuanwuv1 "huawei-csi-driver/client/apis/xuanwu/v1"
clientSet "huawei-csi-driver/pkg/client/clientset/versioned"
backendInformers "huawei-csi-driver/pkg/client/informers/externalversions/xuanwu/v1"
backendListers "huawei-csi-driver/pkg/client/listers/xuanwu/v1"
storageBackend "huawei-csi-driver/pkg/storage-backend/handle"
+ "huawei-csi-driver/pkg/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
+)
+
+const (
+ defaultRetryIntervalStart = 5 * time.Second
+ defaultRetryIntervalMax = 5 * time.Minute
+ defaultProvisionTimeout = 5 * time.Minute
)
var (
retryIntervalStart = flag.Duration(
"retry-interval-start",
- 5*time.Second,
+ defaultRetryIntervalStart,
"Initial retry interval of failed storageBackend creation or deletion. "+
"It doubles with each failure, up to retry-interval-max.")
retryIntervalMax = flag.Duration(
"retry-interval-max",
- 5*time.Minute,
+ defaultRetryIntervalMax,
"Maximum retry interval of failed storageBackend creation or deletion.")
provisionTimeout = flag.Duration(
"provision-timeout",
- 5*time.Minute,
+ defaultProvisionTimeout,
"The timeout of the provision storage backend.")
)
@@ -258,10 +263,13 @@ func (ctrl *backendController) syncContentByKey(ctx context.Context, objKey stri
content, err := ctrl.contentLister.Get(name)
if err == nil {
- if ctrl.isMatchProvider(content) {
- // the content exists in informer cache, the handle event must be one of "create/update/sync"
- return ctrl.updateContent(ctx, content)
+ if !ctrl.isMatchProvider(content) {
+ return fmt.Errorf("backend provider [%s] does not match driver provider [%s]",
+ content.Spec.Provider, ctrl.providerName)
}
+
+ // the content exists in informer cache, the handle event must be one of "create/update/sync"
+ return ctrl.updateContent(ctx, content)
}
if !apiErrors.IsNotFound(err) {
@@ -325,7 +333,7 @@ func (ctrl *backendController) syncContent(ctx context.Context, content *xuanwuv
log.AddContext(ctx).Debugf("Start to sync content %s.", content.Name)
defer log.AddContext(ctx).Debugf("Finished sync content %s.", content.Name)
- syncTask := taskflow.NewTaskFlow(ctx, "Sync-StorageBackendContent")
+ syncTask := flow.NewTaskFlow(ctx, "Sync-StorageBackendContent")
syncTask.AddTask("Init-Content-Status", ctrl.initContentStatusTask, nil)
syncTask.AddTask("Delete-Content", ctrl.deleteContentTask, nil)
syncTask.AddTask("Create-Content", ctrl.createContentTask, nil)
diff --git a/pkg/sidecar/controller/sidecar_handler.go b/pkg/sidecar/controller/sidecar_handler.go
index cb40c099..8b4954ed 100644
--- a/pkg/sidecar/controller/sidecar_handler.go
+++ b/pkg/sidecar/controller/sidecar_handler.go
@@ -65,8 +65,7 @@ func (cdr *drCSIHandler) DeleteStorageBackend(ctx context.Context, backendName s
// UpdateStorageBackend update the storageBackend
func (cdr *drCSIHandler) UpdateStorageBackend(ctx context.Context, content *xuanwuv1.StorageBackendContent) error {
- return cdr.backend.UpdateStorageBackend(ctx, content.Name, content.Spec.BackendClaim,
- content.Spec.ConfigmapMeta, content.Spec.SecretMeta, content.Spec.Parameters)
+ return cdr.backend.UpdateStorageBackend(ctx, content)
}
// GetStorageBackendStats get all backend info from the provider
diff --git a/pkg/storage-backend/controller/claim_sync.go b/pkg/storage-backend/controller/claim_sync.go
index 564506db..fe5e267a 100644
--- a/pkg/storage-backend/controller/claim_sync.go
+++ b/pkg/storage-backend/controller/claim_sync.go
@@ -29,8 +29,8 @@ import (
"huawei-csi-driver/csi/backend"
"huawei-csi-driver/pkg/finalizers"
"huawei-csi-driver/pkg/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
)
// syncClaimByKey processes a StorageBackendClaim request.
@@ -94,7 +94,7 @@ func (ctrl *BackendController) syncClaim(ctx context.Context, storageBackend *xu
log.AddContext(ctx).Infof("Start to syncClaim %s.", utils.StorageBackendClaimKey(storageBackend))
defer log.AddContext(ctx).Infof("Finished syncClaim %s.", utils.StorageBackendClaimKey(storageBackend))
- syncTask := taskflow.NewTaskFlow(ctx, "Sync-StorageBackendClaim")
+ syncTask := flow.NewTaskFlow(ctx, "Sync-StorageBackendClaim")
syncTask.AddTask("Set-Claim-Status-Pending", ctrl.setClaimStatusTask, nil)
syncTask.AddTask("Remove-Configmap-Finalizer", ctrl.removeConfigmapFinalizerTask, nil)
syncTask.AddTask("Remove-Secret-Finalizer", ctrl.removeSecretFinalizerTask, nil)
diff --git a/pkg/storage-backend/controller/controller.go b/pkg/storage-backend/controller/controller.go
index bf704655..a0940678 100644
--- a/pkg/storage-backend/controller/controller.go
+++ b/pkg/storage-backend/controller/controller.go
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -39,19 +39,25 @@ import (
"huawei-csi-driver/utils/log"
)
+const (
+ defaultRetryIntervalStart = 5 * time.Second
+ defaultRetryIntervalMax = 5 * time.Minute
+ defaultProvisionTimeout = 5 * time.Minute
+)
+
var (
retryIntervalStart = flag.Duration(
"retry-interval-start",
- 5*time.Second,
+ defaultRetryIntervalStart,
"Initial retry interval of failed storageBackend creation or deletion. "+
"It doubles with each failure, up to retry-interval-max.")
retryIntervalMax = flag.Duration(
"retry-interval-max",
- 5*time.Minute,
+ defaultRetryIntervalMax,
"Maximum retry interval of failed storageBackend creation or deletion.")
provisionTimeout = flag.Duration(
"provision-timeout",
- 5*time.Minute,
+ defaultProvisionTimeout,
"The timeout of the provision storage backend.")
)
diff --git a/pkg/storage-backend/handle/storage_backend.go b/pkg/storage-backend/handle/storage_backend.go
index 19248b9a..088548f5 100644
--- a/pkg/storage-backend/handle/storage_backend.go
+++ b/pkg/storage-backend/handle/storage_backend.go
@@ -21,6 +21,7 @@ import (
"google.golang.org/grpc"
+ xuanwuv1 "huawei-csi-driver/client/apis/xuanwu/v1"
"huawei-csi-driver/lib/drcsi"
"huawei-csi-driver/lib/drcsi/rpc"
"huawei-csi-driver/utils/log"
@@ -34,8 +35,7 @@ type BackendInterfaces interface {
// RemoveStorageBackend remove the storageBackend from provider
RemoveStorageBackend(ctx context.Context, backendName string) (err error)
// UpdateStorageBackend update the storageBackend
- UpdateStorageBackend(ctx context.Context, contentName, backendName, configmapMeta, secretMeta string,
- parameters map[string]string) error
+ UpdateStorageBackend(ctx context.Context, content *xuanwuv1.StorageBackendContent) error
// GetStorageBackendStats get all backend info from the provider
GetStorageBackendStats(ctx context.Context, contentName, backendName string) (*drcsi.GetBackendStatsResponse, error)
}
@@ -94,15 +94,14 @@ func updateStorageBackend(ctx context.Context, conn *grpc.ClientConn, req *drcsi
}
// UpdateStorageBackend update the storageBackend
-func (b *backend) UpdateStorageBackend(ctx context.Context, contentName, backendName, configmapMeta, secretMeta string,
- parameters map[string]string) error {
- log.AddContext(ctx).Infof("UpdateStorageBackend of backend %s", backendName)
+func (b *backend) UpdateStorageBackend(ctx context.Context, content *xuanwuv1.StorageBackendContent) error {
+ log.AddContext(ctx).Infof("UpdateStorageBackend of backend %s", content.Name)
req := drcsi.UpdateStorageBackendRequest{
- Name: contentName,
- BackendId: backendName,
- ConfigmapMeta: configmapMeta,
- SecretMeta: secretMeta,
- Parameters: parameters,
+ Name: content.Name,
+ BackendId: content.Spec.BackendClaim,
+ ConfigmapMeta: content.Spec.ConfigmapMeta,
+ SecretMeta: content.Spec.SecretMeta,
+ Parameters: content.Spec.Parameters,
}
_, err := updateStorageBackend(ctx, b.conn, &req)
diff --git a/pkg/webhook/utils.go b/pkg/webhook/utils.go
index c0b8a033..7547228b 100644
--- a/pkg/webhook/utils.go
+++ b/pkg/webhook/utils.go
@@ -1,5 +1,5 @@
/*
-Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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,6 +35,8 @@ import (
"huawei-csi-driver/utils/log"
)
+const certUntilYears = 99
+
// GenerateCertificate Self Signed certificate using given CN, returns x509 cert
// and priv key in PEM format
func GenerateCertificate(ctx context.Context, cn string, dnsName string) ([]byte, []byte, error) {
@@ -55,7 +57,7 @@ func GenerateCertificate(ctx context.Context, cn string, dnsName string) ([]byte
CommonName: cn,
},
NotBefore: time.Now(),
- NotAfter: time.Now().AddDate(99, 0, 0),
+ NotAfter: time.Now().AddDate(certUntilYears, 0, 0),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
@@ -94,7 +96,7 @@ func GetTLSCertificate(cert, priv []byte) (tls.Certificate, error) {
}
// CreateCertSecrets creates k8s secret to store signed cert data
-func CreateCertSecrets(ctx context.Context, webHookCfg WebHook, cert, key []byte, ns string) (*v1.Secret, error) {
+func CreateCertSecrets(ctx context.Context, webHookCfg Config, cert, key []byte, ns string) (*v1.Secret, error) {
secretData := make(map[string][]byte)
secretData[webHookCfg.PrivateKey] = key
secretData[webHookCfg.PrivateCert] = cert
diff --git a/pkg/webhook/validate_webhook.go b/pkg/webhook/validate_webhook.go
index a36d6c56..8e3683b5 100644
--- a/pkg/webhook/validate_webhook.go
+++ b/pkg/webhook/validate_webhook.go
@@ -17,6 +17,7 @@ package webhook
import (
"context"
+ "reflect"
admissionV1 "k8s.io/api/admissionregistration/v1"
apisErrors "k8s.io/apimachinery/pkg/api/errors"
@@ -44,28 +45,66 @@ type AdmissionRule struct {
}
// CreateValidateWebhook create new webhook config if not exist already
-func CreateValidateWebhook(ctx context.Context, admissionWebhook AdmissionWebHookCFG,
- caBundle []byte, ns string) error {
+func CreateValidateWebhook(ctx context.Context, webHookCfg AdmissionWebHookCFG, caBundle []byte, ns string) error {
+ webhook := newValidateWebhook(webHookCfg, caBundle, ns)
+ req := &admissionV1.ValidatingWebhookConfiguration{
+ ObjectMeta: metaV1.ObjectMeta{Name: webHookCfg.WebhookName},
+ Webhooks: []admissionV1.ValidatingWebhook{webhook},
+ }
+
+ foundWebhookCfg, err := admission.Instance().GetValidatingWebhookCfg(req.Name)
+ if err != nil {
+ if !apisErrors.IsNotFound(err) {
+ log.AddContext(ctx).Errorf("get webhook configuration [%s] failed: %v", req.Name, err)
+ return err
+ }
+
+ // no webhook configuration in k8s cluster, we need to create a new one.
+ if _, err := admission.Instance().CreateValidatingWebhookCfg(req); err != nil {
+ log.AddContext(ctx).Errorf("create webhook configuration [%s] failed: %v", req.Name, err)
+ return err
+ }
+ log.AddContext(ctx).Infof("webhook configuration [%s] has been created", req.Name)
+ return nil
+ }
+
+ if reflect.DeepEqual(foundWebhookCfg.Webhooks, req.Webhooks) {
+ return nil
+ }
+
+ // webhook configuration has changed, we need to update it.
+ foundWebhookCfg.Webhooks = req.Webhooks
+ if _, err := admission.Instance().UpdateValidatingWebhookCfg(foundWebhookCfg); err != nil {
+ log.AddContext(ctx).Errorf("update webhook configuration failed: %v", err)
+ return err
+ }
+
+ log.AddContext(ctx).Infof("webhook [%s] has been updated", req.Name)
+
+ return nil
+}
+
+func newValidateWebhook(webhookCfg AdmissionWebHookCFG, caBundle []byte, ns string) admissionV1.ValidatingWebhook {
sideEffect := admissionV1.SideEffectClassNoneOnDryRun
failurePolicy := admissionV1.Fail
matchPolicy := admissionV1.Exact
- webhook := admissionV1.ValidatingWebhook{
- Name: admissionWebhook.WebhookName,
+ return admissionV1.ValidatingWebhook{
+ Name: webhookCfg.WebhookName,
ClientConfig: admissionV1.WebhookClientConfig{
Service: &admissionV1.ServiceReference{
- Name: admissionWebhook.ServiceName,
+ Name: webhookCfg.ServiceName,
Namespace: ns,
- Path: &admissionWebhook.WebhookPath,
- Port: &admissionWebhook.WebhookPort,
+ Path: &webhookCfg.WebhookPath,
+ Port: &webhookCfg.WebhookPort,
},
CABundle: caBundle,
},
Rules: []admissionV1.RuleWithOperations{{
- Operations: admissionWebhook.AdmissionOps,
+ Operations: webhookCfg.AdmissionOps,
Rule: admissionV1.Rule{
- APIGroups: admissionWebhook.AdmissionRule.APIGroups,
- APIVersions: admissionWebhook.AdmissionRule.APIVersions,
- Resources: admissionWebhook.AdmissionRule.Resources,
+ APIGroups: webhookCfg.AdmissionRule.APIGroups,
+ APIVersions: webhookCfg.AdmissionRule.APIVersions,
+ Resources: webhookCfg.AdmissionRule.Resources,
},
}},
SideEffects: &sideEffect,
@@ -73,19 +112,4 @@ func CreateValidateWebhook(ctx context.Context, admissionWebhook AdmissionWebHoo
AdmissionReviewVersions: []string{"v1", "v1beta1"},
MatchPolicy: &matchPolicy,
}
-
- req := &admissionV1.ValidatingWebhookConfiguration{
- ObjectMeta: metaV1.ObjectMeta{
- Name: admissionWebhook.WebhookName,
- },
- Webhooks: []admissionV1.ValidatingWebhook{webhook},
- }
-
- _, err := admission.Instance().CreateValidatingWebhookCfg(req)
- if err != nil && !apisErrors.IsAlreadyExists(err) {
- log.AddContext(ctx).Errorf("unable to create webhook configuration: %v", err)
- return err
- }
- log.AddContext(ctx).Infof("%v webhook v1 configured", admissionWebhook.WebhookName)
- return nil
}
diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go
index a9d449e8..87c5c1d8 100644
--- a/pkg/webhook/webhook.go
+++ b/pkg/webhook/webhook.go
@@ -61,8 +61,8 @@ const (
ClaimBoundFinalizer string = "storagebackend.xuanwu.huawei.io/storagebackendclaim-bound-protection"
)
-// WebHook uses to start the webhook server
-type WebHook struct {
+// Config uses to start the webhook server
+type Config struct {
NamespaceEnv string
DefaultNamespace string
ServiceName string
@@ -221,7 +221,7 @@ func (c *Controller) serve(w http.ResponseWriter, r *http.Request, admit admitHa
log.AddContext(ctx).Infoln("return response success")
}
-func (c *Controller) getTlsCert(ctx context.Context, webHookCfg WebHook, ns string) (tls.Certificate, []byte, error) {
+func (c *Controller) getTlsCert(ctx context.Context, webHookCfg Config, ns string) (tls.Certificate, []byte, error) {
var tlsCert tls.Certificate
var caBytes []byte
certSecrets, err := app.GetGlobalConfig().K8sUtils.GetSecret(ctx, webHookCfg.SecretName, ns)
@@ -271,7 +271,7 @@ func (c *Controller) getTlsCert(ctx context.Context, webHookCfg WebHook, ns stri
}
// Start uses to start the webhook server
-func (c *Controller) Start(ctx context.Context, webHookCfg WebHook, admissionWebhooks []AdmissionWebHookCFG) error {
+func (c *Controller) Start(ctx context.Context, webHookCfg Config, admissionWebhooks []AdmissionWebHookCFG) error {
c.lock.Lock()
defer c.lock.Unlock()
@@ -316,7 +316,7 @@ func (c *Controller) Start(ctx context.Context, webHookCfg WebHook, admissionWeb
}
// Stop uses to stop the webhook server
-func (c *Controller) Stop(ctx context.Context, webHookCfg WebHook,
+func (c *Controller) Stop(ctx context.Context, webHookCfg Config,
admissionWebhooks []AdmissionWebHookCFG) error {
c.lock.Lock()
defer c.lock.Unlock()
@@ -333,7 +333,7 @@ func (c *Controller) Stop(ctx context.Context, webHookCfg WebHook,
return nil
}
-func getNameSpaceFromEnv(webHookCfg WebHook) string {
+func getNameSpaceFromEnv(webHookCfg Config) string {
ns := os.Getenv(webHookCfg.NamespaceEnv)
if ns == "" {
ns = webHookCfg.DefaultNamespace
@@ -446,7 +446,7 @@ func validateCommon(ctx context.Context, claim *xuanwuv1.StorageBackendClaim) er
log.AddContext(ctx).Infof("claim name: %s", claim.Name)
storageInfo, err := backend.GetStorageBackendInfo(ctx,
utils.MakeMetaWithNamespace(app.GetGlobalConfig().Namespace, claim.Name),
- claim.Spec.ConfigMapMeta, claim.Spec.SecretMeta, claim.Spec.CertSecret, claim.Spec.UseCert)
+ backend.NewGetBackendInfoArgsFromClaim(claim))
if err != nil {
return err
}
diff --git a/pkg/webhook/webhook_cfg.go b/pkg/webhook/webhook_cfg.go
index f9ecd527..7119a731 100644
--- a/pkg/webhook/webhook_cfg.go
+++ b/pkg/webhook/webhook_cfg.go
@@ -37,13 +37,13 @@ const (
)
// GetStorageWebHookCfg used to get storage webhook configuration
-func GetStorageWebHookCfg() (WebHook, []AdmissionWebHookCFG) {
+func GetStorageWebHookCfg() (Config, []AdmissionWebHookCFG) {
var handleFuncPair []HandleFuncPair
handleFuncPair = append(handleFuncPair,
HandleFuncPair{WebhookPath: claimWebhookPath,
WebHookFunc: admitStorageBackendClaim})
- webHookCfg := WebHook{
+ webHookCfg := Config{
NamespaceEnv: constants.NamespaceEnv,
DefaultNamespace: app.GetGlobalConfig().Namespace,
ServiceName: serviceName,
diff --git a/pkg/webhook/webhook_test.go b/pkg/webhook/webhook_test.go
index fed0d501..a9bdc77a 100644
--- a/pkg/webhook/webhook_test.go
+++ b/pkg/webhook/webhook_test.go
@@ -50,7 +50,7 @@ func TestMain(m *testing.M) {
// TestControllerStart test start function in normal scenario
func TestControllerStart(t *testing.T) {
- m := gomonkey.ApplyFunc(getNameSpaceFromEnv, func(webHookCfg WebHook) string {
+ m := gomonkey.ApplyFunc(getNameSpaceFromEnv, func(webHookCfg Config) string {
return "namespace"
})
defer m.Reset()
@@ -79,7 +79,7 @@ func TestControllerStart(t *testing.T) {
})
c := Controller{started: false}
- webHookCfg := WebHook{
+ webHookCfg := Config{
WebHookType: AdmissionWebHookValidating,
PrivateKey: "privateKey",
PrivateCert: "privateCert",
diff --git a/storage/fusionstorage/attacher/attacher.go b/storage/fusionstorage/attacher/attacher.go
index cef00ff7..a3575f4c 100644
--- a/storage/fusionstorage/attacher/attacher.go
+++ b/storage/fusionstorage/attacher/attacher.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -33,9 +33,9 @@ import (
"huawei-csi-driver/utils/log"
)
-// Attacher defines attacher client
-type Attacher struct {
- cli *client.Client
+// VolumeAttacher defines attacher client
+type VolumeAttacher struct {
+ cli *client.RestClient
protocol string
invoker string
portals []string
@@ -43,25 +43,36 @@ type Attacher struct {
alua map[string]interface{}
}
+// VolumeAttacherConfig defines configurations of VolumeAttacher
+type VolumeAttacherConfig struct {
+ Cli *client.RestClient
+ Protocol string
+ Invoker string
+ Portals []string
+ Hosts map[string]string
+ Alua map[string]interface{}
+}
+
const (
// DisableAlua defines switchover mode disable alua
DisableAlua = "Disable_alua"
+
+ iscsiPortalFieldsLength = 2
)
// NewAttacher used to init a new attacher
-func NewAttacher(cli *client.Client, protocol, invoker string, portals []string,
- hosts map[string]string, alua map[string]interface{}) *Attacher {
- return &Attacher{
- cli: cli,
- protocol: protocol,
- invoker: invoker,
- portals: portals,
- hosts: hosts,
- alua: alua,
+func NewAttacher(config VolumeAttacherConfig) *VolumeAttacher {
+ return &VolumeAttacher{
+ cli: config.Cli,
+ protocol: config.Protocol,
+ invoker: config.Invoker,
+ portals: config.Portals,
+ hosts: config.Hosts,
+ alua: config.Alua,
}
}
-func (p *Attacher) getHostName(ctx context.Context, parameters map[string]interface{}) (string, error) {
+func (p *VolumeAttacher) getHostName(ctx context.Context, parameters map[string]interface{}) (string, error) {
hostName, ok := parameters["HostName"].(string)
if !ok {
return "", fmt.Errorf("can not find host name,parameters:%v", parameters)
@@ -70,7 +81,7 @@ func (p *Attacher) getHostName(ctx context.Context, parameters map[string]interf
return hostName, nil
}
-func (p *Attacher) parseISCSIPortal(ctx context.Context, iscsiPortal map[string]interface{}) string {
+func (p *VolumeAttacher) parseISCSIPortal(ctx context.Context, iscsiPortal map[string]interface{}) string {
if iscsiPortal["iscsiStatus"] != "active" {
log.AddContext(ctx).Errorf("ISCSI portal %v is not active", iscsiPortal)
return ""
@@ -83,7 +94,7 @@ func (p *Attacher) parseISCSIPortal(ctx context.Context, iscsiPortal map[string]
}
portalSplit := strings.Split(portal, ":")
- if len(portalSplit) < 2 {
+ if len(portalSplit) < iscsiPortalFieldsLength {
log.AddContext(ctx).Errorf("ISCSI portal %s is invalid", portal)
return ""
}
@@ -98,7 +109,7 @@ func (p *Attacher) parseISCSIPortal(ctx context.Context, iscsiPortal map[string]
return ip.String()
}
-func (p *Attacher) needUpdateIscsiHost(host map[string]interface{}, hostAlua map[string]interface{}) bool {
+func (p *VolumeAttacher) needUpdateIscsiHost(host map[string]interface{}, hostAlua map[string]interface{}) bool {
switchoverMode, ok := hostAlua["switchoverMode"]
if !ok {
return false
@@ -118,7 +129,7 @@ func (p *Attacher) needUpdateIscsiHost(host map[string]interface{}, hostAlua map
return false
}
-func (p *Attacher) createIscsiHost(ctx context.Context, hostName string) error {
+func (p *VolumeAttacher) createIscsiHost(ctx context.Context, hostName string) error {
host, err := p.cli.GetHostByName(ctx, hostName)
if err != nil {
return err
@@ -135,7 +146,7 @@ func (p *Attacher) createIscsiHost(ctx context.Context, hostName string) error {
return err
}
-func (p *Attacher) getTargetPortals(ctx context.Context) ([]string, []string, error) {
+func (p *VolumeAttacher) getTargetPortals(ctx context.Context) ([]string, []string, error) {
nodeResultList, err := p.cli.QueryIscsiPortal(ctx)
if err != nil {
log.AddContext(ctx).Errorf("Get ISCSI portals error: %v", err)
@@ -184,7 +195,7 @@ func (p *Attacher) getTargetPortals(ctx context.Context) ([]string, []string, er
return tgtPortals, tgtIQNs, nil
}
-func (p *Attacher) parseiSCSIPortalList(ctx context.Context,
+func (p *VolumeAttacher) parseiSCSIPortalList(ctx context.Context,
iscsiPortalList []interface{}, validIPs map[string]bool, validIQNs map[string]string) error {
for _, portal := range iscsiPortalList {
iscsiPortal, exist := portal.(map[string]interface{})
@@ -203,7 +214,7 @@ func (p *Attacher) parseiSCSIPortalList(ctx context.Context,
return nil
}
-func (p *Attacher) attachIscsiInitiatorToHost(ctx context.Context, hostName string) error {
+func (p *VolumeAttacher) attachIscsiInitiatorToHost(ctx context.Context, hostName string) error {
parameters := map[string]interface{}{
"HostName": hostName,
}
@@ -236,7 +247,7 @@ func (p *Attacher) attachIscsiInitiatorToHost(ctx context.Context, hostName stri
if len(host) == 0 {
addInitiator = true
} else if host != hostName {
- return fmt.Errorf("ISCSI initiator %s is already associated to another host %s", initiatorName, host)
+ return fmt.Errorf("Connector initiator %s is already associated to another host %s", initiatorName, host)
}
}
@@ -250,7 +261,7 @@ func (p *Attacher) attachIscsiInitiatorToHost(ctx context.Context, hostName stri
return nil
}
-func (p *Attacher) isVolumeAddToHost(ctx context.Context, lunName, hostName string) (bool, error) {
+func (p *VolumeAttacher) isVolumeAddToHost(ctx context.Context, lunName, hostName string) (bool, error) {
hosts, err := p.cli.QueryHostOfVolume(ctx, lunName)
if err != nil {
return false, err
@@ -265,7 +276,7 @@ func (p *Attacher) isVolumeAddToHost(ctx context.Context, lunName, hostName stri
return false, nil
}
-func (p *Attacher) doMapping(ctx context.Context, lunName, hostName string) (string, error) {
+func (p *VolumeAttacher) doMapping(ctx context.Context, lunName, hostName string) (string, error) {
lun, err := p.cli.GetVolumeByName(ctx, lunName)
if err != nil {
log.AddContext(ctx).Errorf("Get lun %s error: %v", lunName, err)
@@ -304,7 +315,7 @@ func (p *Attacher) doMapping(ctx context.Context, lunName, hostName string) (str
return lun["wwn"].(string), nil
}
-func (p *Attacher) doUnmapping(ctx context.Context, lunName, hostName string) (string, error) {
+func (p *VolumeAttacher) doUnmapping(ctx context.Context, lunName, hostName string) (string, error) {
lun, err := p.getLunInfo(ctx, lunName)
if lun == nil {
return "", err
@@ -337,7 +348,7 @@ func (p *Attacher) doUnmapping(ctx context.Context, lunName, hostName string) (s
return lun["wwn"].(string), nil
}
-func (p *Attacher) getMappingProperties(ctx context.Context,
+func (p *VolumeAttacher) getMappingProperties(ctx context.Context,
wwn, hostLunId string, parameters map[string]interface{}) (map[string]interface{}, error) {
tgtPortals, tgtIQNs, err := p.getTargetPortals(ctx)
if err != nil {
@@ -359,7 +370,7 @@ func (p *Attacher) getMappingProperties(ctx context.Context,
return connectInfo, nil
}
-func (p *Attacher) iSCSIControllerAttach(ctx context.Context, lunInfo utils.Volume,
+func (p *VolumeAttacher) iSCSIControllerAttach(ctx context.Context, lunInfo utils.Volume,
parameters map[string]interface{}) (
map[string]interface{}, error) {
hostName, err := p.getHostName(ctx, parameters)
@@ -404,7 +415,7 @@ func (p *Attacher) iSCSIControllerAttach(ctx context.Context, lunInfo utils.Volu
}
// SCSIControllerAttach used to attach volume to host
-func (p *Attacher) SCSIControllerAttach(ctx context.Context,
+func (p *VolumeAttacher) SCSIControllerAttach(ctx context.Context,
lunInfo utils.Volume,
parameters map[string]interface{}) (string, error) {
hostName, err := p.getHostName(ctx, parameters)
@@ -432,7 +443,7 @@ func (p *Attacher) SCSIControllerAttach(ctx context.Context,
}
// ControllerDetach used to detach volume from host
-func (p *Attacher) ControllerDetach(ctx context.Context,
+func (p *VolumeAttacher) ControllerDetach(ctx context.Context,
lunName string,
parameters map[string]interface{}) (string, error) {
hostName, err := p.getHostName(ctx, parameters)
@@ -455,7 +466,7 @@ func (p *Attacher) ControllerDetach(ctx context.Context,
}
// ControllerAttach used to attach volume and return mapping info
-func (p *Attacher) ControllerAttach(ctx context.Context,
+func (p *VolumeAttacher) ControllerAttach(ctx context.Context,
lunName string,
parameters map[string]interface{}) (map[string]interface{}, error) {
@@ -484,10 +495,10 @@ func (p *Attacher) ControllerAttach(ctx context.Context,
}
// NodeStage used to stage node
-func (p *Attacher) NodeStage(ctx context.Context,
+func (p *VolumeAttacher) NodeStage(ctx context.Context,
lunInfo utils.Volume,
parameters map[string]interface{}) (*connector.ConnectInfo, error) {
- var conn connector.Connector
+ var conn connector.VolumeConnector
var mappingInfo map[string]interface{}
var err error
if p.protocol == "iscsi" {
@@ -514,7 +525,7 @@ func (p *Attacher) NodeStage(ctx context.Context,
}
// NodeUnstage used to unstage node
-func (p *Attacher) NodeUnstage(ctx context.Context,
+func (p *VolumeAttacher) NodeUnstage(ctx context.Context,
lunName string,
parameters map[string]interface{}) (*connector.DisConnectInfo, error) {
lun, err := p.getLunInfo(ctx, lunName)
@@ -522,7 +533,7 @@ func (p *Attacher) NodeUnstage(ctx context.Context,
return nil, err
}
- var conn connector.Connector
+ var conn connector.VolumeConnector
if p.protocol == "iscsi" {
conn = connector.GetConnector(ctx, connector.ISCSIDriver)
} else {
@@ -540,7 +551,7 @@ func (p *Attacher) NodeUnstage(ctx context.Context,
}, nil
}
-func (p *Attacher) getLunInfo(ctx context.Context, lunName string) (map[string]interface{}, error) {
+func (p *VolumeAttacher) getLunInfo(ctx context.Context, lunName string) (map[string]interface{}, error) {
lun, err := p.cli.GetVolumeByName(ctx, lunName)
if err != nil {
log.AddContext(ctx).Errorf("Get lun %s error: %v", lunName, err)
diff --git a/storage/fusionstorage/client/client.go b/storage/fusionstorage/client/client.go
index 0830c19c..c5f5aff8 100644
--- a/storage/fusionstorage/client/client.go
+++ b/storage/fusionstorage/client/client.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -53,7 +53,8 @@ const (
// IPLock defines error code of ip lock
IPLock = 1077949071
- unconnectedError = "unconnected"
+ unconnectedError = "unconnected"
+ defaultHttpTimeout = 60 * time.Second
)
var (
@@ -81,8 +82,8 @@ func isFilterLog(method, url string) bool {
return exist && filter[url]
}
-// Client defines fusion storage client
-type Client struct {
+// RestClient defines fusion storage client
+type RestClient struct {
url string
user string
secretNamespace string
@@ -114,7 +115,7 @@ type NewClientConfig struct {
}
// NewClient used to init a new fusion storage client
-func NewClient(ctx context.Context, clientConfig *NewClientConfig) *Client {
+func NewClient(ctx context.Context, clientConfig *NewClientConfig) *RestClient {
var err error
var parallelCount int
@@ -131,7 +132,7 @@ func NewClient(ctx context.Context, clientConfig *NewClientConfig) *Client {
log.AddContext(ctx).Infof("Init parallel count is %d", parallelCount)
clientSemaphore = utils.NewSemaphore(parallelCount)
- return &Client{
+ return &RestClient{
url: clientConfig.Url,
user: clientConfig.User,
secretName: clientConfig.SecretName,
@@ -144,7 +145,7 @@ func NewClient(ctx context.Context, clientConfig *NewClientConfig) *Client {
}
// DuplicateClient used to duplicate client
-func (cli *Client) DuplicateClient() *Client {
+func (cli *RestClient) DuplicateClient() *RestClient {
dup := *cli
dup.client = nil
@@ -152,7 +153,7 @@ func (cli *Client) DuplicateClient() *Client {
}
// ValidateLogin try to login fusion storage by secret
-func (cli *Client) ValidateLogin(ctx context.Context) error {
+func (cli *RestClient) ValidateLogin(ctx context.Context) error {
jar, err := cookiejar.New(nil)
if err != nil {
log.AddContext(ctx).Errorf("create jar failed, error: %v", err)
@@ -169,7 +170,7 @@ func (cli *Client) ValidateLogin(ctx context.Context) error {
TLSClientConfig: &tls.Config{InsecureSkipVerify: !useCert, RootCAs: certPool},
},
Jar: jar,
- Timeout: 60 * time.Second,
+ Timeout: defaultHttpTimeout,
}
log.AddContext(ctx).Infof("Try to login %s.", cli.url)
@@ -199,7 +200,7 @@ func (cli *Client) ValidateLogin(ctx context.Context) error {
}
// Login try to login fusion storage by backend id
-func (cli *Client) Login(ctx context.Context) error {
+func (cli *RestClient) Login(ctx context.Context) error {
var err error
cli.client, err = newHTTPClientByBackendID(ctx, cli.backendID)
if err != nil {
@@ -256,7 +257,7 @@ func (cli *Client) Login(ctx context.Context) error {
}
// SetAccountId used to set account id of the client
-func (cli *Client) SetAccountId(ctx context.Context) error {
+func (cli *RestClient) SetAccountId(ctx context.Context) error {
log.AddContext(ctx).Debugf("setAccountId start. account name: %s", cli.accountName)
if cli.accountName == "" {
cli.accountName = types.DefaultAccountName
@@ -279,7 +280,7 @@ func (cli *Client) SetAccountId(ctx context.Context) error {
}
// Logout used to log out
-func (cli *Client) Logout(ctx context.Context) {
+func (cli *RestClient) Logout(ctx context.Context) {
defer func() {
cli.authToken = ""
cli.client = nil
@@ -305,26 +306,26 @@ func (cli *Client) Logout(ctx context.Context) {
}
// KeepAlive used to keep connection token alive
-func (cli *Client) KeepAlive(ctx context.Context) {
+func (cli *RestClient) KeepAlive(ctx context.Context) {
_, err := cli.post(ctx, "/dsware/service/v1.3/sec/keepAlive", nil)
if err != nil {
log.AddContext(ctx).Warningf("Keep token alive error: %v", err)
}
}
-func (cli *Client) reLoginLock(ctx context.Context) {
+func (cli *RestClient) reLoginLock(ctx context.Context) {
log.AddContext(ctx).Debugln("Try to reLoginLock.")
cli.reloginMutex.Lock()
log.AddContext(ctx).Debugln("ReLoginLock success.")
}
-func (cli *Client) reLoginUnlock(ctx context.Context) {
+func (cli *RestClient) reLoginUnlock(ctx context.Context) {
log.AddContext(ctx).Debugln("Try to reLoginUnlock.")
cli.reloginMutex.Unlock()
log.AddContext(ctx).Debugln("ReLoginUnlock success.")
}
-func (cli *Client) doCall(ctx context.Context, method string, url string, data map[string]any) (
+func (cli *RestClient) doCall(ctx context.Context, method string, url string, data map[string]any) (
http.Header, []byte, error) {
var err error
var reqUrl string
@@ -382,7 +383,7 @@ func (cli *Client) doCall(ctx context.Context, method string, url string, data m
return resp.Header, respBody, nil
}
-func (cli *Client) setRequestHeader(ctx context.Context, req *http.Request, url string) {
+func (cli *RestClient) setRequestHeader(ctx context.Context, req *http.Request, url string) {
req.Header.Set("Referer", cli.url)
req.Header.Set("Content-Type", "application/json")
@@ -401,7 +402,7 @@ func (cli *Client) setRequestHeader(ctx context.Context, req *http.Request, url
}
}
-func (cli *Client) baseCall(ctx context.Context, method string, url string, data map[string]interface{}) (http.Header,
+func (cli *RestClient) baseCall(ctx context.Context, method string, url string, data map[string]any) (http.Header,
map[string]any, error) {
var body map[string]any
respHeader, respBody, err := cli.doCall(ctx, method, url, data)
@@ -416,7 +417,7 @@ func (cli *Client) baseCall(ctx context.Context, method string, url string, data
return respHeader, body, nil
}
-func (cli *Client) retryCall(ctx context.Context, method string, url string, data map[string]any) (
+func (cli *RestClient) retryCall(ctx context.Context, method string, url string, data map[string]any) (
http.Header, map[string]any, error) {
log.AddContext(ctx).Debugf("retry call: method: %s, url: %s, data: %v.", method, url, data)
@@ -444,7 +445,7 @@ func (cli *Client) retryCall(ctx context.Context, method string, url string, dat
return respHeader, body, nil
}
-func (cli *Client) call(ctx context.Context, method string, url string, data map[string]any) (
+func (cli *RestClient) call(ctx context.Context, method string, url string, data map[string]any) (
http.Header, map[string]any, error) {
var body map[string]any
@@ -476,7 +477,7 @@ func (cli *Client) call(ctx context.Context, method string, url string, data map
return respHeader, body, nil
}
-func (cli *Client) reLogin(ctx context.Context) error {
+func (cli *RestClient) reLogin(ctx context.Context) error {
cli.reLoginLock(ctx)
defer cli.reLoginUnlock(ctx)
@@ -497,7 +498,7 @@ func (cli *Client) reLogin(ctx context.Context) error {
return nil
}
-func (cli *Client) get(ctx context.Context,
+func (cli *RestClient) get(ctx context.Context,
url string,
data map[string]interface{}) (map[string]interface{}, error) {
_, body, err := cli.call(ctx, "GET", url, data)
@@ -505,32 +506,32 @@ func (cli *Client) get(ctx context.Context,
}
// Post used to send post request to storage client
-func (cli *Client) Post(ctx context.Context, url string, data map[string]interface{}) (map[string]interface{}, error) {
+func (cli *RestClient) Post(ctx context.Context, url string, data map[string]any) (map[string]any, error) {
return cli.post(ctx, url, data)
}
-func (cli *Client) post(ctx context.Context,
+func (cli *RestClient) post(ctx context.Context,
url string,
data map[string]interface{}) (map[string]interface{}, error) {
_, body, err := cli.call(ctx, "POST", url, data)
return body, err
}
-func (cli *Client) put(ctx context.Context,
+func (cli *RestClient) put(ctx context.Context,
url string,
data map[string]interface{}) (map[string]interface{}, error) {
_, body, err := cli.call(ctx, "PUT", url, data)
return body, err
}
-func (cli *Client) delete(ctx context.Context,
+func (cli *RestClient) delete(ctx context.Context,
url string,
data map[string]interface{}) (map[string]interface{}, error) {
_, body, err := cli.call(ctx, "DELETE", url, data)
return body, err
}
-func (cli *Client) checkErrorCode(ctx context.Context, resp map[string]interface{}, errorCode int64) bool {
+func (cli *RestClient) checkErrorCode(ctx context.Context, resp map[string]interface{}, errorCode int64) bool {
details, exist := resp["detail"].([]interface{})
if !exist || len(details) == 0 {
return false
@@ -590,19 +591,19 @@ func newHTTPClientByBackendID(ctx context.Context, backendID string) (*http.Clie
jar, err := cookiejar.New(nil)
if err != nil {
log.AddContext(ctx).Errorf("create jar failed, error: %v", err)
- return nil, err
+ return defaultHttpClient(), err
}
useCert, certMeta, err := pkgUtils.GetCertSecretFromBackendID(ctx, backendID)
if err != nil {
log.AddContext(ctx).Errorf("get cert secret from backend [%v] failed, error: %v", backendID, err)
- return nil, err
+ return defaultHttpClient(), err
}
useCert, certPool, err := pkgUtils.GetCertPool(ctx, useCert, certMeta)
if err != nil {
log.AddContext(ctx).Errorf("get cert pool failed, error: %v", err)
- return nil, err
+ return defaultHttpClient(), err
}
return &http.Client{
@@ -610,6 +611,14 @@ func newHTTPClientByBackendID(ctx context.Context, backendID string) (*http.Clie
TLSClientConfig: &tls.Config{InsecureSkipVerify: !useCert, RootCAs: certPool},
},
Jar: jar,
- Timeout: 60 * time.Second,
+ Timeout: defaultHttpTimeout,
}, nil
}
+
+func defaultHttpClient() *http.Client {
+ var defaultUseCert bool
+ return &http.Client{
+ Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !defaultUseCert}},
+ Timeout: defaultHttpTimeout,
+ }
+}
diff --git a/storage/fusionstorage/client/client_host.go b/storage/fusionstorage/client/client_host.go
index 50d96d83..9ffc72df 100644
--- a/storage/fusionstorage/client/client_host.go
+++ b/storage/fusionstorage/client/client_host.go
@@ -29,7 +29,7 @@ const (
)
// GetHostByName used to get host by name
-func (cli *Client) GetHostByName(ctx context.Context, hostName string) (map[string]interface{}, error) {
+func (cli *RestClient) GetHostByName(ctx context.Context, hostName string) (map[string]interface{}, error) {
data := map[string]interface{}{
"hostName": hostName,
}
@@ -66,7 +66,7 @@ func (cli *Client) GetHostByName(ctx context.Context, hostName string) (map[stri
}
// CreateHost used to create host
-func (cli *Client) CreateHost(ctx context.Context,
+func (cli *RestClient) CreateHost(ctx context.Context,
hostName string,
alua map[string]interface{}) error {
data := map[string]interface{}{
@@ -97,7 +97,7 @@ func (cli *Client) CreateHost(ctx context.Context,
}
// UpdateHost used to update host
-func (cli *Client) UpdateHost(ctx context.Context, hostName string, alua map[string]interface{}) error {
+func (cli *RestClient) UpdateHost(ctx context.Context, hostName string, alua map[string]interface{}) error {
data := map[string]interface{}{
"hostName": hostName,
}
@@ -124,7 +124,7 @@ func (cli *Client) UpdateHost(ctx context.Context, hostName string, alua map[str
}
// QueryHostByPort used query host by port
-func (cli *Client) QueryHostByPort(ctx context.Context, port string) (string, error) {
+func (cli *RestClient) QueryHostByPort(ctx context.Context, port string) (string, error) {
data := map[string]interface{}{
"portName": []string{port},
}
@@ -160,7 +160,7 @@ func (cli *Client) QueryHostByPort(ctx context.Context, port string) (string, er
}
// AddPortToHost used add port to host
-func (cli *Client) AddPortToHost(ctx context.Context, initiatorName, hostName string) error {
+func (cli *RestClient) AddPortToHost(ctx context.Context, initiatorName, hostName string) error {
data := map[string]interface{}{
"hostName": hostName,
"portNames": []string{initiatorName},
@@ -182,7 +182,7 @@ func (cli *Client) AddPortToHost(ctx context.Context, initiatorName, hostName st
}
// AddLunToHost usd to add lun to host
-func (cli *Client) AddLunToHost(ctx context.Context, lunName, hostName string) error {
+func (cli *RestClient) AddLunToHost(ctx context.Context, lunName, hostName string) error {
data := map[string]interface{}{
"hostName": hostName,
"lunNames": []string{lunName},
@@ -202,7 +202,7 @@ func (cli *Client) AddLunToHost(ctx context.Context, lunName, hostName string) e
}
// DeleteLunFromHost used to delete lun from host
-func (cli *Client) DeleteLunFromHost(ctx context.Context, lunName, hostName string) error {
+func (cli *RestClient) DeleteLunFromHost(ctx context.Context, lunName, hostName string) error {
data := map[string]interface{}{
"hostName": hostName,
"lunNames": []string{lunName},
@@ -222,7 +222,7 @@ func (cli *Client) DeleteLunFromHost(ctx context.Context, lunName, hostName stri
}
// QueryHostOfVolume used to query host of volume
-func (cli *Client) QueryHostOfVolume(ctx context.Context, lunName string) ([]map[string]interface{}, error) {
+func (cli *RestClient) QueryHostOfVolume(ctx context.Context, lunName string) ([]map[string]interface{}, error) {
data := map[string]interface{}{
"lunName": lunName,
}
diff --git a/storage/fusionstorage/client/client_iscsi.go b/storage/fusionstorage/client/client_iscsi.go
index bb63ddd4..b2c3c225 100644
--- a/storage/fusionstorage/client/client_iscsi.go
+++ b/storage/fusionstorage/client/client_iscsi.go
@@ -30,7 +30,7 @@ const (
)
// GetInitiatorByName used to get initiator by name
-func (cli *Client) GetInitiatorByName(ctx context.Context, name string) (map[string]interface{}, error) {
+func (cli *RestClient) GetInitiatorByName(ctx context.Context, name string) (map[string]interface{}, error) {
data := map[string]interface{}{
"portName": name,
}
@@ -60,7 +60,7 @@ func (cli *Client) GetInitiatorByName(ctx context.Context, name string) (map[str
}
// CreateInitiator used to create initiator by name
-func (cli *Client) CreateInitiator(ctx context.Context, name string) error {
+func (cli *RestClient) CreateInitiator(ctx context.Context, name string) error {
data := map[string]interface{}{
"portName": name,
}
@@ -81,7 +81,7 @@ func (cli *Client) CreateInitiator(ctx context.Context, name string) error {
}
// QueryIscsiPortal used to query iscsi portal
-func (cli *Client) QueryIscsiPortal(ctx context.Context) ([]map[string]interface{}, error) {
+func (cli *RestClient) QueryIscsiPortal(ctx context.Context) ([]map[string]interface{}, error) {
data := make(map[string]interface{})
resp, err := cli.post(ctx, "/dsware/service/cluster/dswareclient/queryIscsiPortal", data)
if err != nil {
diff --git a/storage/fusionstorage/client/client_namespace.go b/storage/fusionstorage/client/client_namespace.go
index c4cdfd5f..fe473317 100644
--- a/storage/fusionstorage/client/client_namespace.go
+++ b/storage/fusionstorage/client/client_namespace.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -34,7 +34,8 @@ const (
)
// CreateFileSystem used to create file system by params
-func (cli *Client) CreateFileSystem(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
+func (cli *RestClient) CreateFileSystem(ctx context.Context,
+ params map[string]interface{}) (map[string]interface{}, error) {
data := map[string]interface{}{
"name": params["name"].(string),
"storage_pool_id": params["poolId"].(int64),
@@ -85,7 +86,7 @@ func (cli *Client) CreateFileSystem(ctx context.Context, params map[string]inter
}
// DeleteFileSystem used to delete file system by id
-func (cli *Client) DeleteFileSystem(ctx context.Context, id string) error {
+func (cli *RestClient) DeleteFileSystem(ctx context.Context, id string) error {
url := fmt.Sprintf("/api/v2/converged_service/namespaces/%s", id)
resp, err := cli.delete(ctx, url, nil)
if err != nil {
@@ -108,7 +109,7 @@ func (cli *Client) DeleteFileSystem(ctx context.Context, id string) error {
}
// GetFileSystemByName used to get file system by name
-func (cli *Client) GetFileSystemByName(ctx context.Context, name string) (map[string]interface{}, error) {
+func (cli *RestClient) GetFileSystemByName(ctx context.Context, name string) (map[string]interface{}, error) {
url := fmt.Sprintf("/api/v2/converged_service/namespaces?name=%s", name)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -144,7 +145,7 @@ func (cli *Client) GetFileSystemByName(ctx context.Context, name string) (map[st
}
// CreateNfsShare used to create nfs share by params
-func (cli *Client) CreateNfsShare(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
+func (cli *RestClient) CreateNfsShare(ctx context.Context, params map[string]any) (map[string]any, error) {
data := map[string]interface{}{
"share_path": params["sharepath"].(string),
"file_system_id": params["fsid"].(string),
@@ -185,7 +186,7 @@ func (cli *Client) CreateNfsShare(ctx context.Context, params map[string]interfa
}
// DeleteNfsShare used to delete nfs share by id
-func (cli *Client) DeleteNfsShare(ctx context.Context, id, accountId string) error {
+func (cli *RestClient) DeleteNfsShare(ctx context.Context, id, accountId string) error {
url := fmt.Sprintf("/api/v2/nas_protocol/nfs_share?id=%s&account_id=%s", id, accountId)
resp, err := cli.delete(ctx, url, nil)
if err != nil {
@@ -209,7 +210,7 @@ func (cli *Client) DeleteNfsShare(ctx context.Context, id, accountId string) err
}
// GetNfsShareByPath used to get nfs share by path
-func (cli *Client) GetNfsShareByPath(ctx context.Context, path, accountId string) (map[string]interface{}, error) {
+func (cli *RestClient) GetNfsShareByPath(ctx context.Context, path, accountId string) (map[string]interface{}, error) {
bytesPath, err := json.Marshal([]map[string]string{{"share_path": path}})
if err != nil {
return nil, err
@@ -269,7 +270,7 @@ type AllowNfsShareAccessRequest struct {
}
// AllowNfsShareAccess used for create nfs share client
-func (cli *Client) AllowNfsShareAccess(ctx context.Context, req *AllowNfsShareAccessRequest) error {
+func (cli *RestClient) AllowNfsShareAccess(ctx context.Context, req *AllowNfsShareAccessRequest) error {
data := map[string]interface{}{
"access_name": req.AccessName,
"share_id": req.ShareId,
@@ -306,7 +307,7 @@ func (cli *Client) AllowNfsShareAccess(ctx context.Context, req *AllowNfsShareAc
}
// DeleteNfsShareAccess used to delete nfs share access by id
-func (cli *Client) DeleteNfsShareAccess(ctx context.Context, accessID string) error {
+func (cli *RestClient) DeleteNfsShareAccess(ctx context.Context, accessID string) error {
url := fmt.Sprintf("/api/v2/nas_protocol/nfs_share_auth_client?id=%s", accessID)
resp, err := cli.delete(ctx, url, nil)
if err != nil {
@@ -329,7 +330,7 @@ func (cli *Client) DeleteNfsShareAccess(ctx context.Context, accessID string) er
}
// GetNfsShareAccess used to get nfs share access by id
-func (cli *Client) GetNfsShareAccess(ctx context.Context, shareID string) (map[string]interface{}, error) {
+func (cli *RestClient) GetNfsShareAccess(ctx context.Context, shareID string) (map[string]interface{}, error) {
url := fmt.Sprintf("/api/v2/nas_protocol/nfs_share_auth_client_list?filter=share_id::%s", shareID)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -361,7 +362,7 @@ func (cli *Client) GetNfsShareAccess(ctx context.Context, shareID string) (map[s
}
// GetQuotaByFileSystemName query quota info by file system name
-func (cli *Client) GetQuotaByFileSystemName(ctx context.Context, fsName string) (map[string]interface{}, error) {
+func (cli *RestClient) GetQuotaByFileSystemName(ctx context.Context, fsName string) (map[string]interface{}, error) {
fs, err := cli.GetFileSystemByName(ctx, fsName)
if err != nil {
log.AddContext(ctx).Errorf("Get filesystem %s error: %v", fsName, err)
diff --git a/storage/fusionstorage/client/client_namespace_test.go b/storage/fusionstorage/client/client_namespace_test.go
index d94c20f7..62be9889 100644
--- a/storage/fusionstorage/client/client_namespace_test.go
+++ b/storage/fusionstorage/client/client_namespace_test.go
@@ -21,8 +21,8 @@ import (
"reflect"
"testing"
- "bou.ke/monkey"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/agiledragon/gomonkey/v2"
+ "github.com/stretchr/testify/require"
)
const (
@@ -30,15 +30,15 @@ const (
)
func TestAllowNfsShareAccess(t *testing.T) {
- convey.Convey("Normal", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
- func(_ *Client, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
+ t.Run("Normal", func(t *testing.T) {
+ guard := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "Post",
+ func(_ *RestClient, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
return map[string]interface{}{
"data": map[string]interface{}{},
"result": map[string]interface{}{"code": float64(0), "description": ""},
}, nil
})
- defer guard.Unpatch()
+ defer guard.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
AccessName: "test",
@@ -48,17 +48,17 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
AccountId: "0",
})
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("Result Code Not Exist", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
- func(_ *Client, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
+ t.Run("Result Code Not Exist", func(t *testing.T) {
+ guard := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "Post",
+ func(_ *RestClient, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
return map[string]interface{}{
"data": map[string]interface{}{},
}, nil
})
- defer guard.Unpatch()
+ defer guard.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
AccessName: "test",
@@ -68,18 +68,18 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
AccountId: "0",
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("Client Already Exist", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
- func(_ *Client, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
+ t.Run("RestClient Already Exist", func(t *testing.T) {
+ guard := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "Post",
+ func(_ *RestClient, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
return map[string]interface{}{
"data": map[string]interface{}{},
"result": map[string]interface{}{"code": float64(clientAlreadyExist), "description": ""},
}, nil
})
- defer guard.Unpatch()
+ defer guard.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
AccessName: "test",
@@ -89,18 +89,18 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
AccountId: "0",
})
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("Error code is not zero", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
- func(_ *Client, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
+ t.Run("Error code is not zero", func(t *testing.T) {
+ guard := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "Post",
+ func(_ *RestClient, _ context.Context, _ string, _ map[string]interface{}) (map[string]interface{}, error) {
return map[string]interface{}{
"data": map[string]interface{}{},
"result": map[string]interface{}{"code": float64(100), "description": ""},
}, nil
})
- defer guard.Unpatch()
+ defer guard.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
AccessName: "test",
@@ -110,6 +110,6 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
AccountId: "0",
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
diff --git a/storage/fusionstorage/client/client_qos.go b/storage/fusionstorage/client/client_qos.go
index 4deb1b56..760eeeab 100644
--- a/storage/fusionstorage/client/client_qos.go
+++ b/storage/fusionstorage/client/client_qos.go
@@ -29,7 +29,7 @@ import (
)
// GetConvergedQoSNameByID used to get qos name by id
-func (cli *Client) GetConvergedQoSNameByID(ctx context.Context, qosId int) (string, error) {
+func (cli *RestClient) GetConvergedQoSNameByID(ctx context.Context, qosId int) (string, error) {
url := fmt.Sprintf("/api/v2/dros_service/converged_qos_policy?qos_scale=%d&id=%d",
types.QosScaleNamespace, qosId)
resp, err := cli.get(ctx, url, nil)
@@ -55,7 +55,7 @@ func (cli *Client) GetConvergedQoSNameByID(ctx context.Context, qosId int) (stri
}
// CreateConvergedQoS used to create converged QoS
-func (cli *Client) CreateConvergedQoS(ctx context.Context, req *types.CreateConvergedQoSReq) (int, error) {
+func (cli *RestClient) CreateConvergedQoS(ctx context.Context, req *types.CreateConvergedQoSReq) (int, error) {
data := map[string]interface{}{
"account_id": cli.accountId,
"qos_scale": req.QosScale,
@@ -88,7 +88,7 @@ func (cli *Client) CreateConvergedQoS(ctx context.Context, req *types.CreateConv
}
// DeleteConvergedQoS used to delete converged QoS by name
-func (cli *Client) DeleteConvergedQoS(ctx context.Context, qosName string) error {
+func (cli *RestClient) DeleteConvergedQoS(ctx context.Context, qosName string) error {
url := fmt.Sprintf("/api/v2/dros_service/converged_qos_policy?qos_scale=%d&name=%s",
types.QosScaleNamespace, qosName)
resp, err := cli.delete(ctx, url, nil)
@@ -100,7 +100,7 @@ func (cli *Client) DeleteConvergedQoS(ctx context.Context, qosName string) error
}
// CreateQoS used to create QoS
-func (cli *Client) CreateQoS(ctx context.Context, qosName string, qosData map[string]int) error {
+func (cli *RestClient) CreateQoS(ctx context.Context, qosName string, qosData map[string]int) error {
data := map[string]interface{}{
"qosName": qosName,
"qosSpecInfo": qosData,
@@ -121,7 +121,7 @@ func (cli *Client) CreateQoS(ctx context.Context, qosName string, qosData map[st
}
// DeleteQoS used to delete QoS by name
-func (cli *Client) DeleteQoS(ctx context.Context, qosName string) error {
+func (cli *RestClient) DeleteQoS(ctx context.Context, qosName string) error {
data := map[string]interface{}{
"qosNames": []string{qosName},
}
@@ -141,7 +141,7 @@ func (cli *Client) DeleteQoS(ctx context.Context, qosName string) error {
}
// DisassociateConvergedQoSWithVolume used to delete a converged QoS policy association
-func (cli *Client) DisassociateConvergedQoSWithVolume(ctx context.Context, objectName string) error {
+func (cli *RestClient) DisassociateConvergedQoSWithVolume(ctx context.Context, objectName string) error {
url := fmt.Sprintf("/api/v2/dros_service/converged_qos_association?qos_scale=%d&object_name=%s",
types.QosScaleNamespace, objectName)
resp, err := cli.delete(ctx, url, nil)
@@ -153,7 +153,7 @@ func (cli *Client) DisassociateConvergedQoSWithVolume(ctx context.Context, objec
}
// AssociateConvergedQoSWithVolume used to add a converged QoS policy association
-func (cli *Client) AssociateConvergedQoSWithVolume(ctx context.Context,
+func (cli *RestClient) AssociateConvergedQoSWithVolume(ctx context.Context,
req *types.AssociateConvergedQoSWithVolumeReq) error {
data := map[string]interface{}{
@@ -172,7 +172,7 @@ func (cli *Client) AssociateConvergedQoSWithVolume(ctx context.Context,
}
// AssociateQoSWithVolume used to associate QoS with volume
-func (cli *Client) AssociateQoSWithVolume(ctx context.Context, volName, qosName string) error {
+func (cli *RestClient) AssociateQoSWithVolume(ctx context.Context, volName, qosName string) error {
data := map[string]interface{}{
"keyNames": []string{volName},
"qosName": qosName,
@@ -193,7 +193,7 @@ func (cli *Client) AssociateQoSWithVolume(ctx context.Context, volName, qosName
}
// DisassociateQoSWithVolume used to disassociate QoS with volume
-func (cli *Client) DisassociateQoSWithVolume(ctx context.Context, volName, qosName string) error {
+func (cli *RestClient) DisassociateQoSWithVolume(ctx context.Context, volName, qosName string) error {
data := map[string]interface{}{
"keyNames": []string{volName},
"qosName": qosName,
@@ -214,7 +214,7 @@ func (cli *Client) DisassociateQoSWithVolume(ctx context.Context, volName, qosNa
}
// GetQoSPolicyAssociationCount used to get count of qos association
-func (cli *Client) GetQoSPolicyAssociationCount(ctx context.Context, qosPolicyId int) (int, error) {
+func (cli *RestClient) GetQoSPolicyAssociationCount(ctx context.Context, qosPolicyId int) (int, error) {
filterRaw := fmt.Sprintf("{\"qos_policy_id\":\"%d\",\"qos_scale\":\"%d\",\"account_id\":\"%d\"}",
qosPolicyId, types.QosScaleNamespace, cli.accountId)
url := fmt.Sprintf("/api/v2/dros_service/converged_qos_association_count?filter=%s",
@@ -245,7 +245,7 @@ func (cli *Client) GetQoSPolicyAssociationCount(ctx context.Context, qosPolicyId
}
// GetQoSPolicyIdByFsName used to get qos id by fs name
-func (cli *Client) GetQoSPolicyIdByFsName(ctx context.Context, namespaceName string) (int, error) {
+func (cli *RestClient) GetQoSPolicyIdByFsName(ctx context.Context, namespaceName string) (int, error) {
rangeRaw := "{\"offset\":0,\"limit\":100}"
filterRaw := fmt.Sprintf("{\"object_name\":\"%s\",\"qos_scale\":\"0\",\"account_id\":\"%d\"}",
namespaceName, cli.accountId)
@@ -285,7 +285,7 @@ func (cli *Client) GetQoSPolicyIdByFsName(ctx context.Context, namespaceName str
}
// GetQoSNameByVolume used to get QoS name by volume name
-func (cli *Client) GetQoSNameByVolume(ctx context.Context, volName string) (string, error) {
+func (cli *RestClient) GetQoSNameByVolume(ctx context.Context, volName string) (string, error) {
url := fmt.Sprintf("/dsware/service/v1.3/volume/qos?volName=%s", volName)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -307,7 +307,7 @@ func (cli *Client) GetQoSNameByVolume(ctx context.Context, volName string) (stri
}
// GetAssociateCountOfQoS used to get associate count of QoS
-func (cli *Client) GetAssociateCountOfQoS(ctx context.Context, qosName string) (int, error) {
+func (cli *RestClient) GetAssociateCountOfQoS(ctx context.Context, qosName string) (int, error) {
storagePools, err := cli.getAllPools(ctx)
if err != nil {
return 0, err
@@ -360,7 +360,7 @@ func (cli *Client) GetAssociateCountOfQoS(ctx context.Context, qosName string) (
return 0, nil
}
-func (cli *Client) getAssociateObjOfQoS(ctx context.Context,
+func (cli *RestClient) getAssociateObjOfQoS(ctx context.Context,
qosName, objType string,
poolId int64) (map[string]interface{}, error) {
data := map[string]interface{}{
@@ -382,7 +382,7 @@ func (cli *Client) getAssociateObjOfQoS(ctx context.Context,
return resp, nil
}
-func (cli *Client) getAssociatePoolOfQoS(ctx context.Context, qosName string) (map[string]interface{}, error) {
+func (cli *RestClient) getAssociatePoolOfQoS(ctx context.Context, qosName string) (map[string]interface{}, error) {
data := map[string]interface{}{
"qosName": qosName,
}
diff --git a/storage/fusionstorage/client/client_quota.go b/storage/fusionstorage/client/client_quota.go
index 40e4b0bb..23319415 100644
--- a/storage/fusionstorage/client/client_quota.go
+++ b/storage/fusionstorage/client/client_quota.go
@@ -29,7 +29,7 @@ const (
)
// CreateQuota creates quota by params
-func (cli *Client) CreateQuota(ctx context.Context, params map[string]interface{}) error {
+func (cli *RestClient) CreateQuota(ctx context.Context, params map[string]interface{}) error {
resp, err := cli.post(ctx, "/api/v2/file_service/fs_quota", params)
if err != nil {
return err
@@ -52,7 +52,7 @@ func (cli *Client) CreateQuota(ctx context.Context, params map[string]interface{
}
// UpdateQuota updates quota by params
-func (cli *Client) UpdateQuota(ctx context.Context, params map[string]interface{}) error {
+func (cli *RestClient) UpdateQuota(ctx context.Context, params map[string]interface{}) error {
resp, err := cli.put(ctx, "/api/v2/file_service/fs_quota", params)
if err != nil {
return err
@@ -73,7 +73,7 @@ func (cli *Client) UpdateQuota(ctx context.Context, params map[string]interface{
}
// GetQuotaByFileSystemById query quota info by file system id
-func (cli *Client) GetQuotaByFileSystemById(ctx context.Context, fsID string) (map[string]interface{}, error) {
+func (cli *RestClient) GetQuotaByFileSystemById(ctx context.Context, fsID string) (map[string]interface{}, error) {
url := "/api/v2/file_service/fs_quota?parent_type=40&parent_id=" +
fsID + "&range=%7B%22offset%22%3A0%2C%22limit%22%3A100%7D"
resp, err := cli.get(ctx, url, nil)
@@ -110,7 +110,7 @@ func (cli *Client) GetQuotaByFileSystemById(ctx context.Context, fsID string) (m
}
// DeleteQuota deletes quota by id
-func (cli *Client) DeleteQuota(ctx context.Context, quotaID string) error {
+func (cli *RestClient) DeleteQuota(ctx context.Context, quotaID string) error {
url := fmt.Sprintf("/api/v2/file_service/fs_quota/%s", quotaID)
resp, err := cli.delete(ctx, url, nil)
if err != nil {
diff --git a/storage/fusionstorage/client/client_snapshot.go b/storage/fusionstorage/client/client_snapshot.go
index 5a2fcc75..55ef1302 100644
--- a/storage/fusionstorage/client/client_snapshot.go
+++ b/storage/fusionstorage/client/client_snapshot.go
@@ -28,7 +28,7 @@ const (
)
// CreateSnapshot creates volume snapshot
-func (cli *Client) CreateSnapshot(ctx context.Context, snapshotName, volName string) error {
+func (cli *RestClient) CreateSnapshot(ctx context.Context, snapshotName, volName string) error {
data := map[string]interface{}{
"volName": volName,
"snapshotName": snapshotName,
@@ -48,7 +48,7 @@ func (cli *Client) CreateSnapshot(ctx context.Context, snapshotName, volName str
}
// DeleteSnapshot deletes volume snapshot
-func (cli *Client) DeleteSnapshot(ctx context.Context, snapshotName string) error {
+func (cli *RestClient) DeleteSnapshot(ctx context.Context, snapshotName string) error {
data := map[string]interface{}{
"snapshotName": snapshotName,
}
@@ -67,7 +67,7 @@ func (cli *Client) DeleteSnapshot(ctx context.Context, snapshotName string) erro
}
// GetSnapshotByName get snapshot by name
-func (cli *Client) GetSnapshotByName(ctx context.Context, snapshotName string) (map[string]interface{}, error) {
+func (cli *RestClient) GetSnapshotByName(ctx context.Context, snapshotName string) (map[string]interface{}, error) {
url := fmt.Sprintf("/dsware/service/v1.3/snapshot/queryByName?snapshotName=%s", snapshotName)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -94,7 +94,7 @@ func (cli *Client) GetSnapshotByName(ctx context.Context, snapshotName string) (
}
// CreateVolumeFromSnapshot creates volume from snapshot
-func (cli *Client) CreateVolumeFromSnapshot(ctx context.Context,
+func (cli *RestClient) CreateVolumeFromSnapshot(ctx context.Context,
volName string,
volSize int64,
snapshotName string) error {
diff --git a/storage/fusionstorage/client/client_system.go b/storage/fusionstorage/client/client_system.go
index 898ba241..d565fdb2 100644
--- a/storage/fusionstorage/client/client_system.go
+++ b/storage/fusionstorage/client/client_system.go
@@ -28,7 +28,7 @@ import (
)
// GetAccountIdByName gets account id by account name
-func (cli *Client) GetAccountIdByName(ctx context.Context, accountName string) (string, error) {
+func (cli *RestClient) GetAccountIdByName(ctx context.Context, accountName string) (string, error) {
url := fmt.Sprintf("/dfv/service/obsPOE/query_accounts?name=%s", accountName)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -53,7 +53,7 @@ func (cli *Client) GetAccountIdByName(ctx context.Context, accountName string) (
}
// GetPoolByName gets pool by pool name
-func (cli *Client) GetPoolByName(ctx context.Context, poolName string) (map[string]interface{}, error) {
+func (cli *RestClient) GetPoolByName(ctx context.Context, poolName string) (map[string]interface{}, error) {
resp, err := cli.get(ctx, "/dsware/service/v1.3/storagePool", nil)
if err != nil {
return nil, err
@@ -86,7 +86,7 @@ func (cli *Client) GetPoolByName(ctx context.Context, poolName string) (map[stri
}
// GetPoolById gets pool by pool id
-func (cli *Client) GetPoolById(ctx context.Context, poolId int64) (map[string]interface{}, error) {
+func (cli *RestClient) GetPoolById(ctx context.Context, poolId int64) (map[string]interface{}, error) {
url := fmt.Sprintf("/dsware/service/v1.3/storagePool?poolId=%d", poolId)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -120,7 +120,7 @@ func (cli *Client) GetPoolById(ctx context.Context, poolId int64) (map[string]in
}
// GetAllAccounts gets all accounts
-func (cli *Client) GetAllAccounts(ctx context.Context) ([]string, error) {
+func (cli *RestClient) GetAllAccounts(ctx context.Context) ([]string, error) {
resp, err := cli.get(ctx, "/dfv/service/obsPOE/accounts", nil)
if err != nil {
return nil, err
@@ -154,7 +154,7 @@ func (cli *Client) GetAllAccounts(ctx context.Context) ([]string, error) {
}
// GetAllPools gets all pools
-func (cli *Client) GetAllPools(ctx context.Context) (map[string]interface{}, error) {
+func (cli *RestClient) GetAllPools(ctx context.Context) (map[string]interface{}, error) {
resp, err := cli.get(ctx, "/dsware/service/v1.3/storagePool", nil)
if err != nil {
return nil, err
@@ -189,7 +189,7 @@ func (cli *Client) GetAllPools(ctx context.Context) (map[string]interface{}, err
return pools, nil
}
-func (cli *Client) getAllPools(ctx context.Context) ([]interface{}, error) {
+func (cli *RestClient) getAllPools(ctx context.Context) ([]interface{}, error) {
resp, err := cli.get(ctx, "/dsware/service/v1.3/storagePool", nil)
if err != nil {
return nil, err
@@ -208,7 +208,7 @@ func (cli *Client) getAllPools(ctx context.Context) ([]interface{}, error) {
}
// GetNFSServiceSetting gets nfs service settings
-func (cli *Client) GetNFSServiceSetting(ctx context.Context) (map[string]bool, error) {
+func (cli *RestClient) GetNFSServiceSetting(ctx context.Context) (map[string]bool, error) {
setting := map[string]bool{"SupportNFS41": false}
req := make(map[string]interface{})
diff --git a/storage/fusionstorage/client/client_test.go b/storage/fusionstorage/client/client_test.go
index b1aa14d3..88047c4b 100644
--- a/storage/fusionstorage/client/client_test.go
+++ b/storage/fusionstorage/client/client_test.go
@@ -21,7 +21,7 @@ import (
"testing"
"github.com/prashantv/gostub"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
"huawei-csi-driver/csi/app"
cfg "huawei-csi-driver/csi/app/config"
@@ -29,7 +29,7 @@ import (
)
var (
- testClient *Client
+ testClient *RestClient
)
func TestMain(m *testing.M) {
@@ -56,27 +56,27 @@ func TestMain(m *testing.M) {
}
func TestGetErrorCode(t *testing.T) {
- convey.Convey("Normal case", t, func() {
+ t.Run("Normal case", func(t *testing.T) {
errCode, err := getErrorCode(map[string]any{
"name": "mock-name",
"errorCode": 12345.0,
"suggestion": "mock-suggestion",
})
- convey.So(errCode, convey.ShouldEqual, 12345)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, 12345, errCode)
})
- convey.Convey("Error code is string ", t, func() {
+ t.Run("Error code is string ", func(t *testing.T) {
errCode, err := getErrorCode(map[string]any{
"name": "mock-name",
"errorCode": "12345",
"suggestion": "mock-suggestion",
})
- convey.So(errCode, convey.ShouldEqual, 12345)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, 12345, errCode)
})
- convey.Convey("Error code in result", t, func() {
+ t.Run("Error code in result", func(t *testing.T) {
errCode, err := getErrorCode(map[string]any{
"result": map[string]any{
"code": 12345.0,
@@ -85,11 +85,11 @@ func TestGetErrorCode(t *testing.T) {
"name": "mock-name",
},
})
- convey.So(errCode, convey.ShouldEqual, 12345)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
+ require.Equal(t, 12345, errCode)
})
- convey.Convey("Can not convert to int", t, func() {
+ t.Run("Can not convert to int", func(t *testing.T) {
_, err := getErrorCode(map[string]any{
"result": map[string]any{
"code": "a12345",
@@ -98,6 +98,6 @@ func TestGetErrorCode(t *testing.T) {
"name": "mock-name",
},
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
diff --git a/storage/fusionstorage/client/client_volume.go b/storage/fusionstorage/client/client_volume.go
index 31404e1b..e3924ce6 100644
--- a/storage/fusionstorage/client/client_volume.go
+++ b/storage/fusionstorage/client/client_volume.go
@@ -32,7 +32,7 @@ const (
)
// CreateVolume creates volume by params
-func (cli *Client) CreateVolume(ctx context.Context, params map[string]interface{}) error {
+func (cli *RestClient) CreateVolume(ctx context.Context, params map[string]interface{}) error {
data := map[string]interface{}{
"volName": params["name"].(string),
"volSize": params["capacity"].(int64),
@@ -54,7 +54,7 @@ func (cli *Client) CreateVolume(ctx context.Context, params map[string]interface
}
// GetVolumeByName gets volume info by name
-func (cli *Client) GetVolumeByName(ctx context.Context, name string) (map[string]interface{}, error) {
+func (cli *RestClient) GetVolumeByName(ctx context.Context, name string) (map[string]interface{}, error) {
url := fmt.Sprintf("/dsware/service/v1.3/volume/queryByName?volName=%s", name)
resp, err := cli.get(ctx, url, nil)
if err != nil {
@@ -87,7 +87,7 @@ func (cli *Client) GetVolumeByName(ctx context.Context, name string) (map[string
}
// DeleteVolume deletes volume by name
-func (cli *Client) DeleteVolume(ctx context.Context, name string) error {
+func (cli *RestClient) DeleteVolume(ctx context.Context, name string) error {
data := map[string]interface{}{
"volNames": []string{name},
}
@@ -139,7 +139,7 @@ func (cli *Client) DeleteVolume(ctx context.Context, name string) error {
}
// AttachVolume attaches volume target ip
-func (cli *Client) AttachVolume(ctx context.Context, name, ip string) error {
+func (cli *RestClient) AttachVolume(ctx context.Context, name, ip string) error {
data := map[string]interface{}{
"volName": []string{name},
"ipList": []string{ip},
@@ -169,7 +169,7 @@ func (cli *Client) AttachVolume(ctx context.Context, name, ip string) error {
}
// DetachVolume detaches volume from target ip
-func (cli *Client) DetachVolume(ctx context.Context, name, ip string) error {
+func (cli *RestClient) DetachVolume(ctx context.Context, name, ip string) error {
data := map[string]interface{}{
"volName": []string{name},
"ipList": []string{ip},
@@ -199,7 +199,7 @@ func (cli *Client) DetachVolume(ctx context.Context, name, ip string) error {
}
// ExtendVolume extends volume capacity
-func (cli *Client) ExtendVolume(ctx context.Context, lunName string, newCapacity int64) error {
+func (cli *RestClient) ExtendVolume(ctx context.Context, lunName string, newCapacity int64) error {
data := map[string]interface{}{
"volName": lunName,
"newVolSize": newCapacity,
@@ -219,7 +219,7 @@ func (cli *Client) ExtendVolume(ctx context.Context, lunName string, newCapacity
}
// GetHostLunId gets host lun id of hostName
-func (cli *Client) GetHostLunId(ctx context.Context, hostName, lunName string) (string, error) {
+func (cli *RestClient) GetHostLunId(ctx context.Context, hostName, lunName string) (string, error) {
data := map[string]interface{}{
"hostName": hostName,
}
diff --git a/storage/fusionstorage/smartx/smartx.go b/storage/fusionstorage/smartx/smartx.go
index ae06b55e..b2cad7bc 100644
--- a/storage/fusionstorage/smartx/smartx.go
+++ b/storage/fusionstorage/smartx/smartx.go
@@ -71,11 +71,11 @@ func VerifyQos(ctx context.Context, qosConfig string) (map[string]int, error) {
// QoS provides qos client
type QoS struct {
- cli *client.Client
+ cli *client.RestClient
}
// NewQoS inits a new qos client
-func NewQoS(cli *client.Client) *QoS {
+func NewQoS(cli *client.RestClient) *QoS {
return &QoS{
cli: cli,
}
diff --git a/storage/fusionstorage/volume/nas.go b/storage/fusionstorage/volume/nas.go
index cb0f82fd..8b6ce26e 100644
--- a/storage/fusionstorage/volume/nas.go
+++ b/storage/fusionstorage/volume/nas.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -33,8 +33,8 @@ import (
"huawei-csi-driver/storage/fusionstorage/types"
fsUtils "huawei-csi-driver/storage/fusionstorage/utils"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
)
const (
@@ -44,6 +44,8 @@ const (
quotaParentFileSystem = "40"
directoryQuotaType = "1"
quotaInvalidValue = 18446744073709552000
+ waitUntilTimeout = 6 * time.Hour
+ waitUntilInterval = 5 * time.Second
)
const (
@@ -61,11 +63,11 @@ const (
// NAS provides nas storage client
type NAS struct {
- cli *client.Client
+ cli *client.RestClient
}
// NewNAS inits a new nas client
-func NewNAS(cli *client.Client) *NAS {
+func NewNAS(cli *client.RestClient) *NAS {
return &NAS{
cli: cli,
}
@@ -289,7 +291,7 @@ func (p *NAS) Create(ctx context.Context, params map[string]interface{}) (utils.
return nil, err
}
- createTask := taskflow.NewTaskFlow(ctx, "Create-FileSystem-Volume")
+ createTask := flow.NewTaskFlow(ctx, "Create-FileSystem-Volume")
createTask.AddTask("Create-FS", p.createFS, p.revertFS)
createTask.AddTask("Create-Quota", p.createQuota, p.revertQuota)
createTask.AddTask("Create-Converged-QoS", p.createConvergedQoS, p.revertConvergedQoS)
@@ -611,7 +613,7 @@ func (p *NAS) waitFilesystemCreated(ctx context.Context, fsName string) error {
} else {
return false, nil
}
- }, time.Hour*6, time.Second*5)
+ }, waitUntilTimeout, waitUntilInterval)
return err
}
diff --git a/storage/fusionstorage/volume/nas_test.go b/storage/fusionstorage/volume/nas_test.go
index 8d2cc003..2b845aaf 100644
--- a/storage/fusionstorage/volume/nas_test.go
+++ b/storage/fusionstorage/volume/nas_test.go
@@ -22,9 +22,8 @@ import (
"reflect"
"testing"
- "bou.ke/monkey"
"github.com/agiledragon/gomonkey/v2"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
"huawei-csi-driver/storage/fusionstorage/client"
"huawei-csi-driver/storage/fusionstorage/types"
@@ -35,7 +34,7 @@ const (
logName = "expandTest.log"
)
-var testClient *client.Client
+var testClient *client.RestClient
var ctx context.Context
func TestMain(m *testing.M) {
@@ -48,10 +47,10 @@ func TestMain(m *testing.M) {
}
func TestPreCreate(t *testing.T) {
- convey.Convey("Normal", t, func() {
+ t.Run("Normal", func(t *testing.T) {
m := gomonkey.ApplyMethod(reflect.TypeOf(testClient),
"GetPoolByName",
- func(_ *client.Client, ctx context.Context, poolName string) (map[string]interface{}, error) {
+ func(_ *client.RestClient, ctx context.Context, poolName string) (map[string]interface{}, error) {
return map[string]interface{}{"mock": "mock"}, nil
})
defer m.Reset()
@@ -61,21 +60,21 @@ func TestPreCreate(t *testing.T) {
"authclient": "*",
"name": "mock-name",
})
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("Auth client empty", t, func() {
+ t.Run("Auth client empty", func(t *testing.T) {
nas := NewNAS(testClient)
err := nas.preCreate(context.TODO(), map[string]interface{}{
"name": "mock-name",
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("Name is empty", t, func() {
+ t.Run("Name is empty", func(t *testing.T) {
m := gomonkey.ApplyMethod(reflect.TypeOf(testClient),
"GetPoolByName",
- func(_ *client.Client, ctx context.Context, poolName string) (map[string]interface{}, error) {
+ func(_ *client.RestClient, ctx context.Context, poolName string) (map[string]interface{}, error) {
return map[string]interface{}{"mock": "mock"}, nil
})
defer m.Reset()
@@ -84,28 +83,26 @@ func TestPreCreate(t *testing.T) {
err := nas.preCreate(context.TODO(), map[string]interface{}{
"authclient": "*",
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestExpandWithNormal(t *testing.T) {
- convey.Convey("Normal", t, func() {
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("Normal", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": float64(522),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": "522@2",
"space_hard_quota": float64(2147483648),
"space_soft_quota": float64(18446744073709551615),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, param map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, param map[string]interface{}) error {
_, exitId := param["id"]
_, exitHardQuota := param["space_hard_quota"]
_, exitSoftQuota := param["space_soft_quota"]
@@ -114,28 +111,28 @@ func TestExpandWithNormal(t *testing.T) {
}
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
}
func TestExpandWithFileSystemNotExit(t *testing.T) {
- convey.Convey("File System Not Exist", t, func() {
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("File System Not Exist", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return nil, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": "522@2",
"space_hard_quota": float64(2147483648),
"space_soft_quota": float64(18446744073709551615),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, param map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, param map[string]interface{}) error {
_, exitId := param["id"]
_, exitHardQuota := param["space_hard_quota"]
_, exitSoftQuota := param["space_soft_quota"]
@@ -144,29 +141,29 @@ func TestExpandWithFileSystemNotExit(t *testing.T) {
}
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestExpandWithQuotaIdNotExist(t *testing.T) {
- convey.Convey("Quota Id Not Exist", t, func() {
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("Quota Id Not Exist", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": float64(522),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"space_hard_quota": float64(2147483648),
"space_soft_quota": float64(18446744073709551615),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, param map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, param map[string]interface{}) error {
_, exitId := param["id"]
_, exitHardQuota := param["space_hard_quota"]
_, exitSoftQuota := param["space_soft_quota"]
@@ -175,28 +172,28 @@ func TestExpandWithQuotaIdNotExist(t *testing.T) {
}
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestExpandWhenHardQuotaOrSoftQuotaNotExist(t *testing.T) {
- convey.Convey("space_hard_quota or space_soft_quota Not Exist", t, func() {
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("space_hard_quota or space_soft_quota Not Exist", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": float64(522),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": "522@2",
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, param map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, param map[string]interface{}) error {
_, exitId := param["id"]
_, exitHardQuota := param["space_hard_quota"]
_, exitSoftQuota := param["space_soft_quota"]
@@ -205,30 +202,29 @@ func TestExpandWhenHardQuotaOrSoftQuotaNotExist(t *testing.T) {
}
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestExpandWhenHardQuotaNotExist(t *testing.T) {
- convey.Convey("Hard Quota Not Exist", t, func() {
-
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("Hard Quota Not Exist", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": float64(522),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": "522@2",
"space_hard_quota": float64(18446744073709551615),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, param map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, param map[string]interface{}) error {
_, exitId := param["id"]
_, exitHardQuota := param["space_hard_quota"]
_, exitSoftQuota := param["space_soft_quota"]
@@ -237,30 +233,29 @@ func TestExpandWhenHardQuotaNotExist(t *testing.T) {
}
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestExpandWithSoftQuotaNotExist(t *testing.T) {
- convey.Convey("Soft Quota Not Exist", t, func() {
-
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("Soft Quota Not Exist", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": float64(522),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": "522@2",
"space_soft_quota": float64(18446744073709551615),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, param map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, param map[string]interface{}) error {
_, exitId := param["id"]
_, exitHardQuota := param["space_hard_quota"]
_, exitSoftQuota := param["space_soft_quota"]
@@ -269,108 +264,111 @@ func TestExpandWithSoftQuotaNotExist(t *testing.T) {
}
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestExpandWithUpdateQuotaFail(t *testing.T) {
-
- convey.Convey("Update Quota Fail", t, func() {
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ t.Run("Update Quota Fail", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(reflect.TypeOf(testClient), "GetFileSystemByName",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": float64(522),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
- func(_ *client.Client, _ context.Context, _ string) (map[string]interface{}, error) {
+ }).ApplyMethod(reflect.TypeOf(testClient), "GetQuotaByFileSystemById",
+ func(_ *client.RestClient, _ context.Context, _ string) (map[string]interface{}, error) {
return map[string]interface{}{
"id": "522@2",
"space_hard_quota": float64(2147483648),
"space_soft_quota": float64(18446744073709551615),
}, nil
- })
- _ = monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "UpdateQuota",
- func(_ *client.Client, _ context.Context, _ map[string]interface{}) error {
+ }).ApplyMethod(reflect.TypeOf(testClient), "UpdateQuota",
+ func(_ *client.RestClient, _ context.Context, _ map[string]interface{}) error {
return errors.New("fail")
})
+ defer p.Reset()
+
nas := NewNAS(testClient)
err := nas.Expand(context.TODO(), "123", 3221225472)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestCreateConvergedQoS(t *testing.T) {
- convey.Convey("Empty", t, func() {
+ t.Run("Empty", func(t *testing.T) {
nas := NewNAS(testClient)
param := map[string]interface{}{}
taskResult := map[string]interface{}{}
_, err := nas.createConvergedQoS(ctx, param, taskResult)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("No fsName", t, func() {
+ t.Run("No fsName", func(t *testing.T) {
nas := NewNAS(testClient)
param := map[string]interface{}{
"qos": map[string]int{"maxIOPS": 999, "maxMBPS": 999},
}
taskResult := map[string]interface{}{}
_, err := nas.createConvergedQoS(ctx, param, taskResult)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
func TestPreProcessConvergedQoS(t *testing.T) {
- convey.Convey("Empty", t, func() {
+ t.Run("Empty", func(t *testing.T) {
nas := NewNAS(testClient)
param := map[string]interface{}{}
err := nas.preProcessConvergedQoS(ctx, param)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("not json", t, func() {
+ t.Run("not json", func(t *testing.T) {
nas := NewNAS(testClient)
param := map[string]interface{}{
"qos": "not json",
}
err := nas.preProcessConvergedQoS(ctx, param)
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("normal", t, func() {
+ t.Run("normal", func(t *testing.T) {
nas := NewNAS(testClient)
param := map[string]interface{}{
"qos": "{\"maxMBPS\":999,\"maxIOPS\":999}",
}
err := nas.preProcessConvergedQoS(ctx, param)
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
}
func TestDeleteConvergedQoSByFsName(t *testing.T) {
- convey.Convey("GetQoSPolicyIdByFsName failed", t, func() {
- m := gomonkey.ApplyMethod(reflect.TypeOf(&client.Client{}),
+ t.Run("GetQoSPolicyIdByFsName failed", func(t *testing.T) {
+ m := gomonkey.ApplyMethod(reflect.TypeOf(&client.RestClient{}),
"GetQoSPolicyIdByFsName",
- func(_ *client.Client, _ context.Context, _ string) (int, error) { return 0, errors.New("mock-error") },
+ func(_ *client.RestClient, _ context.Context, _ string) (int, error) {
+ return 0, errors.New("mock-error")
+ },
)
defer m.Reset()
nas := NewNAS(testClient)
err := nas.deleteConvergedQoSByFsName(ctx, "mock-fs-name")
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("GetQoSPolicyIdByFsName empty", t, func() {
- m := gomonkey.ApplyMethod(reflect.TypeOf(&client.Client{}),
+ t.Run("GetQoSPolicyIdByFsName empty", func(t *testing.T) {
+ m := gomonkey.ApplyMethod(reflect.TypeOf(&client.RestClient{}),
"GetQoSPolicyIdByFsName",
- func(_ *client.Client, _ context.Context, _ string) (int, error) { return types.NoQoSPolicyId, nil },
+ func(_ *client.RestClient, _ context.Context, _ string) (int, error) { return types.NoQoSPolicyId, nil },
)
defer m.Reset()
nas := NewNAS(testClient)
err := nas.deleteConvergedQoSByFsName(ctx, "mock-fs-name")
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
}
diff --git a/storage/fusionstorage/volume/san.go b/storage/fusionstorage/volume/san.go
index 28c8f629..1dc8d373 100644
--- a/storage/fusionstorage/volume/san.go
+++ b/storage/fusionstorage/volume/san.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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 (
"huawei-csi-driver/storage/fusionstorage/client"
"huawei-csi-driver/storage/fusionstorage/smartx"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
)
const (
@@ -36,15 +36,17 @@ const (
// ISCSITYPE defines iscsi type
ISCSITYPE = 1
+
+ snapshotRandBase = 10000000000
)
// SAN provides san storage client
type SAN struct {
- cli *client.Client
+ cli *client.RestClient
}
// NewSAN inits a new san client
-func NewSAN(cli *client.Client) *SAN {
+func NewSAN(cli *client.RestClient) *SAN {
return &SAN{
cli: cli,
}
@@ -105,7 +107,7 @@ func (p *SAN) Create(ctx context.Context, params map[string]interface{}) (utils.
return nil, err
}
- taskflow := taskflow.NewTaskFlow(ctx, "Create-FusionStorage-LUN-Volume")
+ taskflow := flow.NewTaskFlow(ctx, "Create-FusionStorage-LUN-Volume")
taskflow.AddTask("Create-LUN", p.createLun, p.revertLun)
taskflow.AddTask("Create-QoS", p.createQoS, nil)
@@ -189,7 +191,7 @@ func (p *SAN) clone(ctx context.Context, params map[string]interface{}) error {
return errors.New(msg)
}
- snapshotName := fmt.Sprintf("k8s_vol_%s_snap_%d", cloneFrom, utils.RandomInt(10000000000))
+ snapshotName := fmt.Sprintf("k8s_vol_%s_snap_%d", cloneFrom, utils.RandomInt(snapshotRandBase))
err = p.cli.CreateSnapshot(ctx, snapshotName, cloneFrom)
if err != nil {
@@ -364,7 +366,7 @@ func (p *SAN) Expand(ctx context.Context, name string, newSize int64) (bool, err
return false, errors.New(msg)
}
- expandTask := taskflow.NewTaskFlow(ctx, "Expand-LUN-Volume")
+ expandTask := flow.NewTaskFlow(ctx, "Expand-LUN-Volume")
expandTask.AddTask("Expand-PreCheck-Capacity", p.preExpandCheckCapacity, nil)
expandTask.AddTask("Expand-Local-Lun", p.expandLocalLun, nil)
@@ -449,7 +451,7 @@ func (p *SAN) CreateSnapshot(ctx context.Context,
}
}
- taskflow := taskflow.NewTaskFlow(ctx, "Create-LUN-Snapshot")
+ taskflow := flow.NewTaskFlow(ctx, "Create-LUN-Snapshot")
taskflow.AddTask("Create-Snapshot", p.createSnapshot, nil)
_, err = taskflow.Run(map[string]interface{}{
diff --git a/storage/oceanstor/attacher/attacher.go b/storage/oceanstor/attacher/attacher.go
index 05a1dafc..3e9d91a1 100644
--- a/storage/oceanstor/attacher/attacher.go
+++ b/storage/oceanstor/attacher/attacher.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -32,20 +32,20 @@ import (
)
const (
- hostGroupType = 14
- lunGroupType = 256
+ splitIqnLength = 6
+ maxHostNameLength = 31
)
-// AttacherPlugin defines interfaces of attach operations
-type AttacherPlugin interface {
+// VolumeAttacherPlugin defines interfaces of attach operations
+type VolumeAttacherPlugin interface {
ControllerAttach(context.Context, string, map[string]interface{}) (map[string]interface{}, error)
ControllerDetach(context.Context, string, map[string]interface{}) (string, error)
getTargetRoCEPortals(context.Context) ([]string, error)
getLunInfo(context.Context, string) (map[string]interface{}, error)
}
-// Attacher defines attacher to attach volume
-type Attacher struct {
+// VolumeAttacher defines attacher to attach volume
+type VolumeAttacher struct {
cli client.BaseClientInterface
protocol string
invoker string
@@ -53,43 +53,48 @@ type Attacher struct {
alua map[string]interface{}
}
+// VolumeAttacherConfig defines the configurations of VolumeAttacher
+type VolumeAttacherConfig struct {
+ Product string
+ Cli client.BaseClientInterface
+ Protocol string
+ Invoker string
+ Portals []string
+ Alua map[string]interface{}
+}
+
// NewAttacher init a new attacher
-func NewAttacher(
- product string,
- cli client.BaseClientInterface,
- protocol, invoker string,
- portals []string,
- alua map[string]interface{}) AttacherPlugin {
- switch product {
+func NewAttacher(config VolumeAttacherConfig) VolumeAttacherPlugin {
+ switch config.Product {
case "DoradoV6":
- return newDoradoV6Attacher(cli, protocol, invoker, portals, alua)
+ return newDoradoV6Attacher(config)
default:
- return newOceanStorAttacher(cli, protocol, invoker, portals, alua)
+ return newOceanStorAttacher(config)
}
}
-func (p *Attacher) getHostName(postfix string) string {
+func (p *VolumeAttacher) getHostName(postfix string) string {
host := fmt.Sprintf("k8s_%s", postfix)
- if len(host) <= 31 {
+ if len(host) <= maxHostNameLength {
return host
}
- return host[:31]
+ return host[:maxHostNameLength]
}
-func (p *Attacher) getHostGroupName(postfix string) string {
+func (p *VolumeAttacher) getHostGroupName(postfix string) string {
return fmt.Sprintf("k8s_%s_hostgroup_%s", p.invoker, postfix)
}
-func (p *Attacher) getLunGroupName(postfix string) string {
+func (p *VolumeAttacher) getLunGroupName(postfix string) string {
return fmt.Sprintf("k8s_%s_lungroup_%s", p.invoker, postfix)
}
-func (p *Attacher) getMappingName(postfix string) string {
+func (p *VolumeAttacher) getMappingName(postfix string) string {
return fmt.Sprintf("k8s_%s_mapping_%s", p.invoker, postfix)
}
-func (p *Attacher) getHost(ctx context.Context,
+func (p *VolumeAttacher) getHost(ctx context.Context,
parameters map[string]interface{},
toCreate bool) (map[string]interface{}, error) {
var err error
@@ -125,7 +130,7 @@ func (p *Attacher) getHost(ctx context.Context,
return nil, nil
}
-func (p *Attacher) createMapping(ctx context.Context, hostID string) (string, error) {
+func (p *VolumeAttacher) createMapping(ctx context.Context, hostID string) (string, error) {
mappingName := p.getMappingName(hostID)
mapping, err := p.cli.GetMappingByName(ctx, mappingName)
if err != nil {
@@ -143,12 +148,12 @@ func (p *Attacher) createMapping(ctx context.Context, hostID string) (string, er
return mapping["ID"].(string), nil
}
-func (p *Attacher) createHostGroup(ctx context.Context, hostID, mappingID string) error {
+func (p *VolumeAttacher) createHostGroup(ctx context.Context, hostID, mappingID string) error {
var err error
var hostGroup map[string]interface{}
var hostGroupID string
- hostGroupsByHostID, err := p.cli.QueryAssociateHostGroup(ctx, 21, hostID)
+ hostGroupsByHostID, err := p.cli.QueryAssociateHostGroup(ctx, client.AssociateObjTypeHost, hostID)
if err != nil {
log.AddContext(ctx).Errorf("Query associated hostgroups of host %s error: %v",
hostID, err)
@@ -201,8 +206,8 @@ func (p *Attacher) createHostGroup(ctx context.Context, hostID, mappingID string
return p.addToHostGroupMapping(ctx, hostGroupName, hostGroupID, mappingID)
}
-func (p *Attacher) addToHostGroupMapping(ctx context.Context, groupName, groupID, mappingID string) error {
- hostGroupsByMappingID, err := p.cli.QueryAssociateHostGroup(ctx, 245, mappingID)
+func (p *VolumeAttacher) addToHostGroupMapping(ctx context.Context, groupName, groupID, mappingID string) error {
+ hostGroupsByMappingID, err := p.cli.QueryAssociateHostGroup(ctx, client.AssociateObjTypeMapping, mappingID)
if err != nil {
log.AddContext(ctx).Errorf("Query associated host groups of mapping %s error: %v", mappingID, err)
return err
@@ -218,7 +223,7 @@ func (p *Attacher) addToHostGroupMapping(ctx context.Context, groupName, groupID
}
}
- err = p.cli.AddGroupToMapping(ctx, hostGroupType, groupID, mappingID)
+ err = p.cli.AddGroupToMapping(ctx, client.AssociateObjTypeHostGroup, groupID, mappingID)
if err != nil {
log.AddContext(ctx).Errorf("Add host group %s to mapping %s error: %v",
groupID, mappingID, err)
@@ -228,11 +233,11 @@ func (p *Attacher) addToHostGroupMapping(ctx context.Context, groupName, groupID
return nil
}
-func (p *Attacher) createLunGroup(ctx context.Context, lunID, hostID, mappingID string) error {
+func (p *VolumeAttacher) createLunGroup(ctx context.Context, lunID, hostID, mappingID string) error {
var err error
var lunGroup map[string]interface{}
- lunGroupsByLunID, err := p.cli.QueryAssociateLunGroup(ctx, 11, lunID)
+ lunGroupsByLunID, err := p.cli.QueryAssociateLunGroup(ctx, client.AssociateObjTypeLUN, lunID)
if err != nil {
log.AddContext(ctx).Errorf("Query associated lun groups of lun %s error: %v", lunID, err)
return err
@@ -280,8 +285,8 @@ func (p *Attacher) createLunGroup(ctx context.Context, lunID, hostID, mappingID
return p.addToLUNGroupMapping(ctx, lunGroupName, lunGroupID, mappingID)
}
-func (p *Attacher) addToLUNGroupMapping(ctx context.Context, groupName, groupID, mappingID string) error {
- lunGroupsByMappingID, err := p.cli.QueryAssociateLunGroup(ctx, 245, mappingID)
+func (p *VolumeAttacher) addToLUNGroupMapping(ctx context.Context, groupName, groupID, mappingID string) error {
+ lunGroupsByMappingID, err := p.cli.QueryAssociateLunGroup(ctx, client.AssociateObjTypeMapping, mappingID)
if err != nil {
log.AddContext(ctx).Errorf("Query associated lun groups of mapping %s error: %v", mappingID, err)
return err
@@ -297,7 +302,7 @@ func (p *Attacher) addToLUNGroupMapping(ctx context.Context, groupName, groupID,
}
}
- err = p.cli.AddGroupToMapping(ctx, lunGroupType, groupID, mappingID)
+ err = p.cli.AddGroupToMapping(ctx, client.AssociateObjTypeLUNGroup, groupID, mappingID)
if err != nil {
log.AddContext(ctx).Errorf("Add lun group %s to mapping %s error: %v",
groupID, mappingID, err)
@@ -307,7 +312,7 @@ func (p *Attacher) addToLUNGroupMapping(ctx context.Context, groupName, groupID,
return nil
}
-func (p *Attacher) needUpdateInitiatorAlua(initiator map[string]interface{}) bool {
+func (p *VolumeAttacher) needUpdateInitiatorAlua(initiator map[string]interface{}) bool {
if p.alua == nil {
return false
}
@@ -341,7 +346,7 @@ func (p *Attacher) needUpdateInitiatorAlua(initiator map[string]interface{}) boo
return false
}
-func (p *Attacher) getISCSIProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]interface{}) (
+func (p *VolumeAttacher) getISCSIProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]any) (
map[string]interface{}, error) {
tgtPortals, tgtIQNs, err := p.getTargetISCSIProperties(ctx)
if err != nil {
@@ -362,7 +367,7 @@ func (p *Attacher) getISCSIProperties(ctx context.Context, wwn, hostLunId string
}, nil
}
-func (p *Attacher) getFCProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]interface{}) (
+func (p *VolumeAttacher) getFCProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]any) (
map[string]interface{}, error) {
tgtWWNs, err := p.getTargetFCProperties(ctx, parameters)
if err != nil {
@@ -382,7 +387,7 @@ func (p *Attacher) getFCProperties(ctx context.Context, wwn, hostLunId string, p
}, nil
}
-func (p *Attacher) getFCNVMeProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]interface{}) (
+func (p *VolumeAttacher) getFCNVMeProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]any) (
map[string]interface{}, error) {
portWWNList, err := p.getTargetFCNVMeProperties(ctx, parameters)
if err != nil {
@@ -395,7 +400,7 @@ func (p *Attacher) getFCNVMeProperties(ctx context.Context, wwn, hostLunId strin
}, nil
}
-func (p *Attacher) getRoCEProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]interface{}) (
+func (p *VolumeAttacher) getRoCEProperties(ctx context.Context, wwn, hostLunId string, parameters map[string]any) (
map[string]interface{}, error) {
tgtPortals, err := p.getTargetRoCEPortals(ctx)
if err != nil {
@@ -408,7 +413,7 @@ func (p *Attacher) getRoCEProperties(ctx context.Context, wwn, hostLunId string,
}, nil
}
-func (p *Attacher) getMappingProperties(ctx context.Context,
+func (p *VolumeAttacher) getMappingProperties(ctx context.Context,
wwn, hostLunId string, parameters map[string]interface{}) (map[string]interface{}, error) {
if p.protocol == "iscsi" {
return p.getISCSIProperties(ctx, wwn, hostLunId, parameters)
@@ -423,7 +428,7 @@ func (p *Attacher) getMappingProperties(ctx context.Context,
return nil, utils.Errorf(ctx, "UnSupport protocol %s", p.protocol)
}
-func (p *Attacher) getTargetISCSIProperties(ctx context.Context) ([]string, []string, error) {
+func (p *VolumeAttacher) getTargetISCSIProperties(ctx context.Context) ([]string, []string, error) {
ports, err := p.cli.GetIscsiTgtPort(ctx)
if err != nil {
log.AddContext(ctx).Errorf("Get iSCSI tgt port error: %v", err)
@@ -451,12 +456,12 @@ func (p *Attacher) getTargetISCSIProperties(ctx context.Context) ([]string, []st
portIqn := strings.Split(strings.Split(portID, ",")[0], "+")[1]
splitIqn := strings.Split(portIqn, ":")
- if len(splitIqn) < 6 {
+ if len(splitIqn) < splitIqnLength {
continue
}
- validIPs[splitIqn[5]] = true
- validIQNs[splitIqn[5]] = portIqn
+ validIPs[splitIqn[splitIqnLength-1]] = true
+ validIQNs[splitIqn[splitIqnLength-1]] = portIqn
}
var tgtPortals []string
@@ -482,7 +487,7 @@ func (p *Attacher) getTargetISCSIProperties(ctx context.Context) ([]string, []st
return tgtPortals, tgtIQNs, nil
}
-func (p *Attacher) getTargetRoCEPortals(ctx context.Context) ([]string, error) {
+func (p *VolumeAttacher) getTargetRoCEPortals(ctx context.Context) ([]string, error) {
var availablePortals []string
for _, portal := range p.portals {
ip := net.ParseIP(portal).String()
@@ -521,8 +526,8 @@ func (p *Attacher) getTargetRoCEPortals(ctx context.Context) ([]string, error) {
return availablePortals, nil
}
-func (p *Attacher) getTargetFCNVMeProperties(ctx context.Context, parameters map[string]interface{}) ([]nvme.PortWWNPair, error) {
-
+func (p *VolumeAttacher) getTargetFCNVMeProperties(ctx context.Context,
+ parameters map[string]interface{}) ([]nvme.PortWWNPair, error) {
fcInitiators, err := GetMultipleInitiators(ctx, FC, parameters)
if err != nil {
log.AddContext(ctx).Errorf("Get fc initiator error:%v", err)
@@ -545,7 +550,7 @@ func (p *Attacher) getTargetFCNVMeProperties(ctx context.Context, parameters map
return ret, nil
}
-func (p *Attacher) getTargetFCProperties(ctx context.Context, parameters map[string]interface{}) ([]string, error) {
+func (p *VolumeAttacher) getTargetFCProperties(ctx context.Context, parameters map[string]any) ([]string, error) {
fcInitiators, err := GetMultipleInitiators(ctx, FC, parameters)
if err != nil {
log.AddContext(ctx).Errorf("Get fc initiator error: %v", err)
@@ -582,7 +587,8 @@ func (p *Attacher) getTargetFCProperties(ctx context.Context, parameters map[str
return tgtWWNs, nil
}
-func (p *Attacher) attachISCSI(ctx context.Context, hostID string, parameters map[string]interface{}) (map[string]interface{}, error) {
+func (p *VolumeAttacher) attachISCSI(ctx context.Context,
+ hostID string, parameters map[string]interface{}) (map[string]interface{}, error) {
name, err := GetSingleInitiator(ctx, ISCSI, parameters)
if err != nil {
log.AddContext(ctx).Errorf("Get ISCSI initiator name error: %v", err)
@@ -626,7 +632,8 @@ func (p *Attacher) attachISCSI(ctx context.Context, hostID string, parameters ma
return initiator, nil
}
-func (p *Attacher) attachFC(ctx context.Context, hostID string, parameters map[string]interface{}) ([]map[string]interface{}, error) {
+func (p *VolumeAttacher) attachFC(ctx context.Context,
+ hostID string, parameters map[string]interface{}) ([]map[string]interface{}, error) {
fcInitiators, err := GetMultipleInitiators(ctx, FC, parameters)
if err != nil {
log.AddContext(ctx).Errorf("Get fc initiator error: %v", err)
@@ -684,7 +691,8 @@ func (p *Attacher) attachFC(ctx context.Context, hostID string, parameters map[s
return hostInitiators, nil
}
-func (p *Attacher) attachRoCE(ctx context.Context, hostID string, parameters map[string]interface{}) (map[string]interface{}, error) {
+func (p *VolumeAttacher) attachRoCE(ctx context.Context,
+ hostID string, parameters map[string]interface{}) (map[string]interface{}, error) {
name, err := GetSingleInitiator(ctx, ROCE, parameters)
if err != nil {
log.AddContext(ctx).Errorf("Get RoCE initiator name error: %v", err)
@@ -728,7 +736,7 @@ func (p *Attacher) attachRoCE(ctx context.Context, hostID string, parameters map
return initiator, nil
}
-func (p *Attacher) doMapping(ctx context.Context, hostID, lunName string) (string, string, error) {
+func (p *VolumeAttacher) doMapping(ctx context.Context, hostID, lunName string) (string, string, error) {
lun, err := p.cli.GetLunByName(ctx, lunName)
if err != nil {
log.AddContext(ctx).Errorf("Get lun %s error: %v", lunName, err)
@@ -775,7 +783,7 @@ func (p *Attacher) doMapping(ctx context.Context, hostID, lunName string) (strin
return lunUniqueId, hostLunId, nil
}
-func (p *Attacher) doUnmapping(ctx context.Context, hostID, lunName string) (string, error) {
+func (p *VolumeAttacher) doUnmapping(ctx context.Context, hostID, lunName string) (string, error) {
lun, err := p.cli.GetLunByName(ctx, lunName)
if err != nil {
log.AddContext(ctx).Errorf("Get lun %s info error: %v", lunName, err)
@@ -789,7 +797,7 @@ func (p *Attacher) doUnmapping(ctx context.Context, hostID, lunName string) (str
if !ok {
return "", pkgUtils.Errorf(ctx, "convert lunID to string failed, data: %v", lun["ID"])
}
- lunGroupsByLunID, err := p.cli.QueryAssociateLunGroup(ctx, 11, lunID)
+ lunGroupsByLunID, err := p.cli.QueryAssociateLunGroup(ctx, client.AssociateObjTypeLUN, lunID)
if err != nil {
log.AddContext(ctx).Errorf("Query associated lungroups of lun %s error: %v", lunID, err)
return "", err
@@ -824,7 +832,7 @@ func (p *Attacher) doUnmapping(ctx context.Context, hostID, lunName string) (str
}
// ControllerDetach detaches volume and unmaps lun from host
-func (p *Attacher) ControllerDetach(ctx context.Context,
+func (p *VolumeAttacher) ControllerDetach(ctx context.Context,
lunName string,
parameters map[string]interface{}) (string, error) {
host, err := p.getHost(ctx, parameters, false)
@@ -850,7 +858,7 @@ func (p *Attacher) ControllerDetach(ctx context.Context,
return wwn, nil
}
-func (p *Attacher) getLunInfo(ctx context.Context, lunName string) (map[string]interface{}, error) {
+func (p *VolumeAttacher) getLunInfo(ctx context.Context, lunName string) (map[string]interface{}, error) {
lun, err := p.cli.GetLunByName(ctx, lunName)
if err != nil {
log.AddContext(ctx).Errorf("Get lun %s info error: %v", lunName, err)
diff --git a/storage/oceanstor/attacher/attacher_utils.go b/storage/oceanstor/attacher/attacher_utils.go
index e3b73175..687bc41e 100644
--- a/storage/oceanstor/attacher/attacher_utils.go
+++ b/storage/oceanstor/attacher/attacher_utils.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -42,7 +42,8 @@ const (
)
// GetMultipleInitiators use this method when the initiator is an array e.g. fc
-func GetMultipleInitiators(ctx context.Context, protocol InitiatorType, parameters map[string]interface{}) ([]string, error) {
+func GetMultipleInitiators(ctx context.Context,
+ protocol InitiatorType, parameters map[string]interface{}) ([]string, error) {
initiatorData, err := getInitiatorByProtocol(ctx, protocol, parameters)
if err != nil {
return nil, err
@@ -56,7 +57,8 @@ func GetMultipleInitiators(ctx context.Context, protocol InitiatorType, paramete
}
// GetSingleInitiator use this method when the initiator is single e.g. iscsi
-func GetSingleInitiator(ctx context.Context, protocol InitiatorType, parameters map[string]interface{}) (string, error) {
+func GetSingleInitiator(ctx context.Context,
+ protocol InitiatorType, parameters map[string]interface{}) (string, error) {
initiatorData, err := getInitiatorByProtocol(ctx, protocol, parameters)
if err != nil {
return "", err
@@ -69,7 +71,8 @@ func GetSingleInitiator(ctx context.Context, protocol InitiatorType, parameters
return "", utils.Errorf(ctx, "convert %v initiator to string error:%v", protocol, initiatorData)
}
-func getInitiatorByProtocol(ctx context.Context, protocol InitiatorType, parameters map[string]interface{}) (interface{}, error) {
+func getInitiatorByProtocol(ctx context.Context,
+ protocol InitiatorType, parameters map[string]interface{}) (interface{}, error) {
hostName, ok := parameters["HostName"].(string)
if !ok {
return nil, utils.Errorf(ctx, "Get node host name error,parameters:%v ", parameters)
diff --git a/storage/oceanstor/attacher/dorado_v6_attacher.go b/storage/oceanstor/attacher/dorado_v6_attacher.go
index f6457946..cd0bd835 100644
--- a/storage/oceanstor/attacher/dorado_v6_attacher.go
+++ b/storage/oceanstor/attacher/dorado_v6_attacher.go
@@ -20,14 +20,13 @@ import (
"context"
"errors"
- "huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
-// DoradoV6Attacher implements interface AttacherPlugin
+// DoradoV6Attacher implements interface VolumeAttacherPlugin
type DoradoV6Attacher struct {
- Attacher
+ VolumeAttacher
}
const (
@@ -35,18 +34,14 @@ const (
AccessModeBalanced = "0"
)
-func newDoradoV6Attacher(
- cli client.BaseClientInterface,
- protocol, invoker string,
- portals []string,
- alua map[string]interface{}) AttacherPlugin {
+func newDoradoV6Attacher(config VolumeAttacherConfig) VolumeAttacherPlugin {
return &DoradoV6Attacher{
- Attacher: Attacher{
- cli: cli,
- protocol: protocol,
- invoker: invoker,
- portals: portals,
- alua: alua,
+ VolumeAttacher: VolumeAttacher{
+ cli: config.Cli,
+ protocol: config.Protocol,
+ invoker: config.Invoker,
+ portals: config.Portals,
+ alua: config.Alua,
},
}
}
@@ -96,11 +91,11 @@ func (p *DoradoV6Attacher) ControllerAttach(ctx context.Context,
}
if p.protocol == "iscsi" {
- _, err = p.Attacher.attachISCSI(ctx, hostID, parameters)
+ _, err = p.VolumeAttacher.attachISCSI(ctx, hostID, parameters)
} else if p.protocol == "fc" || p.protocol == "fc-nvme" {
- _, err = p.Attacher.attachFC(ctx, hostID, parameters)
+ _, err = p.VolumeAttacher.attachFC(ctx, hostID, parameters)
} else if p.protocol == "roce" {
- _, err = p.Attacher.attachRoCE(ctx, hostID, parameters)
+ _, err = p.VolumeAttacher.attachRoCE(ctx, hostID, parameters)
}
if err != nil {
diff --git a/storage/oceanstor/attacher/metroAttacher.go b/storage/oceanstor/attacher/metroAttacher.go
index ce15165c..a5c61e8c 100644
--- a/storage/oceanstor/attacher/metroAttacher.go
+++ b/storage/oceanstor/attacher/metroAttacher.go
@@ -23,15 +23,15 @@ import (
"huawei-csi-driver/utils/log"
)
-// MetroAttacher implements interface AttacherPlugin
+// MetroAttacher implements interface VolumeAttacherPlugin
type MetroAttacher struct {
- localAttacher AttacherPlugin
- remoteAttacher AttacherPlugin
+ localAttacher VolumeAttacherPlugin
+ remoteAttacher VolumeAttacherPlugin
protocol string
}
// NewMetroAttacher inits a new metro attacher
-func NewMetroAttacher(localAttacher, remoteAttacher AttacherPlugin, protocol string) *MetroAttacher {
+func NewMetroAttacher(localAttacher, remoteAttacher VolumeAttacherPlugin, protocol string) *MetroAttacher {
return &MetroAttacher{
localAttacher: localAttacher,
remoteAttacher: remoteAttacher,
diff --git a/storage/oceanstor/attacher/oceanstor_attacher.go b/storage/oceanstor/attacher/oceanstor_attacher.go
index 5a502775..6d3b1a4c 100644
--- a/storage/oceanstor/attacher/oceanstor_attacher.go
+++ b/storage/oceanstor/attacher/oceanstor_attacher.go
@@ -20,14 +20,13 @@ import (
"context"
"errors"
- "huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
-// OceanStorAttacher implements interface AttacherPlugin
+// OceanStorAttacher implements interface VolumeAttacherPlugin
type OceanStorAttacher struct {
- Attacher
+ VolumeAttacher
}
const (
@@ -35,19 +34,14 @@ const (
MultiPathTypeDefault = "0"
)
-func newOceanStorAttacher(
- cli client.BaseClientInterface,
- protocol,
- invoker string,
- portals []string,
- alua map[string]interface{}) AttacherPlugin {
+func newOceanStorAttacher(config VolumeAttacherConfig) VolumeAttacherPlugin {
return &OceanStorAttacher{
- Attacher: Attacher{
- cli: cli,
- protocol: protocol,
- invoker: invoker,
- portals: portals,
- alua: alua,
+ VolumeAttacher: VolumeAttacher{
+ cli: config.Cli,
+ protocol: config.Protocol,
+ invoker: config.Invoker,
+ portals: config.Portals,
+ alua: config.Alua,
},
}
}
@@ -85,7 +79,7 @@ func (p *OceanStorAttacher) needUpdateInitiatorAlua(initiator map[string]interfa
func (p *OceanStorAttacher) attachISCSI(ctx context.Context, hostID, hostName string,
parameters map[string]interface{}) error {
- iscsiInitiator, err := p.Attacher.attachISCSI(ctx, hostID, parameters)
+ iscsiInitiator, err := p.VolumeAttacher.attachISCSI(ctx, hostID, parameters)
if err != nil {
return err
}
@@ -100,7 +94,7 @@ func (p *OceanStorAttacher) attachISCSI(ctx context.Context, hostID, hostName st
func (p *OceanStorAttacher) attachFC(ctx context.Context, hostID, hostName string,
parameters map[string]interface{}) error {
- fcInitiators, err := p.Attacher.attachFC(ctx, hostID, parameters)
+ fcInitiators, err := p.VolumeAttacher.attachFC(ctx, hostID, parameters)
if err != nil {
return err
}
@@ -123,7 +117,7 @@ func (p *OceanStorAttacher) attachFC(ctx context.Context, hostID, hostName strin
}
func (p *OceanStorAttacher) attachRoCE(ctx context.Context, hostID string, parameters map[string]interface{}) error {
- _, err := p.Attacher.attachRoCE(ctx, hostID, parameters)
+ _, err := p.VolumeAttacher.attachRoCE(ctx, hostID, parameters)
return err
}
diff --git a/storage/oceanstor/client/client.go b/storage/oceanstor/client/client.go
index dc16322e..2514bb32 100644
--- a/storage/oceanstor/client/client.go
+++ b/storage/oceanstor/client/client.go
@@ -28,6 +28,7 @@ import (
"net/http"
"net/http/cookiejar"
"regexp"
+ "slices"
"strconv"
"sync"
"sync/atomic"
@@ -55,11 +56,6 @@ const (
// QueryCountPerBatch defines query count for each circle of batch operation
QueryCountPerBatch int = 100
- description string = "Created from huawei-csi for Kubernetes"
-
- defaultVStore string = "System_vStore"
- defaultVStoreID string = "0"
-
// IPLockErrorCode defines error code of ip lock
IPLockErrorCode = 1077949071
@@ -76,6 +72,13 @@ const (
UrlNotFound = "404_NotFound"
)
+const (
+ description string = "Created from huawei-csi for Kubernetes"
+ defaultVStore string = "System_vStore"
+ defaultVStoreID string = "0"
+ defaultHttpTimeout = 60 * time.Second
+)
+
var (
// WrongPasswordErrorCodes user or password is incorrect
WrongPasswordErrorCodes = []int64{1077987870, 1077949081, 1077949061}
@@ -104,7 +107,6 @@ type BaseClientInterface interface {
VStore
DTree
OceanStorQuota
- Container
LIF
Call(ctx context.Context, method string, url string, data map[string]interface{}) (Response, error)
@@ -159,8 +161,8 @@ var (
},
}
- // ClientSemaphore provides semaphore of client
- ClientSemaphore *utils.Semaphore
+ // RequestSemaphore provides semaphore of client
+ RequestSemaphore *utils.Semaphore
)
func isFilterLog(method, url string) bool {
@@ -215,7 +217,7 @@ func newHTTPClientByBackendID(ctx context.Context, backendID string) (HTTP, erro
var defaultUseCert bool
client := &http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !defaultUseCert}},
- Timeout: 60 * time.Second,
+ Timeout: defaultHttpTimeout,
}
jar, err := cookiejar.New(nil)
@@ -258,7 +260,7 @@ func newHTTPClientByCertMeta(ctx context.Context, useCert bool, certMeta string)
TLSClientConfig: &tls.Config{InsecureSkipVerify: !useCert, RootCAs: certPool},
},
Jar: jar,
- Timeout: 60 * time.Second,
+ Timeout: defaultHttpTimeout,
}, nil
}
@@ -334,7 +336,7 @@ func NewClient(ctx context.Context, param *NewClientConfig) (*BaseClient, error)
}
log.AddContext(ctx).Infof("Init parallel count is %d", parallelCount)
- ClientSemaphore = utils.NewSemaphore(parallelCount)
+ RequestSemaphore = utils.NewSemaphore(parallelCount)
httpClient, err := newHTTPClientByCertMeta(ctx, param.UseCert, param.CertSecretMeta)
if err != nil {
@@ -446,7 +448,7 @@ func (cli *BaseClient) GetRequest(ctx context.Context,
if data != nil {
reqBytes, err := json.Marshal(data)
if err != nil {
- log.AddContext(ctx).Errorf("json.Marshal data %v error: %v", data, err)
+ log.AddContext(ctx).Errorf("json.Marshal data %v error: %v", maskRequestData(data), err)
return req, err
}
reqBody = bytes.NewReader(reqBytes)
@@ -501,8 +503,12 @@ func (cli *BaseClient) BaseCall(ctx context.Context,
log.FilteredLog(ctx, isFilterLog(method, url), utils.IsDebugLog(method, url, debugLog, debugLogRegex),
fmt.Sprintf("Request method: %s, Url: %s, body: %v", method, req.URL, data))
- ClientSemaphore.Acquire()
- defer ClientSemaphore.Release()
+ if RequestSemaphore == nil {
+ return r, errors.New("request semaphore is nil")
+ }
+
+ RequestSemaphore.Acquire()
+ defer RequestSemaphore.Release()
resp, err := cli.Client.Do(req)
if err != nil {
@@ -560,9 +566,9 @@ func (cli *BaseClient) SafeBaseCall(ctx context.Context,
log.FilteredLog(ctx, isFilterLog(method, url), utils.IsDebugLog(method, url, debugLog, debugLogRegex),
fmt.Sprintf("Request method: %s, Url: %s, body: %v", method, req.URL, data))
- if ClientSemaphore != nil {
- ClientSemaphore.Acquire()
- defer ClientSemaphore.Release()
+ if RequestSemaphore != nil {
+ RequestSemaphore.Acquire()
+ defer RequestSemaphore.Release()
}
return cli.safeDoCall(ctx, method, url, req)
@@ -901,7 +907,7 @@ func (cli *BaseClient) getCountFromResponse(ctx context.Context, data interface{
if !ok {
return 0, utils.Errorf(ctx, "The COUNT is not in respData %v", respData)
}
- count, err := strconv.ParseInt(countStr, 10, 64)
+ count, err := strconv.ParseInt(countStr, constants.DefaultIntBase, constants.DefaultIntBitSize)
if err != nil {
return 0, err
}
@@ -933,7 +939,7 @@ func (cli *BaseClient) getSystemUTCTime(ctx context.Context) (int64, error) {
return 0, utils.Errorf(ctx, "The CMO_SYS_UTC_TIME is not in respData %v", respData)
}
- time, err := strconv.ParseInt(utcTime, 10, 64)
+ time, err := strconv.ParseInt(utcTime, constants.DefaultIntBase, constants.DefaultIntBitSize)
if err != nil {
return 0, err
}
@@ -1115,3 +1121,18 @@ func (cli *BaseClient) setLifInfo(ctx context.Context) {
func (cli *BaseClient) systemInfoRefreshing() bool {
return atomic.LoadUint32(&cli.SystemInfoRefreshing) == 1
}
+
+func maskRequestData(data map[string]any) map[string]any {
+ sensitiveKey := []string{"user", "password", "iqn", "tgt", "tgtname", "initiatorname"}
+
+ maskedData := make(map[string]any)
+ for k, v := range data {
+ if slices.Contains(sensitiveKey, k) {
+ maskedData[k] = "***"
+ } else {
+ maskedData[k] = v
+ }
+ }
+
+ return maskedData
+}
diff --git a/storage/oceanstor/client/client_dtree.go b/storage/oceanstor/client/client_dtree.go
index 6a1ed629..b82a54c9 100644
--- a/storage/oceanstor/client/client_dtree.go
+++ b/storage/oceanstor/client/client_dtree.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -62,7 +62,8 @@ func (cli *BaseClient) CreateDTree(ctx context.Context, params map[string]interf
}
// GetDTreeByName use for get dTree information
-func (cli *BaseClient) GetDTreeByName(ctx context.Context, parentID, parentName, vStoreID, name string) (map[string]interface{}, error) {
+func (cli *BaseClient) GetDTreeByName(ctx context.Context,
+ parentID, parentName, vStoreID, name string) (map[string]interface{}, error) {
url := fmt.Sprintf("/QUOTATREE?PARENTNAME=%s&NAME=%s&vstoreId=%s", parentName, name, vStoreID)
resp, err := cli.Get(ctx, url, nil)
diff --git a/storage/oceanstor/client/client_filesystem.go b/storage/oceanstor/client/client_filesystem.go
index e3481f0e..4c2d0786 100644
--- a/storage/oceanstor/client/client_filesystem.go
+++ b/storage/oceanstor/client/client_filesystem.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -23,6 +23,7 @@ import (
"strconv"
"time"
+ "huawei-csi-driver/pkg/constants"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
)
@@ -37,6 +38,7 @@ const (
msgTimeOut int64 = 1077949001
exceedFSCapacityUpper int64 = 1073844377
lessFSCapacityLower int64 = 1073844376
+ queryNfsSharePerPage int64 = 100
)
const (
@@ -59,7 +61,7 @@ type Filesystem interface {
// GetNfsShareAccessCount used for get nfs share access count by id
GetNfsShareAccessCount(ctx context.Context, parentID, vStoreID string) (int64, error)
// GetNfsShareAccessRange used for get nfs share access
- GetNfsShareAccessRange(ctx context.Context, parentID, vStoreID string, startRange, endRange int64) ([]interface{}, error)
+ GetNfsShareAccessRange(ctx context.Context, parentID, vStoreID string, startRange, endRange int64) ([]any, error)
// CreateFileSystem used for create file system
CreateFileSystem(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error)
// UpdateFileSystem used for update file system
@@ -223,8 +225,8 @@ func (cli *BaseClient) GetNfsShareAccess(ctx context.Context,
}
var i int64
- for i = 0; i < count; i += 100 { // Query per page 100
- clients, err := cli.GetNfsShareAccessRange(ctx, parentID, vStoreID, i, i+100)
+ for i = 0; i < count; i += queryNfsSharePerPage { // Query per page 100
+ clients, err := cli.GetNfsShareAccessRange(ctx, parentID, vStoreID, i, i+queryNfsSharePerPage)
if err != nil {
return nil, err
}
@@ -273,7 +275,7 @@ func (cli *BaseClient) GetNfsShareAccessCount(ctx context.Context, parentID, vSt
if !ok {
return 0, errors.New("convert respData[\"COUNT\"] to string failed")
}
- count := utils.ParseIntWithDefault(countStr, 10, 64, 0)
+ count := utils.ParseIntWithDefault(countStr, constants.DefaultIntBase, constants.DefaultIntBitSize, 0)
return count, nil
}
diff --git a/storage/oceanstor/client/client_filesystem_test.go b/storage/oceanstor/client/client_filesystem_test.go
index 2a1b0267..86062782 100644
--- a/storage/oceanstor/client/client_filesystem_test.go
+++ b/storage/oceanstor/client/client_filesystem_test.go
@@ -19,28 +19,24 @@ package client
import (
"context"
"errors"
- "reflect"
"testing"
- "bou.ke/monkey"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/agiledragon/gomonkey/v2"
+ "github.com/stretchr/testify/require"
)
func TestAllowNfsShareAccess(t *testing.T) {
- convey.Convey("Normal", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
- func(_ *BaseClient, _ context.Context, _ string, _ map[string]interface{}) (Response, error) {
- return Response{
- Data: map[string]interface{}{
- "ID": "5",
- },
- Error: map[string]interface{}{
- "code": float64(0),
- "description": "0",
- },
- }, nil
- })
- defer guard.Unpatch()
+ t.Run("Normal", func(t *testing.T) {
+ p := gomonkey.ApplyMethodReturn(testClient, "Post", Response{
+ Data: map[string]interface{}{
+ "ID": "5",
+ },
+ Error: map[string]interface{}{
+ "code": float64(0),
+ "description": "0",
+ },
+ }, nil)
+ defer p.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
Name: "test",
@@ -51,11 +47,11 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
VStoreID: "0",
})
- convey.So(err, convey.ShouldBeNil)
+ require.NoError(t, err)
})
- convey.Convey("Error code is not zero", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
+ t.Run("Error code is not zero", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(testClient, "Post",
func(_ *BaseClient, _ context.Context, _ string, _ map[string]interface{}) (Response, error) {
return Response{
Data: map[string]interface{}{
@@ -67,7 +63,7 @@ func TestAllowNfsShareAccess(t *testing.T) {
},
}, nil
})
- defer guard.Unpatch()
+ defer p.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
Name: "test",
@@ -78,15 +74,15 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
VStoreID: "0",
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
- convey.Convey("Post quest return error", t, func() {
- guard := monkey.PatchInstanceMethod(reflect.TypeOf(testClient), "Post",
+ t.Run("Post quest return error", func(t *testing.T) {
+ p := gomonkey.ApplyMethod(testClient, "Post",
func(_ *BaseClient, _ context.Context, _ string, _ map[string]interface{}) (Response, error) {
return Response{}, errors.New("mock err")
})
- defer guard.Unpatch()
+ defer p.Reset()
err := testClient.AllowNfsShareAccess(context.TODO(), &AllowNfsShareAccessRequest{
Name: "test",
@@ -97,6 +93,6 @@ func TestAllowNfsShareAccess(t *testing.T) {
RootSquash: 1,
VStoreID: "0",
})
- convey.So(err, convey.ShouldBeError)
+ require.Error(t, err)
})
}
diff --git a/storage/oceanstor/client/client_host.go b/storage/oceanstor/client/client_host.go
index 2adcc6b7..4cc96fd4 100644
--- a/storage/oceanstor/client/client_host.go
+++ b/storage/oceanstor/client/client_host.go
@@ -32,6 +32,19 @@ const (
hostGroupNotExist int64 = 1077937500
)
+const (
+ // AssociateObjTypeMapping mapping type
+ AssociateObjTypeMapping = 245
+ // AssociateObjTypeHost host type
+ AssociateObjTypeHost = 21
+ // AssociateObjTypeHostGroup host group type
+ AssociateObjTypeHostGroup = 14
+ // AssociateObjTypeLUN LUN type
+ AssociateObjTypeLUN = 11
+ // AssociateObjTypeLUNGroup LUN group type
+ AssociateObjTypeLUNGroup = 256
+)
+
// Host defines interfaces for host operations
type Host interface {
// QueryAssociateHostGroup used for query associate host group
diff --git a/storage/oceanstor/client/client_hypermetro.go b/storage/oceanstor/client/client_hypermetro.go
index cc8e6a5f..bea428d6 100644
--- a/storage/oceanstor/client/client_hypermetro.go
+++ b/storage/oceanstor/client/client_hypermetro.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -28,6 +28,17 @@ const (
hyperMetroNotExist int64 = 1077674242
)
+const (
+ // MetroPairSyncSpeedLow is low synchronization rate of the HyperMetro pair.
+ MetroPairSyncSpeedLow = iota + 1
+ // MetroPairSyncSpeedMedium is medium synchronization rate of the HyperMetro pair.
+ MetroPairSyncSpeedMedium
+ // MetroPairSyncSpeedHigh is high synchronization rate of the HyperMetro pair.
+ MetroPairSyncSpeedHigh
+ // MetroPairSyncSpeedHighest is the highest synchronization rate of the HyperMetro pair.
+ MetroPairSyncSpeedHighest
+)
+
// HyperMetro defines interfaces for hyper metro operations
type HyperMetro interface {
// GetHyperMetroDomainByName used for get hyper metro domain by name
@@ -176,7 +187,7 @@ func (cli *BaseClient) GetHyperMetroPair(ctx context.Context, pairID string) (ma
}
// GetHyperMetroPairByLocalObjID used for get hyper metro pair by local object id
-func (cli *BaseClient) GetHyperMetroPairByLocalObjID(ctx context.Context, objID string) (map[string]interface{}, error) {
+func (cli *BaseClient) GetHyperMetroPairByLocalObjID(ctx context.Context, objID string) (map[string]any, error) {
url := fmt.Sprintf("/HyperMetroPair?filter=LOCALOBJID::%s", objID)
resp, err := cli.Get(ctx, url, nil)
diff --git a/storage/oceanstor/client/client_lun.go b/storage/oceanstor/client/client_lun.go
index 6d6b13ab..dab00df0 100644
--- a/storage/oceanstor/client/client_lun.go
+++ b/storage/oceanstor/client/client_lun.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -23,6 +23,7 @@ import (
"fmt"
"strconv"
+ "huawei-csi-driver/pkg/constants"
pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/utils"
"huawei-csi-driver/utils/log"
@@ -34,6 +35,8 @@ const (
lunAlreadyInGroup int64 = 1077936862
lunNotExist int64 = 1077936859
parameterIncorrect int64 = 50331651
+
+ maxLunNameLength = 31
)
// Lun defines interfaces for lun operations
@@ -130,10 +133,10 @@ func (cli *BaseClient) GetLunByName(ctx context.Context, name string) (map[strin
// MakeLunName v3/v5 storage support 1 to 31 characters
func (cli *BaseClient) MakeLunName(name string) string {
- if len(name) <= 31 {
+ if len(name) <= maxLunNameLength {
return name
}
- return name[:31]
+ return name[:maxLunNameLength]
}
// GetLunByID used for get lun by id
@@ -394,7 +397,7 @@ func (cli *BaseClient) GetLunCountOfMapping(ctx context.Context, mappingID strin
return 0, pkgUtils.Errorf(ctx, "convert countStr to string failed, data: %v", respData["COUNT"])
}
- count := utils.ParseIntWithDefault(countStr, 10, 64, 0)
+ count := utils.ParseIntWithDefault(countStr, constants.DefaultIntBase, constants.DefaultIntBitSize, 0)
return count, nil
}
@@ -421,7 +424,7 @@ func (cli *BaseClient) GetLunCountOfHost(ctx context.Context, hostID string) (in
if !ok {
return 0, pkgUtils.Errorf(ctx, "convert countStr to string failed, data: %v", respData["COUNT"])
}
- count := utils.ParseIntWithDefault(countStr, 10, 64, 0)
+ count := utils.ParseIntWithDefault(countStr, constants.DefaultIntBase, constants.DefaultIntBitSize, 0)
return count, nil
}
@@ -460,7 +463,7 @@ func (cli *BaseClient) GetHostLunId(ctx context.Context, hostID, lunID string) (
}
hostLunIdFloat, ok := associateData["HostLUNID"].(float64)
if ok {
- hostLunId = strconv.FormatInt(int64(hostLunIdFloat), 10)
+ hostLunId = strconv.FormatInt(int64(hostLunIdFloat), constants.DefaultIntBase)
break
}
}
diff --git a/storage/oceanstor/client/client_qos.go b/storage/oceanstor/client/client_qos.go
index adbc2491..a17f10ee 100644
--- a/storage/oceanstor/client/client_qos.go
+++ b/storage/oceanstor/client/client_qos.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. 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.
@@ -38,7 +38,7 @@ type Qos interface {
// DeleteQos used for delete qos
DeleteQos(ctx context.Context, qosID, vStoreID string) error
// CreateQos used for create qos
- CreateQos(ctx context.Context, name, objID, objType, vStoreID string, params map[string]int) (map[string]interface{}, error)
+ CreateQos(ctx context.Context, args CreateQoSArgs) (map[string]any, error)
// UpdateQos used for update qos
UpdateQos(ctx context.Context, qosID, vStoreID string, params map[string]interface{}) error
// ActivateQos used for active qos
@@ -47,9 +47,17 @@ type Qos interface {
DeactivateQos(ctx context.Context, qosID, vStoreID string) error
}
+// CreateQoSArgs is the arguments to create QoS
+type CreateQoSArgs struct {
+ Name string
+ ObjID string
+ ObjType string
+ VStoreID string
+ Params map[string]int
+}
+
// CreateQos used for create qos
-func (cli *BaseClient) CreateQos(ctx context.Context, name, objID, objType, vStoreID string, params map[string]int) (
- map[string]interface{}, error) {
+func (cli *BaseClient) CreateQos(ctx context.Context, args CreateQoSArgs) (map[string]any, error) {
utcTime, err := cli.getSystemUTCTime(ctx)
if err != nil {
@@ -63,24 +71,24 @@ func (cli *BaseClient) CreateQos(ctx context.Context, name, objID, objType, vSto
}
data := map[string]interface{}{
- "NAME": name,
+ "NAME": args.Name,
"SCHEDULEPOLICY": 1,
"SCHEDULESTARTTIME": utcZeroTime.Unix(),
"STARTTIME": "00:00",
"DURATION": 86400,
}
- if objType == "fs" {
- data["FSLIST"] = []string{objID}
+ if args.ObjType == "fs" {
+ data["FSLIST"] = []string{args.ObjID}
} else {
- data["LUNLIST"] = []string{objID}
+ data["LUNLIST"] = []string{args.ObjID}
}
- if vStoreID != "" {
- data["vstoreId"] = vStoreID
+ if args.VStoreID != "" {
+ data["vstoreId"] = args.VStoreID
}
- for k, v := range params {
+ for k, v := range args.Params {
data[k] = v
}
@@ -91,8 +99,8 @@ func (cli *BaseClient) CreateQos(ctx context.Context, name, objID, objType, vSto
code := int64(resp.Error["code"].(float64))
if code == smartQosAlreadyExist {
- log.AddContext(ctx).Warningf("The QoS %s is already exist.", name)
- return cli.GetQosByName(ctx, name, vStoreID)
+ log.AddContext(ctx).Warningf("The QoS %s is already exist.", args.Name)
+ return cli.GetQosByName(ctx, args.Name, args.VStoreID)
} else if code != 0 {
return nil, fmt.Errorf("Create qos %v error: %d", data, code)
}
diff --git a/storage/oceanstor/client/client_quota.go b/storage/oceanstor/client/client_quota.go
index 579684e2..1714f901 100644
--- a/storage/oceanstor/client/client_quota.go
+++ b/storage/oceanstor/client/client_quota.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -86,7 +86,8 @@ func (cli *BaseClient) UpdateQuota(ctx context.Context, quotaID string, params m
}
// GetQuota gets quota info by id
-func (cli *BaseClient) GetQuota(ctx context.Context, quotaID, vStoreID string, spaceUnitType uint32) (map[string]interface{}, error) {
+func (cli *BaseClient) GetQuota(ctx context.Context,
+ quotaID, vStoreID string, spaceUnitType uint32) (map[string]interface{}, error) {
resp, err := cli.Get(ctx, fmt.Sprintf("/FS_QUOTA/%v", quotaID), map[string]interface{}{
"SPACEUNITTYPE": spaceUnitType,
"vstoreId": vStoreID,
diff --git a/storage/oceanstor/client/client_test.go b/storage/oceanstor/client/client_test.go
index b0362058..1c87baef 100644
--- a/storage/oceanstor/client/client_test.go
+++ b/storage/oceanstor/client/client_test.go
@@ -20,6 +20,7 @@ import (
"bytes"
"context"
"errors"
+ "io"
"io/ioutil"
"net/http"
"reflect"
@@ -78,7 +79,7 @@ func TestLogin(t *testing.T) {
defer m.Reset()
for _, s := range cases {
- g := gomonkey.ApplyMethod(reflect.TypeOf(testClient.Client), "Do", func(_ *http.Client, req *http.Request) (*http.Response, error) {
+ g := gomonkey.ApplyMethod(testClient.Client, "Do", func(_ *http.Client, req *http.Request) (*http.Response, error) {
r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
return &http.Response{
StatusCode: 200,
@@ -250,23 +251,25 @@ func TestGetLunByName(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.GetLunByName(context.TODO(), "zfy")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.GetLunByName(context.TODO(), "zfy")
+ assert.Equal(t, s.wantErr, err != nil, "%v", err)
+ })
}
}
@@ -346,23 +349,25 @@ func TestGetLunByID(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.GetLunByID(context.TODO(), "0")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.GetLunByID(context.TODO(), "0")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -396,23 +401,25 @@ func TestAddLunToGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- err := testClient.AddLunToGroup(context.TODO(), "", "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ err := testClient.AddLunToGroup(context.TODO(), "", "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -441,23 +448,25 @@ func TestRemoveLunFromGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- err := testClient.RemoveLunFromGroup(context.TODO(), "", "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ err := testClient.RemoveLunFromGroup(context.TODO(), "", "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -490,23 +499,25 @@ func TestGetLunGroupByName(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.GetLunGroupByName(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.GetLunGroupByName(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -536,23 +547,25 @@ func TestCreateLunGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.CreateLunGroup(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.CreateLunGroup(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -581,23 +594,25 @@ func TestDeleteLunGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- err := testClient.DeleteLunGroup(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ err := testClient.DeleteLunGroup(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -628,23 +643,25 @@ func TestQueryAssociateLunGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.QueryAssociateLunGroup(context.TODO(), 245, "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.QueryAssociateLunGroup(context.TODO(), 245, "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -673,23 +690,25 @@ func TestDeleteLun(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- err := testClient.DeleteLun(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ err := testClient.DeleteLun(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -737,23 +756,25 @@ func TestGetPoolByName(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.GetPoolByName(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.GetPoolByName(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -801,23 +822,25 @@ func TestGetAllPools(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.GetAllPools(context.TODO())
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.GetAllPools(context.TODO())
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -851,23 +874,25 @@ func TestCreateHost(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.CreateHost(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.CreateHost(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -906,23 +931,25 @@ func TestGetHostByName(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.GetHostByName(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.GetHostByName(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -951,23 +978,25 @@ func TestDeleteHost(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- err := testClient.DeleteHost(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ err := testClient.DeleteHost(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -999,23 +1028,25 @@ func TestCreateHostGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).AnyTimes()
+ t.Run(s.Name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.ResponseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).AnyTimes()
- _, err := testClient.CreateHostGroup(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.Name, err)
+ _, err := testClient.CreateHostGroup(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -1059,23 +1090,25 @@ func TestGetHostGroupByName(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.responseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).Times(1)
+ t.Run(s.name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.responseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).Times(1)
- _, err := testClient.GetHostGroupByName(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.name, err)
+ _, err := testClient.GetHostGroupByName(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -1114,23 +1147,25 @@ func TestDeleteHostGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.responseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, nil
- }).Times(1)
+ t.Run(s.name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.responseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, nil
+ }).Times(1)
- err := testClient.DeleteHostGroup(context.TODO(), "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.name, err)
+ err := testClient.DeleteHostGroup(context.TODO(), "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -1343,22 +1378,24 @@ func TestAddHostToGroup(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockClient := NewMockHTTPClient(ctrl)
temp := testClient.Client
defer func() { testClient.Client = temp }()
- testClient.Client = mockClient
for _, s := range cases {
- mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.responseBody)))
- return &http.Response{
- StatusCode: int(successStatus),
- Body: r,
- }, s.err
- }).Times(1)
- err := testClient.AddHostToGroup(context.TODO(), "", "")
- assert.Equal(t, s.wantErr, err != nil, "%s, err:%v", s.name, err)
+ t.Run(s.name, func(t *testing.T) {
+ mockClient := NewMockHTTPClient(ctrl)
+ testClient.Client = mockClient
+ mockClient.EXPECT().Do(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) {
+ r := io.NopCloser(bytes.NewReader([]byte(s.responseBody)))
+ return &http.Response{
+ StatusCode: int(successStatus),
+ Body: r,
+ }, s.err
+ }).Times(1)
+ err := testClient.AddHostToGroup(context.TODO(), "", "")
+ assert.Equal(t, s.wantErr, err != nil, "err:%v", err)
+ })
}
}
@@ -1404,7 +1441,7 @@ func TestRemoveHostFromGroup(t *testing.T) {
for _, s := range cases {
d := gomonkey.ApplyMethod(reflect.TypeOf(testClient.Client), "Do",
func(*http.Client, *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(s.responseBody)))
+ r := io.NopCloser(bytes.NewReader([]byte(s.responseBody)))
return &http.Response{
StatusCode: 200,
Body: r,
diff --git a/storage/oceanstor/clientv6/clientv6.go b/storage/oceanstor/clientv6/clientv6.go
index f9f2361f..e650857a 100644
--- a/storage/oceanstor/clientv6/clientv6.go
+++ b/storage/oceanstor/clientv6/clientv6.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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,13 +26,13 @@ import (
"huawei-csi-driver/utils/log"
)
-// ClientV6 provides base client of clientv6
-type ClientV6 struct {
+// V6Client provides base client of clientv6
+type V6Client struct {
client.BaseClient
}
// NewClientV6 inits a new client of clientv6
-func NewClientV6(ctx context.Context, param *client.NewClientConfig) (*ClientV6, error) {
+func NewClientV6(ctx context.Context, param *client.NewClientConfig) (*V6Client, error) {
var err error
var parallelCount int
@@ -48,20 +48,19 @@ func NewClientV6(ctx context.Context, param *client.NewClientConfig) (*ClientV6,
}
log.AddContext(ctx).Infof("Init parallel count is %d", parallelCount)
- client.ClientSemaphore = utils.NewSemaphore(parallelCount)
+ client.RequestSemaphore = utils.NewSemaphore(parallelCount)
cli, err := client.NewClient(ctx, param)
if err != nil {
return nil, err
}
- return &ClientV6{
- *cli,
- }, nil
+ return &V6Client{BaseClient: *cli}, nil
}
// SplitCloneFS used to split clone for dorado or oceantor v6
-func (cli *ClientV6) SplitCloneFS(ctx context.Context, fsID, vStoreId string, splitSpeed int, deleteParentSnapshot bool) error {
+func (cli *V6Client) SplitCloneFS(ctx context.Context,
+ fsID, vStoreId string, splitSpeed int, deleteParentSnapshot bool) error {
data := map[string]interface{}{
"ID": fsID,
"SPLITSPEED": splitSpeed,
@@ -84,6 +83,6 @@ func (cli *ClientV6) SplitCloneFS(ctx context.Context, fsID, vStoreId string, sp
}
// MakeLunName v6 storage lun name support 1 to 255 characters
-func (cli *ClientV6) MakeLunName(name string) string {
+func (cli *V6Client) MakeLunName(name string) string {
return name
}
diff --git a/storage/oceanstor/clientv6/clientv6_test.go b/storage/oceanstor/clientv6/clientv6_test.go
index 8a6ed8a7..d18961a4 100644
--- a/storage/oceanstor/clientv6/clientv6_test.go
+++ b/storage/oceanstor/clientv6/clientv6_test.go
@@ -30,7 +30,7 @@ import (
"huawei-csi-driver/utils/log"
)
-var testClient *ClientV6
+var testClient *V6Client
const (
logName = "clientV6_test.log"
diff --git a/storage/oceanstor/smartx/smartx.go b/storage/oceanstor/smartx/smartx.go
index 76f340b1..256e23d8 100644
--- a/storage/oceanstor/smartx/smartx.go
+++ b/storage/oceanstor/smartx/smartx.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -32,6 +32,17 @@ import (
"huawei-csi-driver/utils/log"
)
+const (
+ kilo = 1000
+ minLatency, maxLatency = 500, 1500
+ minIops, maxIops = 99, 999999999
+ minBandwidth, maxBandwidth = 0, 999999999
+ validIOType0 = 0
+ validIOType1 = 1
+ validIOType2 = 2
+ ioType = 2
+)
+
type qosParameterValidators map[string]func(int) bool
type qosParameterList map[string]struct{}
@@ -45,19 +56,19 @@ var (
doradoParameterValidators = map[string]func(int) bool{
"IOTYPE": func(value int) bool {
- return value == 2
+ return value == validIOType2
},
"MAXBANDWIDTH": func(value int) bool {
return value > 0
},
"MAXIOPS": func(value int) bool {
- return value > 99
+ return value > minIops
},
}
oceanStorV3V5ParameterValidators = map[string]func(int) bool{
"IOTYPE": func(value int) bool {
- return value == 0 || value == 1 || value == 2
+ return value == validIOType0 || value == validIOType1 || value == validIOType2
},
"MAXBANDWIDTH": func(value int) bool {
return value > 0
@@ -78,28 +89,28 @@ var (
doradoV6ParameterValidators = map[string]func(int) bool{
"IOTYPE": func(value int) bool {
- return value == 2
+ return value == ioType
},
"MAXBANDWIDTH": func(value int) bool {
- return value > 0 && value <= 999999999
+ return value > minBandwidth && value <= maxBandwidth
},
"MINBANDWIDTH": func(value int) bool {
- return value > 0 && value <= 999999999
+ return value > minBandwidth && value <= maxBandwidth
},
"MAXIOPS": func(value int) bool {
- return value > 99 && value <= 999999999
+ return value > minIops && value <= maxIops
},
"MINIOPS": func(value int) bool {
- return value > 99 && value <= 999999999
+ return value > minIops && value <= maxIops
},
"LATENCY": func(value int) bool {
// User request Latency values in millisecond but during extraction values are converted in microsecond
// as required in OceanStor DoradoV6 QoS create interface
- return value == 500 || value == 1500
+ return value == minLatency || value == maxLatency
},
}
@@ -194,7 +205,7 @@ func ExtractQoSParameters(ctx context.Context, product string, qosConfig string)
if product == constants.OceanStorDoradoV6 && key == "LATENCY" {
// convert OceanStoreDoradoV6 Latency from millisecond to microsecond
- params[key] = value * 1000
+ params[key] = value * kilo
continue
}
@@ -237,25 +248,25 @@ func ValidateQoSParameters(product string, qosParam map[string]float64) (map[str
return validatedParameters, nil
}
-// SmartX provides smartx client
-type SmartX struct {
+// Client provides smartx client
+type Client struct {
cli client.BaseClientInterface
}
// NewSmartX inits a new smartx client
-func NewSmartX(cli client.BaseClientInterface) *SmartX {
- return &SmartX{
+func NewSmartX(cli client.BaseClientInterface) *Client {
+ return &Client{
cli: cli,
}
}
-func (p *SmartX) getQosName(objID, objType string) string {
+func (p *Client) getQosName(objID, objType string) string {
now := time.Now().Format("20060102150405")
return fmt.Sprintf("k8s_%s%s_%s", objType, objID, now)
}
// CreateQos creates qos and return its id
-func (p *SmartX) CreateQos(ctx context.Context,
+func (p *Client) CreateQos(ctx context.Context,
objID, objType, vStoreID string,
params map[string]int) (string, error) {
var err error
@@ -285,7 +296,7 @@ func (p *SmartX) CreateQos(ctx context.Context,
}
name := p.getQosName(objID, objType)
- qos, err := p.cli.CreateQos(ctx, name, objID, objType, vStoreID, params)
+ qos, err := p.cli.CreateQos(ctx, p.getCreateQosArgs(name, objID, objType, vStoreID, params))
if err != nil {
log.AddContext(ctx).Errorf("Create qos %v for obj %s of type %s error: %v",
params, objID, objType, err)
@@ -314,7 +325,7 @@ func (p *SmartX) CreateQos(ctx context.Context,
}
// DeleteQos deletes qos by id
-func (p *SmartX) DeleteQos(ctx context.Context, qosID, objID, objType, vStoreID string) error {
+func (p *Client) DeleteQos(ctx context.Context, qosID, objID, objType, vStoreID string) error {
qos, err := p.cli.GetQosByID(ctx, qosID, vStoreID)
if err != nil {
log.AddContext(ctx).Errorf("Get qos by ID %s error: %v", qosID, err)
@@ -377,7 +388,7 @@ func (p *SmartX) DeleteQos(ctx context.Context, qosID, objID, objType, vStoreID
}
// CreateLunSnapshot creates lun snapshot
-func (p *SmartX) CreateLunSnapshot(ctx context.Context, name, srcLunID string) (map[string]interface{}, error) {
+func (p *Client) CreateLunSnapshot(ctx context.Context, name, srcLunID string) (map[string]interface{}, error) {
snapshot, err := p.cli.CreateLunSnapshot(ctx, name, srcLunID)
if err != nil {
log.AddContext(ctx).Errorf("Create snapshot %s for lun %s error: %v", name, srcLunID, err)
@@ -399,7 +410,7 @@ func (p *SmartX) CreateLunSnapshot(ctx context.Context, name, srcLunID string) (
}
// DeleteLunSnapshot deletes lun snapshot by id
-func (p *SmartX) DeleteLunSnapshot(ctx context.Context, snapshotID string) error {
+func (p *Client) DeleteLunSnapshot(ctx context.Context, snapshotID string) error {
err := p.cli.DeactivateLunSnapshot(ctx, snapshotID)
if err != nil {
log.AddContext(ctx).Errorf("Deactivate snapshot %s error: %v", snapshotID, err)
@@ -416,7 +427,7 @@ func (p *SmartX) DeleteLunSnapshot(ctx context.Context, snapshotID string) error
}
// CreateFSSnapshot creates fs snapshot
-func (p *SmartX) CreateFSSnapshot(ctx context.Context, name, srcFSID string) (string, error) {
+func (p *Client) CreateFSSnapshot(ctx context.Context, name, srcFSID string) (string, error) {
snapshot, err := p.cli.CreateFSSnapshot(ctx, name, srcFSID)
if err != nil {
log.AddContext(ctx).Errorf("Create snapshot %s for FS %s error: %v", name, srcFSID, err)
@@ -431,7 +442,7 @@ func (p *SmartX) CreateFSSnapshot(ctx context.Context, name, srcFSID string) (st
}
// DeleteFSSnapshot deletes fs snapshot by id
-func (p *SmartX) DeleteFSSnapshot(ctx context.Context, snapshotID string) error {
+func (p *Client) DeleteFSSnapshot(ctx context.Context, snapshotID string) error {
err := p.cli.DeleteFSSnapshot(ctx, snapshotID)
if err != nil {
@@ -441,3 +452,13 @@ func (p *SmartX) DeleteFSSnapshot(ctx context.Context, snapshotID string) error
return nil
}
+
+func (p *Client) getCreateQosArgs(name, objID, objType, vStoreID string, params map[string]int) client.CreateQoSArgs {
+ return client.CreateQoSArgs{
+ Name: name,
+ ObjID: objID,
+ ObjType: objType,
+ VStoreID: vStoreID,
+ Params: params,
+ }
+}
diff --git a/storage/oceanstor/volume/base.go b/storage/oceanstor/volume/base.go
index 40d924d0..f380be51 100644
--- a/storage/oceanstor/volume/base.go
+++ b/storage/oceanstor/volume/base.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -22,6 +22,7 @@ import (
"fmt"
"strconv"
+ "huawei-csi-driver/pkg/constants"
pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/storage/oceanstor/smartx"
@@ -42,6 +43,7 @@ func (p *Base) commonPreModify(ctx context.Context, params map[string]interface{
p.getAllocType,
p.getQoS,
p.getFileMode,
+ p.getMetroPairSyncSpeed,
}
for _, analyzer := range analyzers {
@@ -61,6 +63,7 @@ func (p *Base) commonPreCreate(ctx context.Context, params map[string]interface{
p.getPoolID,
p.getQoS,
p.getFileMode,
+ p.getMetroPairSyncSpeed,
}
for _, analyzer := range analyzers {
@@ -93,16 +96,17 @@ func (p *Base) getCloneSpeed(_ context.Context, params map[string]interface{}) e
if v, exist := params["clonespeed"].(string); exist && v != "" {
speed, err := strconv.Atoi(v)
- if err != nil || speed < 1 || speed > 4 {
+ if err != nil || speed < constants.CloneSpeedLevel1 || speed > constants.CloneSpeedLevel4 {
return fmt.Errorf("error config %s for clonespeed", v)
}
params["clonespeed"] = speed
} else {
- params["clonespeed"] = 3
+ params["clonespeed"] = constants.CloneSpeedLevel3
}
return nil
}
+
func (p *Base) getFileMode(_ context.Context, params map[string]interface{}) error {
if params == nil || len(params) == 0 {
return nil
@@ -196,8 +200,9 @@ func (p *Base) preExpandCheckCapacity(ctx context.Context,
}
func (p *Base) getSnapshotReturnInfo(snapshot map[string]interface{}, snapshotSize int64) map[string]interface{} {
- snapshotCreated := utils.ParseIntWithDefault(snapshot["TIMESTAMP"].(string), 10, 64, 0)
- snapshotSizeBytes := snapshotSize * 512
+ snapshotCreated := utils.ParseIntWithDefault(snapshot["TIMESTAMP"].(string),
+ constants.DefaultIntBase, constants.DefaultIntBitSize, 0)
+ snapshotSizeBytes := snapshotSize * constants.AllocationUnitBytes
return map[string]interface{}{
"CreationTime": snapshotCreated,
"SizeBytes": snapshotSizeBytes,
@@ -268,3 +273,24 @@ func (p *Base) prepareVolObj(ctx context.Context, params, res map[string]interfa
}
return volObj
}
+
+func (p *Base) getMetroPairSyncSpeed(_ context.Context, params map[string]interface{}) error {
+ if params == nil {
+ return nil
+ }
+
+ hyper, exist := params["hypermetro"].(bool)
+ if !exist || !hyper {
+ return nil
+ }
+
+ if v, exist := params["metropairsyncspeed"].(string); exist && v != "" {
+ speed, err := strconv.Atoi(v)
+ if err != nil || speed < client.MetroPairSyncSpeedLow || speed > client.MetroPairSyncSpeedHighest {
+ return fmt.Errorf("error config %s for metroPairSyncSpeed", v)
+ }
+ params["metropairsyncspeed"] = speed
+ }
+
+ return nil
+}
diff --git a/storage/oceanstor/volume/creator/base.go b/storage/oceanstor/volume/creator/base.go
index ad35f797..34320048 100644
--- a/storage/oceanstor/volume/creator/base.go
+++ b/storage/oceanstor/volume/creator/base.go
@@ -26,8 +26,8 @@ import (
"huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/storage/oceanstor/smartx"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
)
const (
@@ -38,7 +38,7 @@ const (
// BaseCreator provides some common methods for volume creation.
type BaseCreator struct {
cli client.BaseClientInterface
- transaction *taskflow.Transaction
+ transaction *flow.Transaction
// common fields of the filesystem
vStoreId string
@@ -67,11 +67,12 @@ type BaseCreator struct {
domainId string
isPairOnlineDelete bool
isSyncPair bool
+ metroPairSyncSpeed int
}
// Init initiates fields of BaseCreator
func (c *BaseCreator) Init(params *Parameter) {
- c.transaction = taskflow.NewTransaction()
+ c.transaction = flow.NewTransaction()
c.fsName = params.PvcName()
c.storagePoolName = params.StoragePool()
c.storagePoolId = params.PoolID()
@@ -87,6 +88,7 @@ func (c *BaseCreator) Init(params *Parameter) {
c.accessKrb5p = params.AccessKrb5p()
c.domainId = params.MetroDomainID()
c.vStorePairId = params.VStorePairId()
+ c.metroPairSyncSpeed = params.SyncMetroPairSpeed()
if !params.IsSkipNfsShareAndQos() {
c.isCreateNfsShare = true
@@ -401,12 +403,14 @@ func (c *BaseCreator) createHyperMetroPair(ctx context.Context,
"HCRESOURCETYPE": filesystemHCRESourceType,
"LOCALOBJID": activeFsId,
"REMOTEOBJID": standbyFsId,
- "SPEED": highestPairSpeed,
"VSTOREPAIRID": c.vStorePairId,
}
if c.domainId != "" {
req["DOMAINID"] = c.domainId
}
+ if c.metroPairSyncSpeed != 0 {
+ req["SPEED"] = c.metroPairSyncSpeed
+ }
pair, err := c.cli.CreateHyperMetroPair(ctx, req)
if err != nil {
return "", fmt.Errorf("create nas hypermetro pair error: %w", err)
diff --git a/storage/oceanstor/volume/creator/filesystem.go b/storage/oceanstor/volume/creator/filesystem.go
index c9c3a3ef..9f182006 100644
--- a/storage/oceanstor/volume/creator/filesystem.go
+++ b/storage/oceanstor/volume/creator/filesystem.go
@@ -59,6 +59,10 @@ func NewFsCreatorFromParams(cli client.BaseClientInterface,
creator.fileSystemMode = params.FilesystemMode()
}
+ if creator.fileSystemMode == client.HyperMetroFilesystemMode {
+ creator.vStoreId = creator.cli.GetvStoreID()
+ }
+
for _, opt := range opts {
opt(creator)
}
diff --git a/storage/oceanstor/volume/creator/hypermetro_fs.go b/storage/oceanstor/volume/creator/hypermetro_fs.go
index 030186f1..20510098 100644
--- a/storage/oceanstor/volume/creator/hypermetro_fs.go
+++ b/storage/oceanstor/volume/creator/hypermetro_fs.go
@@ -27,7 +27,6 @@ import (
const (
filesystemHCRESourceType = 2
- highestPairSpeed = 4
// hyper metro pair running status
hyperMetroPairRunningStatusNormal = "1"
diff --git a/storage/oceanstor/volume/creator/parameter.go b/storage/oceanstor/volume/creator/parameter.go
index dc29640e..346b9f6e 100644
--- a/storage/oceanstor/volume/creator/parameter.go
+++ b/storage/oceanstor/volume/creator/parameter.go
@@ -88,6 +88,8 @@ const (
SnapshotParentIdKey = "snapshotparentid"
// HyperMetroKey is the string of HyperMetro's key
HyperMetroKey = "hypermetro"
+ // MetroPairSyncSpeedKey is the string of MetroPairSyncSpeed's key
+ MetroPairSyncSpeedKey = "metropairsyncspeed"
// RemoteStoragePoolKey is the string of RemoteStoragePool's key
RemoteStoragePoolKey = "remotestoragepool"
// RemotePoolIdKey is the string of RemotePoolId's key
@@ -214,6 +216,11 @@ func (p *Parameter) VStorePairId() string { return getValueOrFallback(p.params,
// IsHyperMetro gets the HyperMetro value of the params map.
func (p *Parameter) IsHyperMetro() bool { return getValueOrFallback(p.params, HyperMetroKey, false) }
+// SyncMetroPairSpeed gets the SyncMetroPairSpeed value of the params map.
+func (p *Parameter) SyncMetroPairSpeed() int {
+ return getValueOrFallback(p.params, MetroPairSyncSpeedKey, 0)
+}
+
// IsReplication gets the Replication value of the params map.
func (p *Parameter) IsReplication() bool { return getValueOrFallback(p.params, ReplicationKey, false) }
diff --git a/storage/oceanstor/volume/dtree.go b/storage/oceanstor/volume/dtree.go
index 8c546146..b1866a47 100644
--- a/storage/oceanstor/volume/dtree.go
+++ b/storage/oceanstor/volume/dtree.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -24,8 +24,15 @@ import (
"huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
+)
+
+const (
+ accessKrb5ReadOnly = 0
+ accessKrb5ReadWrite = 1
+ accessKrb5None = 5
+ accessKrb5Default = -1
)
// DTree provides base DTree client
@@ -98,7 +105,7 @@ func (p *DTree) Create(ctx context.Context, params map[string]interface{}) (util
return nil, err
}
- taskFlow := taskflow.NewTaskFlow(ctx, "Create-FileSystem-DTree-Volume")
+ taskFlow := flow.NewTaskFlow(ctx, "Create-FileSystem-DTree-Volume")
taskFlow.AddTask("Check-FS", p.checkFSExist, nil)
taskFlow.AddTask("Create-DTree", p.createDtree, p.revertDtree)
taskFlow.AddTask("Create-Share", p.createShare, p.revertShare)
@@ -119,7 +126,7 @@ func (p *DTree) Create(ctx context.Context, params map[string]interface{}) (util
func (p *DTree) Delete(ctx context.Context, params map[string]interface{}) error {
var err error
- taskFlow := taskflow.NewTaskFlow(ctx, "Delete-FileSystem-DTree-Volume")
+ taskFlow := flow.NewTaskFlow(ctx, "Delete-FileSystem-DTree-Volume")
taskFlow.AddTask("Check-DTree", p.checkDtreeExist, nil)
taskFlow.AddTask("Delete-Quota", p.deleteQuota, nil)
taskFlow.AddTask("Delete-Share", p.deleteShare, nil)
@@ -135,8 +142,7 @@ func (p *DTree) Delete(ctx context.Context, params map[string]interface{}) error
}
// Expand expands volume size
-func (p *DTree) Expand(ctx context.Context, parentName, dTreeName, vstoreID string, spaceSoftQuota,
- spaceHardQuota int64) error {
+func (p *DTree) Expand(ctx context.Context, parentName, dTreeName, vstoreID string, spaceHardQuota int64) error {
dTreeID, err := p.getDtreeID(ctx, parentName, vstoreID, dTreeName)
if err != nil {
return err
@@ -308,7 +314,7 @@ func (p *DTree) deleteDtree(ctx context.Context, params,
return nil, err
}
-func (p *DTree) allowShareAccess(ctx context.Context, params, taskResult map[string]interface{}) (map[string]interface{}, error) {
+func (p *DTree) allowShareAccess(ctx context.Context, params, taskResult map[string]any) (map[string]any, error) {
shareID, _ := utils.ToStringWithFlag(taskResult["shareId"])
authClient, _ := utils.ToStringWithFlag(params["authclient"])
vStoreID, _ := utils.ToStringWithFlag(params["vstoreid"])
@@ -376,8 +382,8 @@ func (p *DTree) getCurrentShareAccess(ctx context.Context, shareID, vStoreID str
accesses := make(map[string]interface{})
var i int64 = 0
- for ; i < count; i += 100 { // Query per page 100
- clients, err := cli.GetNfsShareAccessRange(ctx, shareID, vStoreID, i, i+100)
+ for ; i < count; i += queryNfsSharePerPage {
+ clients, err := cli.GetNfsShareAccessRange(ctx, shareID, vStoreID, i, i+queryNfsSharePerPage)
if err != nil {
return nil, err
}
@@ -531,7 +537,7 @@ func (p *DTree) revertQuota(ctx context.Context, taskResult map[string]interface
return nil
}
-func (p *DTree) deleteQuota(ctx context.Context, params, taskResult map[string]interface{}) (map[string]interface{}, error) {
+func (p *DTree) deleteQuota(ctx context.Context, params, taskResult map[string]any) (map[string]any, error) {
req := map[string]interface{}{
"PARENTTYPE": client.ParentTypeDTree,
"PARENTID": taskResult["dTreeId"],
@@ -672,20 +678,21 @@ func (p *DTree) getDtreeID(ctx context.Context, parentName, vstoreID, dTreeName
func formatKerberosParam(data interface{}) int {
if data == nil {
- return -1
+ return accessKrb5Default
}
str, ok := data.(string)
if !ok {
- return -1
+ return accessKrb5Default
}
+
switch str {
case "read_only":
- return 0
+ return accessKrb5ReadOnly
case "read_write":
- return 1
+ return accessKrb5ReadWrite
case "none":
- return 5
+ return accessKrb5None
default:
- return -1
+ return accessKrb5Default
}
}
diff --git a/storage/oceanstor/volume/nas.go b/storage/oceanstor/volume/nas.go
index 18bb34b0..1eb875c4 100644
--- a/storage/oceanstor/volume/nas.go
+++ b/storage/oceanstor/volume/nas.go
@@ -31,8 +31,8 @@ import (
"huawei-csi-driver/storage/oceanstor/smartx"
"huawei-csi-driver/storage/oceanstor/volume/creator"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
)
const (
@@ -47,6 +47,8 @@ const (
noAllSquash = 1
rootSquash = 0
noRootSquash = 1
+
+ queryNfsSharePerPage int64 = 100
)
// ErrLogicPortFailOver indicates an error that logic port is fail over.
@@ -67,14 +69,6 @@ type NAS struct {
isRunningOnOwnSite bool
}
-type allowNfsShareAccessParam struct {
- shareID string
- authClient string
- vStoreID string
- activeClient client.BaseClientInterface
- accesses map[string]interface{}
-}
-
// NewNAS inits a new nas client
func NewNAS(cli, metroRemoteCli client.BaseClientInterface, product string,
nasHyperMetro NASHyperMetro, isRunningOnOwnSite bool) *NAS {
@@ -380,600 +374,6 @@ func (p *NAS) validateManageWorkLoadType(ctx context.Context, params, fs map[str
return nil
}
-func (p *NAS) createLocalFS(ctx context.Context, params, taskResult map[string]interface{}) (
- map[string]interface{}, error) {
-
- fsName, ok := params["name"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert fsName to string failed, data: %v", params["name"])
- }
- fs, err := p.cli.GetFileSystemByName(ctx, fsName)
- if err != nil {
- log.AddContext(ctx).Errorf("Get filesystem %s error: %v", fsName, err)
- return nil, err
- }
-
- var isClone bool
- if fs == nil {
- params["parentid"] = params["poolID"]
- params["vstoreId"] = params["localVStoreID"]
-
- if _, exist := params["clonefrom"]; exist {
- fs, err = p.clone(ctx, params)
- if err != nil {
- log.AddContext(ctx).Warningf("p.clone() failed, param:%+v", params)
- }
- isClone = true
- } else if _, exist := params["fromSnapshot"]; exist {
- fs, err = p.createFromSnapshot(ctx, params)
- if err != nil {
- log.AddContext(ctx).Warningf("p.createFromSnapshot() failed, param:%+v", params)
- }
- isClone = true
- } else {
- fs, err = p.cli.CreateFileSystem(ctx, params)
- if err != nil {
- log.AddContext(ctx).Warningf("CreateFileSystem() failed, param:%+v", params)
- }
- }
- } else {
- if fs["ISCLONEFS"].(string) != "false" {
- fsID, ok := fs["ID"].(string)
- if !ok {
- log.AddContext(ctx).Warningf("convert fsID to string failed, data: %v", fs["ID"])
- }
- err = p.waitFSSplitDone(ctx, fsID)
- }
- }
-
- if err != nil {
- log.AddContext(ctx).Errorf("Create filesystem %s error: %v", fsName, err)
- return nil, err
- }
-
- localFSID, ok := fs["ID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert localFSID to string failed, data: %v", fs["ID"])
- }
- if err = p.updateFileSystem(ctx, isClone, localFSID, params); err != nil {
- log.AddContext(ctx).Errorf("Update filesystem %s error: %v", fsName, err)
- return nil, err
- }
-
- return map[string]interface{}{
- "localFSID": localFSID,
- }, nil
-}
-
-func (p *NAS) updateFileSystem(ctx context.Context, isClone bool, objID string, params map[string]interface{}) error {
- if !isClone {
- return nil
- }
-
- log.AddContext(ctx).Infof("The fileSystem %s is cloned, now to update some fields.",
- params["name"].(string))
- data := make(map[string]interface{})
- if val, exist := params["reservedsnapshotspaceratio"].(int); exist {
- data["SNAPSHOTRESERVEPER"] = val
- }
-
- if val, exist := params["isshowsnapdir"].(bool); exist {
- data["ISSHOWSNAPDIR"] = val
- }
-
- if val, exist := params["description"].(string); exist {
- data["DESCRIPTION"] = val
- }
-
- if data == nil {
- log.AddContext(ctx).Infof("The fileSystem %s is cloned, but no field need to update.",
- params["name"].(string))
- return nil
- }
-
- // Only update the local FS, the remote FS is created separately, no need to update
- err := p.cli.UpdateFileSystem(ctx, objID, data)
- if err != nil {
- log.AddContext(ctx).Errorf("Update FileSystem %s field [%v], error: %v", objID, data, err)
- return err
- }
- return nil
-}
-
-func (p *NAS) clone(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
- clonefrom, ok := params["clonefrom"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert clonefrom to string failed, data: %v", params["clonefrom"])
- }
- cloneFromFS, err := p.cli.GetFileSystemByName(ctx, clonefrom)
- if err != nil {
- log.AddContext(ctx).Errorf("Get clone src filesystem %s error: %v", clonefrom, err)
- return nil, err
- }
- if cloneFromFS == nil {
- msg := fmt.Errorf("Filesystem %s does not exist", clonefrom)
- log.AddContext(ctx).Errorln(msg)
- return nil, msg
- }
-
- srcFSCapacity, err := strconv.ParseInt(cloneFromFS["CAPACITY"].(string), 10, 64)
- if err != nil {
- return nil, err
- }
-
- cloneFSCapacity, ok := params["capacity"].(int64)
- if !ok {
- log.AddContext(ctx).Warningf("convert cloneFSCapacity to int64 failed, data: %v", params["capacity"])
- }
- if cloneFSCapacity < srcFSCapacity {
- msg := fmt.Sprintf("Clone filesystem capacity must be >= src %s", clonefrom)
- log.AddContext(ctx).Errorln(msg)
- return nil, errors.New(msg)
- }
-
- cloneFilesystemReq := &CloneFilesystemRequest{
- FsName: params["name"].(string),
- ParentID: cloneFromFS["ID"].(string),
- ParentSnapshotID: "",
- AllocType: params["alloctype"].(int),
- CloneSpeed: params["clonespeed"].(int),
- CloneFsCapacity: cloneFSCapacity,
- SrcCapacity: srcFSCapacity,
- DeleteParentSnapshot: true,
- VStoreId: systemVStore,
- }
- cloneFS, err := p.cloneFilesystem(ctx, cloneFilesystemReq)
- if err != nil {
- log.AddContext(ctx).Errorf("Clone filesystem %s from source filesystem %s error: %s",
- cloneFilesystemReq.FsName, cloneFilesystemReq.ParentID, err)
- return nil, err
- }
-
- return cloneFS, nil
-}
-
-func (p *NAS) createFromSnapshot(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
- srcSnapshotName, ok := params["fromSnapshot"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert srcSnapshotName to string failed, data: %v", params["fromSnapshot"])
- }
- snapshotParentId, ok := params["snapshotparentid"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert snapshotParentId to string failed, data: %v", params["snapshotparentid"])
- }
- srcSnapshot, err := p.cli.GetFSSnapshotByName(ctx, snapshotParentId, srcSnapshotName)
- if err != nil {
- log.AddContext(ctx).Errorf("Get src filesystem snapshot %s error: %v", srcSnapshotName, err)
- return nil, err
- }
- if srcSnapshot == nil {
- msg := fmt.Errorf("src snapshot %s does not exist", srcSnapshotName)
- log.AddContext(ctx).Errorln(msg)
- return nil, msg
- }
-
- parentName, ok := srcSnapshot["PARENTNAME"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert parentName to string failed, data: %v", srcSnapshot["PARENTNAME"])
- }
- parentFS, err := p.cli.GetFileSystemByName(ctx, parentName)
- if err != nil {
- log.AddContext(ctx).Errorf("Get clone src filesystem %s error: %v", parentName, err)
- return nil, err
- }
- if parentFS == nil {
- msg := fmt.Errorf("Filesystem %s does not exist", parentName)
- log.AddContext(ctx).Errorln(msg)
- return nil, msg
- }
-
- srcSnapshotCapacity, err := strconv.ParseInt(parentFS["CAPACITY"].(string), 10, 64)
- if err != nil {
- return nil, err
- }
-
- cloneFilesystemReq := &CloneFilesystemRequest{
- FsName: params["name"].(string),
- ParentID: srcSnapshot["PARENTID"].(string),
- ParentSnapshotID: srcSnapshot["ID"].(string),
- AllocType: params["alloctype"].(int),
- CloneSpeed: params["clonespeed"].(int),
- CloneFsCapacity: params["capacity"].(int64),
- SrcCapacity: srcSnapshotCapacity,
- DeleteParentSnapshot: false,
- VStoreId: systemVStore,
- }
- cloneFS, err := p.cloneFilesystem(ctx, cloneFilesystemReq)
- if err != nil {
- log.AddContext(ctx).Errorf("Clone filesystem %s from source snapshot %s error: %s",
- cloneFilesystemReq.FsName, cloneFilesystemReq.ParentSnapshotID, err)
- return nil, err
- }
-
- return cloneFS, nil
-}
-
-func (p *NAS) cloneFilesystem(ctx context.Context, req *CloneFilesystemRequest) (map[string]interface{}, error) {
- cloneFS, err := p.cli.CloneFileSystem(ctx, req.FsName, req.AllocType, req.ParentID, req.ParentSnapshotID)
- if err != nil {
- log.AddContext(ctx).Errorf("Create cloneFilesystem failed. source filesystem ID [%s], error: [%v]",
- req.ParentID, err)
- return nil, err
- }
-
- cloneFSID, ok := cloneFS["ID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert cloneFSID to string failed, data: %v", cloneFS["ID"])
- }
- if req.CloneFsCapacity > req.SrcCapacity {
- err := p.cli.ExtendFileSystem(ctx, cloneFSID, req.CloneFsCapacity)
- if err != nil {
- log.AddContext(ctx).Errorf("Extend filesystem %s to capacity %d error: %v",
- cloneFSID, req.CloneFsCapacity, err)
- _ = p.cli.DeleteFileSystem(ctx, map[string]interface{}{"ID": cloneFSID})
- return nil, err
- }
- }
-
- vStoreId, ok := cloneFS["vstoreId"].(string)
- if ok {
- req.VStoreId = vStoreId
- }
-
- err = p.splitClone(ctx, cloneFSID, req)
- if err != nil {
- log.AddContext(ctx).Errorf("split clone failed. err: %v", err)
- }
-
- return cloneFS, nil
-}
-
-func (p *NAS) splitClone(ctx context.Context, cloneFSID string, req *CloneFilesystemRequest) error {
- err := p.cli.SplitCloneFS(ctx, cloneFSID, req.VStoreId, req.CloneSpeed, req.DeleteParentSnapshot)
- if err != nil {
- log.AddContext(ctx).Errorf("Split filesystem [%s] error: %v", req.FsName, err)
- delErr := p.cli.DeleteFileSystem(ctx, map[string]interface{}{"ID": cloneFSID})
- if delErr != nil {
- log.AddContext(ctx).Errorf("Delete filesystem [%s] error: %v", cloneFSID, err)
- }
- return err
- }
-
- return p.waitFSSplitDone(ctx, cloneFSID)
-}
-
-func (p *NAS) waitFSSplitDone(ctx context.Context, fsID string) error {
- return utils.WaitUntil(func() (bool, error) {
- fs, err := p.cli.GetFileSystemByID(ctx, fsID)
- if err != nil {
- return false, err
- }
-
- if fs["ISCLONEFS"] == "false" {
- return true, nil
- }
-
- if fs["HEALTHSTATUS"].(string) != filesystemHealthStatusNormal {
- return false, fmt.Errorf("filesystem %s has the bad healthStatus code %s", fs["NAME"], fs["HEALTHSTATUS"].(string))
- }
-
- splitStatus, ok := fs["SPLITSTATUS"].(string)
- if !ok {
- return false, pkgUtils.Errorf(ctx, "convert splitStatus to string failed, data: %v", fs["SPLITSTATUS"])
- }
- if splitStatus == filesystemSplitStatusQueuing ||
- splitStatus == filesystemSplitStatusSplitting ||
- splitStatus == filesystemSplitStatusNotStart {
- return false, nil
- } else if splitStatus == filesystemSplitStatusAbnormal {
- return false, fmt.Errorf("filesystem clone [%s] split status is interrupted, SPLITSTATUS: [%s]",
- fs["NAME"], splitStatus)
- } else {
- return true, nil
- }
- }, time.Hour*6, time.Second*5)
-}
-
-func (p *NAS) revertLocalFS(ctx context.Context, taskResult map[string]interface{}) error {
- fsID, exist := taskResult["localFSID"].(string)
- if !exist || fsID == "" {
- return nil
- }
- deleteParams := map[string]interface{}{
- "ID": fsID,
- }
- if vStoreID, _ := taskResult["localVStoreID"].(string); vStoreID != "" {
- deleteParams["vstoreId"] = vStoreID
- }
- return p.cli.DeleteFileSystem(ctx, deleteParams)
-}
-
-func (p *NAS) createLocalQoS(ctx context.Context,
- params, taskResult map[string]interface{}) (map[string]interface{}, error) {
- qos, exist := params["qos"].(map[string]int)
- if !exist {
- return nil, nil
- }
-
- activeClient := p.getActiveClient(taskResult)
- smartX := smartx.NewSmartX(activeClient)
- vStoreID := p.getVStoreID(taskResult)
- fsID := p.getActiveFsID(taskResult)
- qosID, err := smartX.CreateQos(ctx, fsID, "fs", vStoreID, qos)
- if err != nil {
- log.AddContext(ctx).Errorf("Create qos %v for fs %s error: %v", qos, fsID, err)
- return nil, err
- }
-
- return map[string]interface{}{
- "localQoSID": qosID,
- }, nil
-}
-
-func (p *NAS) revertLocalQoS(ctx context.Context, taskResult map[string]interface{}) error {
- fsID, fsIDExist := taskResult["localFSID"].(string)
- qosID, qosIDExist := taskResult["localQoSID"].(string)
- if !fsIDExist || !qosIDExist {
- return nil
- }
-
- activeClient := p.getActiveClient(taskResult)
- smartX := smartx.NewSmartX(activeClient)
- vStoreID := p.getVStoreID(taskResult)
- fsID = p.getActiveFsID(taskResult)
- return smartX.DeleteQos(ctx, qosID, fsID, "fs", vStoreID)
-}
-
-func (p *NAS) createRemoteQoS(ctx context.Context,
- params, taskResult map[string]interface{}) (map[string]interface{}, error) {
- if p.product == "DoradoV6" {
- return nil, nil
- }
-
- qos, exist := params["qos"].(map[string]int)
- if !exist {
- return nil, nil
- }
-
- fsID, ok := taskResult["remoteFSID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert fsID to string failed, data: %v", taskResult["remoteFSID"])
- }
- remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert remoteCli to BaseClientInterface failed, data: %v", taskResult["remoteCli"])
- }
-
- smartX := smartx.NewSmartX(remoteCli)
- qosID, err := smartX.CreateQos(ctx, fsID, "fs", "", qos)
- if err != nil {
- log.AddContext(ctx).Errorf("Create qos %v for fs %s error: %v", qos, fsID, err)
- return nil, err
- }
-
- return map[string]interface{}{
- "remoteQoSID": qosID,
- }, nil
-}
-
-func (p *NAS) revertRemoteQoS(ctx context.Context, taskResult map[string]interface{}) error {
- if p.product == "DoradoV6" {
- return nil
- }
-
- fsID, fsIDExist := taskResult["remoteFSID"].(string)
- qosID, qosIDExist := taskResult["remoteQoSID"].(string)
- if !fsIDExist || !qosIDExist {
- return nil
- }
- remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
- if !ok {
- return pkgUtils.Errorf(ctx, "convert remoteCli to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
- }
- smartX := smartx.NewSmartX(remoteCli)
- return smartX.DeleteQos(ctx, qosID, fsID, "fs", "")
-}
-
-func (p *NAS) createShare(ctx context.Context,
- params, taskResult map[string]interface{}) (map[string]interface{}, error) {
- fsName, ok := params["name"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert fsName to string failed, data: %v", params["name"])
- }
- sharePath := utils.GetSharePath(fsName)
- activeClient := p.getActiveClient(taskResult)
- vStoreID := p.getVStoreID(taskResult)
- share, err := activeClient.GetNfsShareByPath(ctx, sharePath, vStoreID)
- if err != nil {
- log.AddContext(ctx).Errorf("Get nfs share by path %s error: %v", sharePath, err)
- return nil, err
- }
-
- if share == nil {
- fsID := p.getActiveFsID(taskResult)
- shareParams := map[string]interface{}{
- "sharepath": sharePath,
- "fsid": fsID,
- "description": params["description"].(string),
- "vStoreID": vStoreID,
- }
-
- share, err = activeClient.CreateNfsShare(ctx, shareParams)
- if err != nil {
- log.AddContext(ctx).Errorf("Create nfs share %v error: %v", shareParams, err)
- return nil, err
- }
- }
-
- return map[string]interface{}{
- "shareID": share["ID"].(string),
- }, nil
-}
-
-func (p *NAS) revertShare(ctx context.Context, taskResult map[string]interface{}) error {
- shareID, exist := taskResult["shareID"].(string)
- if !exist || len(shareID) == 0 {
- return nil
- }
- activeClient := p.getActiveClient(taskResult)
- vStoreID := p.getVStoreID(taskResult)
- return activeClient.DeleteNfsShare(ctx, shareID, vStoreID)
-}
-
-func (p *NAS) getCurrentShareAccess(ctx context.Context, shareID, vStoreID string, cli client.BaseClientInterface) (map[string]interface{}, error) {
- count, err := cli.GetNfsShareAccessCount(ctx, shareID, vStoreID)
- if err != nil {
- return nil, err
- }
-
- accesses := make(map[string]interface{})
-
- var i int64 = 0
- for ; i < count; i += 100 { // Query per page 100
- clients, err := cli.GetNfsShareAccessRange(ctx, shareID, vStoreID, i, i+100)
- if err != nil {
- return nil, err
- }
- if clients == nil {
- break
- }
-
- for _, c := range clients {
- client, ok := c.(map[string]interface{})
- if !ok {
- log.AddContext(ctx).Warningf("convert client to map failed, data: %v", c)
- continue
- }
- name, ok := client["NAME"].(string)
- if !ok {
- log.AddContext(ctx).Warningf("convert client name to string failed, data: %v", client["NAME"])
- continue
- }
- accesses[name] = c
- }
- }
-
- return accesses, nil
-}
-
-func (p *NAS) allowShareAccess(ctx context.Context,
- params, taskResult map[string]interface{}) (map[string]interface{}, error) {
- allowShareAccessParam, err := p.preShareAccessParam(ctx, params, taskResult)
- if err != nil {
- return nil, err
- }
-
- for _, i := range strings.Split(allowShareAccessParam.authClient, ";") {
- if _, exist := allowShareAccessParam.accesses[i]; exist {
- delete(allowShareAccessParam.accesses, i)
- continue
- }
-
- req := &client.AllowNfsShareAccessRequest{
- Name: i,
- ParentID: allowShareAccessParam.shareID,
- AccessVal: 1,
- Sync: 0,
- AllSquash: params["allsquash"].(int),
- RootSquash: params["rootsquash"].(int),
- VStoreID: allowShareAccessParam.vStoreID,
- AccessKrb5: formatKerberosParam(params["accesskrb5"]),
- AccessKrb5i: formatKerberosParam(params["accesskrb5i"]),
- AccessKrb5p: formatKerberosParam(params["accesskrb5p"]),
- }
- if err = allowShareAccessParam.activeClient.AllowNfsShareAccess(ctx, req); err != nil {
- log.AddContext(ctx).Errorf("Allow nfs share access %v failed. error: %v", req, err)
- return nil, err
- }
- }
-
- // Remove all other extra access
- for _, i := range allowShareAccessParam.accesses {
- access, ok := i.(map[string]interface{})
- if !ok {
- log.AddContext(ctx).Warningf("convert access to map failed, data: %v", i)
- continue
- }
- accessID, ok := access["ID"].(string)
- if !ok {
- log.AddContext(ctx).Warningf("convert accessID to string failed, data: %v", access["ID"])
- continue
- }
- if err = allowShareAccessParam.activeClient.DeleteNfsShareAccess(ctx, accessID,
- allowShareAccessParam.vStoreID); err != nil {
- log.AddContext(ctx).Warningf("Delete extra nfs share access %s error: %v", accessID, err)
- }
- }
-
- return map[string]interface{}{
- "authClient": allowShareAccessParam.authClient,
- }, nil
-}
-
-func (p *NAS) preShareAccessParam(ctx context.Context, params,
- taskResult map[string]interface{}) (*allowNfsShareAccessParam, error) {
- var res allowNfsShareAccessParam
- var err error
- var b bool
- res.shareID, b = taskResult["shareID"].(string)
- if !b {
- return nil, pkgUtils.Errorf(ctx, "convert shareID to string failed, data: %v",
- taskResult["shareID"])
- }
- res.authClient, b = params["authclient"].(string)
- if !b {
- return nil, pkgUtils.Errorf(ctx, "convert authClient to string failed, data: %v",
- params["authclient"])
- }
- res.activeClient = p.getActiveClient(taskResult)
- res.vStoreID = p.getVStoreID(taskResult)
- res.accesses, err = p.getCurrentShareAccess(ctx, res.shareID, res.vStoreID, res.activeClient)
- if err != nil {
- return nil, pkgUtils.Errorf(ctx, "Get current access of share %s error: %v", res.shareID, err)
- }
- return &res, nil
-}
-
-func (p *NAS) revertShareAccess(ctx context.Context, taskResult map[string]interface{}) error {
- shareID, ok := taskResult["shareID"].(string)
- if !ok {
- return pkgUtils.Errorf(ctx, "convert shareID to string failed, data: %v", taskResult["shareID"])
- }
- authClient, exist := taskResult["authClient"].(string)
- if !exist {
- return nil
- }
-
- activeClient := p.getActiveClient(taskResult)
- vStoreID := p.getVStoreID(taskResult)
- accesses, err := p.getCurrentShareAccess(ctx, shareID, vStoreID, activeClient)
- if err != nil {
- log.AddContext(ctx).Errorf("Get current access of share %s error: %v", shareID, err)
- return err
- }
-
- for _, i := range strings.Split(authClient, ";") {
- if _, exist := accesses[i]; !exist {
- continue
- }
- access, ok := accesses[i].(map[string]interface{})
- if !ok {
- log.AddContext(ctx).Warningf("convert access to map failed, data: %v", accesses[i])
- continue
- }
- accessID, ok := access["ID"].(string)
- if !ok {
- log.AddContext(ctx).Warningf("convert accessID to string failed, data: %v", access["ID"])
- continue
- }
- err := p.cli.DeleteNfsShareAccess(ctx, accessID, vStoreID)
- if err != nil {
- log.AddContext(ctx).Warningf("Delete extra nfs share access %s error: %v", accessID, err)
- }
- }
- return nil
-}
-
// Query queries volume by name
func (p *NAS) Query(ctx context.Context, fsName string, params map[string]interface{}) (utils.Volume, error) {
fs, err := p.cli.GetFileSystemByName(ctx, fsName)
@@ -993,8 +393,9 @@ func (p *NAS) Query(ctx context.Context, fsName string, params map[string]interf
volObj := utils.NewVolume(fsName)
// set the size, need to trans Sectors to Bytes
- if capacity, err := strconv.ParseInt(fs["CAPACITY"].(string), 10, 64); err == nil {
- volObj.SetSize(utils.TransK8SCapacity(capacity, 512))
+ if capacity, err := strconv.ParseInt(fs["CAPACITY"].(string),
+ constants.DefaultIntBase, constants.DefaultIntBitSize); err == nil {
+ volObj.SetSize(utils.TransK8SCapacity(capacity, constants.AllocationUnitBytes))
}
if fileSystemMode, ok := fs["fileSystemMode"].(string); ok {
volObj.SetFilesystemMode(fileSystemMode)
@@ -1032,7 +433,7 @@ func (p *NAS) Delete(ctx context.Context, fsName string) error {
if err != nil {
return err
}
- taskflow := taskflow.NewTaskFlow(ctx, "Delete-FileSystem-Volume")
+ taskflow := flow.NewTaskFlow(ctx, "Delete-FileSystem-Volume")
if len(hyperMetroIDs) > 0 {
if p.metroRemoteCli == nil {
return errors.New("hyper metro backend is not configured")
@@ -1086,7 +487,7 @@ func (p *NAS) Expand(ctx context.Context, fsName string, newSize int64) error {
if err != nil {
return err
}
- expandTask := taskflow.NewTaskFlow(ctx, "Expand-FileSystem-Volume")
+ expandTask := flow.NewTaskFlow(ctx, "Expand-FileSystem-Volume")
expandTask.AddTask("Expand-PreCheck-Capacity", p.preExpandCheckCapacity, nil)
if len(hyperMetroIDs) > 0 {
@@ -1135,15 +536,15 @@ func (p *NAS) preExpandCheckCapacity(ctx context.Context,
}, nil
}
-func (p *NAS) createRemoteFS(ctx context.Context,
- params, taskResult map[string]interface{}) (map[string]interface{}, error) {
+func (p *NAS) createRemoteFS(ctx context.Context, params, taskResult map[string]any) (map[string]any, error) {
fsName, ok := params["name"].(string)
if !ok {
return nil, pkgUtils.Errorf(ctx, "convert fsName to string failed, data: %v", params["name"])
}
remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert remoteCli to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
+ return nil, pkgUtils.Errorf(ctx,
+ "convert remoteCli to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
}
fs, err := remoteCli.GetFileSystemByName(ctx, fsName)
@@ -1167,7 +568,7 @@ func (p *NAS) createRemoteFS(ctx context.Context,
}
}
- return map[string]interface{}{
+ return map[string]any{
"remoteFSID": fs["ID"].(string),
}, nil
}
@@ -1179,7 +580,8 @@ func (p *NAS) revertRemoteFS(ctx context.Context, taskResult map[string]interfac
}
remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
if !ok {
- return pkgUtils.Errorf(ctx, "convert remoteCli to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
+ return pkgUtils.Errorf(ctx,
+ "convert remoteCli to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
}
deleteParams := map[string]interface{}{
"ID": fsID,
@@ -1362,74 +764,6 @@ func (p *NAS) getHyperMetroParams(ctx context.Context,
}, nil
}
-func (p *NAS) createHyperMetro(ctx context.Context,
- params, taskResult map[string]interface{}) (map[string]interface{}, error) {
- vStorePairID, ok := params["vstorepairid"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert vStorePairID to string failed, data: %v", params["vstorepairid"])
- }
- localFSID, ok := taskResult["localFSID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert localFSID to string failed, data: %v", taskResult["localFSID"])
- }
- remoteFSID, ok := taskResult["remoteFSID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert remoteFSID to string failed, data: %v", taskResult["remoteFSID"])
- }
- activeClient := p.getActiveClient(taskResult)
- if activeClient != p.cli {
- localFSID, ok = taskResult["remoteFSID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert localFSID to string failed, data: %v", taskResult["remoteFSID"])
- }
-
- remoteFSID, ok = taskResult["localFSID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert remoteFSID to string failed, data: %v", taskResult["localFSID"])
- }
- }
-
- data := map[string]interface{}{
- "HCRESOURCETYPE": 2, // 2: file system
- "LOCALOBJID": localFSID,
- "REMOTEOBJID": remoteFSID,
- "SPEED": 4, // 4: highest speed
- "VSTOREPAIRID": vStorePairID,
- }
-
- metroDomainID, exist := params["metroDomainID"].(string)
- if exist && metroDomainID != "" {
- data["DOMAINID"] = metroDomainID
- }
-
- pair, err := activeClient.CreateHyperMetroPair(ctx, data)
- if err != nil {
- log.AddContext(ctx).Errorf("Create nas hypermetro pair error: %v", err)
- return nil, err
- }
-
- pairID, ok := pair["ID"].(string)
- if !ok {
- return nil, pkgUtils.Errorf(ctx, "convert pairID to string failed, data: %v", pair["ID"])
- }
- // There is no need to synchronize when use NAS Dorado V6 or OceanStor V6 HyperMetro Volume
- if p.product != constants.OceanStorDoradoV6 {
- err = activeClient.SyncHyperMetroPair(ctx, pairID)
- if err != nil {
- log.AddContext(ctx).Errorf("Sync nas hypermetro pair %s error: %v", pairID, err)
- delErr := activeClient.DeleteHyperMetroPair(ctx, pairID, true)
- if delErr != nil {
- log.AddContext(ctx).Errorf("delete hypermetro pair %s error: %v", pairID, err)
- }
- return nil, err
- }
- }
-
- return map[string]interface{}{
- "hyperMetroPairID": pairID,
- }, nil
-}
-
func (p *NAS) revertHyperMetro(ctx context.Context, taskResult map[string]interface{}) error {
pairID, exist := taskResult["hyperMetroPairID"].(string)
if !exist {
@@ -1503,7 +837,8 @@ func (p *NAS) DeleteHyperMetro(ctx context.Context,
return nil, nil
}
-func (p *NAS) waitHyperMetroPairDeleted(ctx context.Context, pairID string, activeClient client.BaseClientInterface) error {
+func (p *NAS) waitHyperMetroPairDeleted(ctx context.Context,
+ pairID string, activeClient client.BaseClientInterface) error {
var err error
if p.product == "DoradoV6" {
err = activeClient.DeleteHyperMetroPair(ctx, pairID, false)
@@ -1669,9 +1004,7 @@ func (p *NAS) CreateSnapshot(ctx context.Context, fsName, snapshotName string) (
return nil, err
}
- const capacityBase = 10
- const capacityBitSize = 64
- snapshotSize, err := strconv.ParseInt(fs.CAPACITY, capacityBase, capacityBitSize)
+ snapshotSize, err := strconv.ParseInt(fs.CAPACITY, constants.DefaultIntBase, constants.DefaultIntBitSize)
if err != nil {
log.AddContext(ctx).Errorf("parse filesystem failed. err:%v, CAPACITY: %v", err, fs.CAPACITY)
return nil, err
diff --git a/storage/oceanstor/volume/san.go b/storage/oceanstor/volume/san.go
index b628d518..07811c89 100644
--- a/storage/oceanstor/volume/san.go
+++ b/storage/oceanstor/volume/san.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -24,12 +24,18 @@ import (
"strconv"
"time"
+ "huawei-csi-driver/pkg/constants"
pkgUtils "huawei-csi-driver/pkg/utils"
"huawei-csi-driver/storage/oceanstor/client"
"huawei-csi-driver/storage/oceanstor/smartx"
"huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/flow"
"huawei-csi-driver/utils/log"
- "huawei-csi-driver/utils/taskflow"
+)
+
+const (
+ waitUntilTimeout = 6 * time.Hour
+ waitUntilInterval = 5 * time.Second
)
// SAN provides base san client
@@ -84,7 +90,7 @@ func (p *SAN) Create(ctx context.Context, params map[string]interface{}) (utils.
return nil, err
}
- taskflow := taskflow.NewTaskFlow(ctx, "Create-LUN-Volume")
+ taskflow := flow.NewTaskFlow(ctx, "Create-LUN-Volume")
hyperMetro, hyperMetroOK := params["hypermetro"].(bool)
if hyperMetroOK && hyperMetro {
@@ -127,8 +133,9 @@ func (p *SAN) Query(ctx context.Context, name string) (utils.Volume, error) {
volObj.SetLunWWN(lunWWN)
}
// set the size, need to trans Sectors to Bytes
- if capacity, err := strconv.ParseInt(lun["CAPACITY"].(string), 10, 64); err == nil {
- volObj.SetSize(utils.TransK8SCapacity(capacity, 512))
+ if capacity, err := strconv.ParseInt(lun["CAPACITY"].(string),
+ constants.DefaultIntBase, constants.DefaultIntBitSize); err == nil {
+ volObj.SetSize(utils.TransK8SCapacity(capacity, constants.AllocationUnitBytes))
}
return volObj, nil
@@ -156,7 +163,7 @@ func (p *SAN) Delete(ctx context.Context, name string) error {
if err != nil {
return pkgUtils.Errorf(ctx, "Unmarshal san HASRSSOBJECT failed, data: %v, err: %v", rssStr, err)
}
- taskflow := taskflow.NewTaskFlow(ctx, "Delete-LUN-Volume")
+ taskflow := flow.NewTaskFlow(ctx, "Delete-LUN-Volume")
if hyperMetro, ok := rss["HyperMetro"]; ok && hyperMetro == "TRUE" {
taskflow.AddTask("Delete-HyperMetro", p.deleteHyperMetro, nil)
taskflow.AddTask("Delete-HyperMetro-Remote-LUN", p.deleteHyperMetroRemoteLun, nil)
@@ -213,7 +220,7 @@ func (p *SAN) Expand(ctx context.Context, name string, newSize int64) (bool, err
if err != nil {
return false, pkgUtils.Errorf(ctx, "Unmarshal HASHSSOBJECT failed, error: %v", err)
}
- expandTask := taskflow.NewTaskFlow(ctx, "Expand-LUN-Volume")
+ expandTask := flow.NewTaskFlow(ctx, "Expand-LUN-Volume")
expandTask.AddTask("Expand-PreCheck-Capacity", p.preExpandCheckCapacity, nil)
if hyperMetro, ok := rss["HyperMetro"]; ok && hyperMetro == "TRUE" {
@@ -843,7 +850,7 @@ func (p *SAN) waitLunCopyFinish(ctx context.Context, lunCopyName string) error {
} else {
return true, nil
}
- }, time.Hour*6, time.Second*5)
+ }, waitUntilTimeout, waitUntilInterval)
if err != nil {
return err
@@ -883,7 +890,7 @@ func (p *SAN) waitClonePairFinish(ctx context.Context, clonePairID string) error
} else {
return false, fmt.Errorf("ClonePair %s running status is abnormal", clonePairID)
}
- }, time.Hour*6, time.Second*5)
+ }, waitUntilTimeout, waitUntilInterval)
if err != nil {
return err
@@ -930,7 +937,8 @@ func (p *SAN) createRemoteLun(ctx context.Context,
}
remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
if !ok {
- return nil, pkgUtils.Errorf(ctx, "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
+ return nil, pkgUtils.Errorf(ctx,
+ "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
}
lun, err := remoteCli.GetLunByName(ctx, lunName)
@@ -965,7 +973,8 @@ func (p *SAN) revertRemoteLun(ctx context.Context, taskResult map[string]interfa
}
remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
if !ok {
- return pkgUtils.Errorf(ctx, "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
+ return pkgUtils.Errorf(ctx,
+ "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
}
return remoteCli.DeleteLun(ctx, lunID)
}
@@ -984,7 +993,8 @@ func (p *SAN) createRemoteQoS(ctx context.Context,
remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
if !ok {
- return nil, pkgUtils.Errorf(ctx, "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
+ return nil, pkgUtils.Errorf(ctx,
+ "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
}
lun, err := remoteCli.GetLunByID(ctx, lunID)
if err != nil {
@@ -1014,7 +1024,8 @@ func (p *SAN) revertRemoteQoS(ctx context.Context, taskResult map[string]interfa
}
remoteCli, ok := taskResult["remoteCli"].(client.BaseClientInterface)
if !ok {
- return pkgUtils.Errorf(ctx, "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
+ return pkgUtils.Errorf(ctx,
+ "remoteCli convert to client.BaseClientInterface failed, data: %v", taskResult["remoteCli"])
}
smartX := smartx.NewSmartX(remoteCli)
return smartX.DeleteQos(ctx, qosID, lunID, "lun", "")
@@ -1129,7 +1140,7 @@ func (p *SAN) waitHyperMetroSyncFinish(ctx context.Context, pairID string) error
} else {
return true, nil
}
- }, time.Hour*6, time.Second*5)
+ }, waitUntilTimeout, waitUntilInterval)
if err != nil {
p.cli.StopHyperMetroPair(ctx, pairID)
@@ -1501,7 +1512,7 @@ func (p *SAN) CreateSnapshot(ctx context.Context,
}
}
- taskflow := taskflow.NewTaskFlow(ctx, "Create-LUN-Snapshot")
+ taskflow := flow.NewTaskFlow(ctx, "Create-LUN-Snapshot")
taskflow.AddTask("Create-Snapshot", p.createSnapshot, p.revertSnapshot)
taskflow.AddTask("Active-Snapshot", p.activateSnapshot, nil)
@@ -1539,7 +1550,7 @@ func (p *SAN) DeleteSnapshot(ctx context.Context, snapshotName string) error {
return nil
}
- taskflow := taskflow.NewTaskFlow(ctx, "Delete-LUN-Snapshot")
+ taskflow := flow.NewTaskFlow(ctx, "Delete-LUN-Snapshot")
taskflow.AddTask("Deactivate-Snapshot", p.deactivateSnapshot, nil)
taskflow.AddTask("Delete-Snapshot", p.deleteSnapshot, nil)
@@ -1608,7 +1619,7 @@ func (p *SAN) waitSnapshotReady(ctx context.Context, snapshotName string) error
} else {
return false, nil
}
- }, time.Hour*6, time.Second*5)
+ }, waitUntilTimeout, waitUntilInterval)
if err != nil {
return err
diff --git a/utils/concurrent/slice.go b/utils/concurrent/slice.go
new file mode 100644
index 00000000..f3dfa91a
--- /dev/null
+++ b/utils/concurrent/slice.go
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. 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 concurrent
+
+import (
+ "fmt"
+ "sync"
+)
+
+// Slice is a slice wrapper for safe concurrency
+type Slice[T any] struct {
+ mu sync.RWMutex
+ slice []T
+}
+
+// Len gets the length of Slice
+func (s *Slice[T]) Len() int {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return len(s.slice)
+}
+
+// Get gets the value of given index,
+// NOTE that if index is out of range, will return zero value of T.
+func (s *Slice[T]) Get(index int) T {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ if index < 0 || index >= len(s.slice) {
+ var zero T
+ return zero
+ }
+
+ return s.slice[index]
+}
+
+// Values gets a copy values of the base slice
+func (s *Slice[T]) Values() []T {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ copySlice := make([]T, 0, len(s.slice))
+ for _, v := range s.slice {
+ copySlice = append(copySlice, v)
+ }
+
+ return copySlice
+}
+
+// Append appends elements to the end of the Slice
+func (s *Slice[T]) Append(e ...T) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.slice = append(s.slice, e...)
+}
+
+// Cut cuts the Slice by the given low and high index
+func (s *Slice[T]) Cut(low, high int) error {
+ if low > high {
+ return fmt.Errorf("low index is greater than high index")
+ }
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if low < 0 || low > len(s.slice) || high < 0 || high > len(s.slice) {
+ return fmt.Errorf("index out of range [%d,%d] with length: %d", low, high, len(s.slice))
+ }
+ s.slice = s.slice[low:high]
+ return nil
+}
+
+// Reset sets the base slice to nil
+func (s *Slice[T]) Reset() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.slice = nil
+}
diff --git a/utils/concurrent/slice_test.go b/utils/concurrent/slice_test.go
new file mode 100644
index 00000000..42df239b
--- /dev/null
+++ b/utils/concurrent/slice_test.go
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. 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 concurrent
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestSlice_ConcurrentAppend(t *testing.T) {
+ // arrange
+ s := Slice[struct{}]{}
+ empty := struct{}{}
+ expectedLen := 10000
+ var wg sync.WaitGroup
+
+ // action
+ for range expectedLen {
+ wg.Add(1)
+ go func() {
+ s.Append(empty)
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+
+ // assert
+ require.Equal(t, expectedLen, s.Len())
+}
+
+func TestSlice_Reset(t *testing.T) {
+ // arrange
+ s := Slice[int]{slice: []int{2, 3}}
+
+ // action
+ s.Reset()
+ s.Append(1)
+
+ // assert
+ require.Equal(t, 1, s.Len())
+ require.Equal(t, 1, s.Get(0))
+ require.Equal(t, []int{1}, s.Values())
+}
+
+func TestSlice_Cut(t *testing.T) {
+ // arrange
+ s := []int{1, 2}
+ getSlice := func() Slice[int] { return Slice[int]{slice: s} }
+ cases := []struct {
+ name string
+ l int
+ h int
+ expectedValues []int
+ expectedErr bool
+ }{
+ {name: "l is 0, h is 0", l: 0, h: 0, expectedValues: s[0:0], expectedErr: false},
+ {name: "l is 0, h is 1", l: 0, h: 1, expectedValues: s[0:1], expectedErr: false},
+ {name: "l is 0, h is 2", l: 0, h: 2, expectedValues: s[0:], expectedErr: false},
+ {name: "l is 1, h is 1", l: 1, h: 1, expectedValues: s[1:1], expectedErr: false},
+ {name: "l is 1, h is 2", l: 1, h: 2, expectedValues: s[1:], expectedErr: false},
+ {name: "l is 2, h is 2", l: 2, h: 2, expectedValues: s[2:], expectedErr: false},
+ {name: "l is 1, h is 0", l: 1, h: 0, expectedValues: s, expectedErr: true},
+ {name: "l is 0, h is 3", l: 0, h: 3, expectedValues: s, expectedErr: true},
+ {name: "l is 1, h is 3", l: 1, h: 3, expectedValues: s, expectedErr: true},
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ s := getSlice()
+
+ // action
+ err := s.Cut(c.l, c.h)
+
+ // assert
+ require.Equal(t, c.expectedErr, err != nil)
+ if err == nil {
+ require.Equal(t, c.expectedValues, s.Values())
+ }
+ })
+ }
+}
+
+func TestSlice_Get(t *testing.T) {
+ // arrange
+ s := Slice[int]{slice: []int{1, 2}}
+
+ // action
+ v1 := s.Get(0)
+ v2 := s.Get(1)
+ v3 := s.Get(2)
+
+ // assert
+ require.Equal(t, 1, v1)
+ require.Equal(t, 2, v2)
+ require.Equal(t, 0, v3)
+}
diff --git a/utils/flow/taskflow.go b/utils/flow/taskflow.go
new file mode 100644
index 00000000..35c987e6
--- /dev/null
+++ b/utils/flow/taskflow.go
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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 flow offers task flow operations
+package flow
+
+import (
+ "context"
+
+ "huawei-csi-driver/utils"
+ "huawei-csi-driver/utils/log"
+)
+
+// TaskRunFunc run task
+type TaskRunFunc func(ctx context.Context, params map[string]any, result map[string]any) (map[string]any, error)
+
+// TaskWithoutRevert run task without revert
+type TaskWithoutRevert func(ctx context.Context, params map[string]interface{}) error
+
+// TaskRevertFunc revert task
+type TaskRevertFunc func(ctx context.Context, result map[string]interface{}) error
+
+// Task defines the task
+type Task struct {
+ name string
+ finish bool
+ run TaskRunFunc
+ revert TaskRevertFunc
+}
+
+// TaskFlow defines the task flow
+type TaskFlow struct {
+ name string
+ tasks []*Task
+ result map[string]interface{}
+ ctx context.Context
+}
+
+// NewTaskFlow create a task flow
+func NewTaskFlow(ctx context.Context, name string) *TaskFlow {
+ return &TaskFlow{
+ name: name,
+ result: make(map[string]interface{}),
+ ctx: ctx,
+ }
+}
+
+// AddTask add a task to task flow
+func (p *TaskFlow) AddTask(name string, run TaskRunFunc, revert TaskRevertFunc) {
+ p.tasks = append(p.tasks, &Task{
+ name: name,
+ finish: false,
+ run: run,
+ revert: revert,
+ })
+}
+
+// Run execute tasks in the task flow
+func (p *TaskFlow) Run(params map[string]interface{}) (map[string]interface{}, error) {
+ log.AddContext(p.ctx).Debugf("Start to run taskflow %s", p.name)
+
+ for _, task := range p.tasks {
+ result, err := task.run(p.ctx, params, p.result)
+ if err != nil {
+ log.AddContext(p.ctx).Errorf("Run task %s of taskflow %s error: %v", task.name, p.name, err)
+ return nil, err
+ }
+
+ task.finish = true
+
+ if result != nil {
+ p.result = utils.MergeMap(p.result, result)
+ }
+ }
+
+ log.AddContext(p.ctx).Debugf("Taskflow %s is finished", p.name)
+ return p.result, nil
+}
+
+// GetResult get tasks execution results in the task flow
+func (p *TaskFlow) GetResult() map[string]interface{} {
+ return p.result
+}
+
+// Revert revert tasks in the task flow with revert function
+func (p *TaskFlow) Revert() {
+ log.AddContext(p.ctx).Infof("Start to revert taskflow %s", p.name)
+
+ for i := len(p.tasks) - 1; i >= 0; i-- {
+ task := p.tasks[i]
+
+ if task.finish && task.revert != nil {
+ err := task.revert(p.ctx, p.result)
+ if err != nil {
+ log.AddContext(p.ctx).Warningf("Revert task %s of taskflow %s error: %v", task.name, p.name, err)
+ }
+ }
+ }
+
+ log.AddContext(p.ctx).Infof("Taskflow %s is reverted", p.name)
+}
+
+// AddTaskWithOutRevert be used when the task does not need revert function
+func (p *TaskFlow) AddTaskWithOutRevert(run TaskWithoutRevert) *TaskFlow {
+ var buildFun = func(ctx context.Context, params map[string]interface{},
+ _ map[string]interface{}) (map[string]interface{}, error) {
+ if err := run(ctx, params); err != nil {
+ return nil, err
+ }
+ return nil, nil
+ }
+ p.AddTask("", buildFun, nil)
+ return p
+}
+
+// RunWithOutRevert run task without revert function and return only error
+func (p *TaskFlow) RunWithOutRevert(params map[string]interface{}) error {
+ if _, err := p.Run(params); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/utils/flow/taskflow_test.go b/utils/flow/taskflow_test.go
new file mode 100644
index 00000000..6331522a
--- /dev/null
+++ b/utils/flow/taskflow_test.go
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. 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 flow
+
+import (
+ "context"
+ "errors"
+ "reflect"
+ "testing"
+
+ "huawei-csi-driver/utils/log"
+)
+
+var (
+ errMsg = "an error occurred while run mock fun"
+
+ mockFun1 = func(ctx context.Context, params map[string]interface{}) error {
+ params["key_1"] = "value_1"
+ return nil
+ }
+ mockFun2 = func(ctx context.Context, params map[string]interface{}) error {
+ params["key_2"] = "value_2"
+ return nil
+ }
+ mockFun3 = func(ctx context.Context, params map[string]interface{}) error {
+ return errors.New(errMsg)
+ }
+)
+
+const (
+ logName = "taskFlowTest.log"
+)
+
+func TestMain(m *testing.M) {
+ log.MockInitLogging(logName)
+ defer log.MockStopLogging(logName)
+
+ m.Run()
+}
+
+func TestAllTaskReturnSuccess(t *testing.T) {
+ testParams := map[string]interface{}{}
+ err := NewTaskFlow(context.Background(), "test_all_task_return_success").
+ AddTaskWithOutRevert(mockFun1).
+ AddTaskWithOutRevert(mockFun2).
+ RunWithOutRevert(testParams)
+ if err != nil {
+ t.Errorf("an error occurred while run TestTaskWithOutRevert(), err: %v", err)
+ }
+
+ result := map[string]interface{}{
+ "key_1": "value_1",
+ "key_2": "value_2",
+ }
+ if !reflect.DeepEqual(testParams, result) {
+ t.Error("got an unexpected value while run TestTaskWithOutRevert()")
+ }
+}
+
+func TestRunTaskFail(t *testing.T) {
+ testParams := map[string]interface{}{}
+ err := NewTaskFlow(context.Background(), "test_run_task_fail").
+ AddTaskWithOutRevert(mockFun1).
+ AddTaskWithOutRevert(mockFun2).
+ AddTaskWithOutRevert(mockFun3).
+ RunWithOutRevert(testParams)
+ if err == nil {
+ t.Error("an error should be returned while run TestRunTaskFail()")
+ }
+
+ if err.Error() != errMsg {
+ t.Error("got an unexpected error while run TestRunTaskFail()")
+ }
+}
diff --git a/utils/flow/transaction.go b/utils/flow/transaction.go
new file mode 100644
index 00000000..2fba8e67
--- /dev/null
+++ b/utils/flow/transaction.go
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. 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 flow
+
+type transactionStep struct {
+ exec func() error
+ onRollback func()
+}
+
+// Transaction implements a TCC (Try Confirm Cancel) pattern.
+type Transaction struct {
+ stepAt int
+ steps []transactionStep
+}
+
+// NewTransaction instantiate a new transaction.
+func NewTransaction() *Transaction {
+ return &Transaction{
+ steps: []transactionStep{},
+ }
+}
+
+// Then adds a step to the steps chain, and returns the same Transaction,
+func (t *Transaction) Then(exec func() error, onRollback func()) *Transaction {
+ t.steps = append(t.steps, transactionStep{
+ exec: exec,
+ onRollback: onRollback,
+ })
+ return t
+}
+
+// Commit executes the Transaction steps and returns error if any one step returns error.
+func (t *Transaction) Commit() error {
+ var err error
+
+ for t.stepAt < len(t.steps) {
+ if t.stepAt >= 0 && t.stepAt < len(t.steps) && t.steps[t.stepAt].exec != nil {
+ if err = t.steps[t.stepAt].exec(); err != nil {
+ break
+ }
+ }
+
+ t.stepAt++
+ }
+
+ return err
+}
+
+// Rollback executes the Transaction rollbacks.
+func (t *Transaction) Rollback() {
+ for i := t.stepAt - 1; i >= 0; i-- {
+ if i < len(t.steps) && t.steps[i].onRollback != nil {
+ t.steps[i].onRollback()
+ }
+ }
+}
diff --git a/utils/flow/transaction_test.go b/utils/flow/transaction_test.go
new file mode 100644
index 00000000..92f5c983
--- /dev/null
+++ b/utils/flow/transaction_test.go
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. 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 flow_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "huawei-csi-driver/utils/flow"
+)
+
+func TestTransaction_NoError(t *testing.T) {
+ // arrange
+ var i int
+ transaction := flow.NewTransaction().
+ Then(
+ func() error {
+ i++
+ return nil
+ },
+ func() { i-- },
+ ).
+ Then(
+ func() error {
+ i += 2
+ return nil
+ },
+ func() { i -= 2 },
+ )
+
+ // act
+ err := transaction.Commit()
+
+ // assert
+ require.NoError(t, err)
+ require.Equal(t, 3, i)
+}
+
+func TestTransaction_WithError(t *testing.T) {
+ // arrange
+ var i int
+ transaction := flow.NewTransaction().
+ Then(
+ func() error {
+ return assert.AnError
+ },
+ func() { i-- },
+ )
+
+ // act
+ err := transaction.Commit()
+
+ // assert
+ require.ErrorIs(t, err, assert.AnError)
+}
+
+func TestTransaction_Rollback(t *testing.T) {
+ // arrange
+ var i int
+ transaction := flow.NewTransaction().
+ Then(
+ func() error {
+ i++
+ return nil
+ },
+ func() { i-- },
+ ).
+ Then(
+ func() error {
+ i += 2
+ return nil
+ },
+ func() { i -= 2 },
+ ).
+ Then(
+ func() error {
+ return assert.AnError
+ },
+ func() { i-- },
+ )
+
+ // act
+ err := transaction.Commit()
+ transaction.Rollback()
+
+ // assert
+ require.ErrorIs(t, err, assert.AnError)
+ require.Equal(t, 0, i)
+}
diff --git a/utils/host_test.go b/utils/host_test.go
index d9491587..223c493e 100644
--- a/utils/host_test.go
+++ b/utils/host_test.go
@@ -22,7 +22,7 @@ import (
"path"
"testing"
- . "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/require"
"huawei-csi-driver/utils/log"
)
@@ -57,47 +57,39 @@ func TestChmodFsPermission(t *testing.T) {
}
}()
- Convey("Change target directory to 777 permission", t, func() {
+ t.Run("Change target directory to 777 permission", func(t *testing.T) {
ChmodFsPermission(context.TODO(), targetPath, "777")
fileInfo, err := os.Stat(targetPath)
- if err != nil {
- log.Errorf("Get file/directory [%s] info failed.", targetPath)
- So(err, ShouldBeNil)
- }
+ require.NoError(t, err)
+
filePerm := fileInfo.Mode().Perm()
- So(filePerm, ShouldEqual, os.FileMode(0777))
+ require.Equal(t, os.FileMode(0777), filePerm)
})
- Convey("Change target directory to 555 permission", t, func() {
+ t.Run("Change target directory to 555 permission", func(t *testing.T) {
ChmodFsPermission(context.TODO(), targetPath, "555")
fileInfo, err := os.Stat(targetPath)
- if err != nil {
- log.Errorf("Get file/directory [%s] info failed.", targetPath)
- So(err, ShouldBeNil)
- }
+ require.NoError(t, err)
+
filePerm := fileInfo.Mode().Perm()
- So(filePerm, ShouldEqual, os.FileMode(0555))
+ require.Equal(t, os.FileMode(0555), filePerm)
})
- Convey("Change target directory to 000 permission", t, func() {
+ t.Run("Change target directory to 000 permission", func(t *testing.T) {
ChmodFsPermission(context.TODO(), targetPath, "000")
fileInfo, err := os.Stat(targetPath)
- if err != nil {
- log.Errorf("Get file/directory [%s] info failed.", targetPath)
- So(err, ShouldBeNil)
- }
+ require.NoError(t, err)
+
filePerm := fileInfo.Mode().Perm()
- So(filePerm, ShouldEqual, os.FileMode(0000))
+ require.Equal(t, os.FileMode(0000), filePerm)
})
- Convey("Change target directory to 456 permission", t, func() {
+ t.Run("Change target directory to 456 permission", func(t *testing.T) {
ChmodFsPermission(context.TODO(), targetPath, "456")
fileInfo, err := os.Stat(targetPath)
- if err != nil {
- log.Errorf("Get file/directory [%s] info failed.", targetPath)
- So(err, ShouldBeNil)
- }
+ require.NoError(t, err)
+
filePerm := fileInfo.Mode().Perm()
- So(filePerm, ShouldEqual, os.FileMode(0456))
+ require.Equal(t, os.FileMode(0456), filePerm)
})
}
diff --git a/utils/k8sutils/pvc_helper.go b/utils/k8sutils/pvc_helper.go
index 80568ae6..658578ba 100644
--- a/utils/k8sutils/pvc_helper.go
+++ b/utils/k8sutils/pvc_helper.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. 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.
@@ -40,14 +40,12 @@ const (
eventAdd = "add"
eventUpdate = "update"
eventDelete = "delete"
-)
-
-var (
- uidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
cacheSyncPeriod = 60 * time.Second
)
+var uidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
+
type persistentVolumeClaimOps interface {
// GetVolumeConfiguration returns PVC's volume info
GetVolumeConfiguration(ctx context.Context, pvName string) (map[string]string, error)
diff --git a/utils/log/console.go b/utils/log/console.go
index 2a01bb74..98f37ade 100644
--- a/utils/log/console.go
+++ b/utils/log/console.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -32,7 +32,7 @@ type ConsoleHook struct {
// newConsoleHook creates a new log hook for writing to stdout/stderr.
func newConsoleHook(logFormat logrus.Formatter) (*ConsoleHook, error) {
- return &ConsoleHook{logFormat}, nil
+ return &ConsoleHook{formatter: logFormat}, nil
}
// Levels returns all supported levels
diff --git a/utils/log/file.go b/utils/log/file.go
index ad3ce544..24eef442 100644
--- a/utils/log/file.go
+++ b/utils/log/file.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -28,11 +28,15 @@ import (
"time"
"github.com/sirupsen/logrus"
+
+ "huawei-csi-driver/pkg/constants"
)
const (
- logFilePermission = 0640
- backupTimeFormat = "20060102-150405"
+ logFilePermission = 0640
+ logFileRootDirPermission = 0750
+ rotatedLogFilePermission = 0440
+ backupTimeFormat = "20060102-150405"
)
// FileHook sends log entries to a file.
@@ -52,7 +56,7 @@ func newFileHook(logFilePath, logFileSize string, logFormat logrus.Formatter) (*
logFileRootDir := filepath.Dir(logFilePath)
dir, err := os.Lstat(logFileRootDir)
if os.IsNotExist(err) {
- if err := os.MkdirAll(logFileRootDir, 0750); err != nil {
+ if err := os.MkdirAll(logFileRootDir, logFileRootDirPermission); err != nil {
return nil, fmt.Errorf("could not create log directory %v. %v", logFileRootDir, err)
}
}
@@ -110,8 +114,8 @@ func (hook *FileHook) Fire(entry *logrus.Entry) error {
return nil
}
-// logfileNeedsRotation checks to see if a file has grown too large
-func (hook *FileHook) logfileNeedsRotation() bool {
+// needsRotation checks to see if a file has grown too large
+func (hook *FileHook) needsRotation() bool {
fileInfo, err := hook.logFileHandle.stat()
if err != nil {
return false
@@ -122,11 +126,11 @@ func (hook *FileHook) logfileNeedsRotation() bool {
// maybeDoLogfileRotation check and perform log rotation
func (hook *FileHook) maybeDoLogfileRotation() error {
- if hook.logfileNeedsRotation() {
+ if hook.needsRotation() {
hook.logRotateMutex.Lock()
defer hook.logRotateMutex.Unlock()
- if hook.logfileNeedsRotation() {
+ if hook.needsRotation() {
// Do the rotation.
err := hook.logFileHandle.rotate()
if err != nil {
@@ -168,7 +172,7 @@ func (f *fileHandler) rotate() error {
if err := os.Rename(f.filePath, rotatedLogFileLocation); err != nil {
return fmt.Errorf("failed to create backup file. %s", err)
}
- if err := os.Chmod(rotatedLogFileLocation, 0440); err != nil {
+ if err := os.Chmod(rotatedLogFileLocation, rotatedLogFilePermission); err != nil {
return fmt.Errorf("failed to chmod backup file. %s", err)
}
@@ -222,7 +226,7 @@ func (f *fileHandler) sortedBackupLogFiles() ([]logFileInfo, error) {
continue
}
- logFiles = append(logFiles, logFileInfo{timestamp, f})
+ logFiles = append(logFiles, logFileInfo{timestamp: timestamp, FileInfo: f})
}
sort.Sort(byTimeFormat(logFiles))
@@ -258,12 +262,12 @@ func getNumInByte(logFileSize string) (int64, error) {
// 3.最后一位是数字或者B
// 3.1 若最后一位是数字,则直接返回 若最后一位是B,则获取前面的数字返回
if lastLetter >= "0" && lastLetter <= "9" {
- sum, err = strconv.ParseInt(maxDataNum, 10, 64)
+ sum, err = strconv.ParseInt(maxDataNum, constants.DefaultIntBase, constants.DefaultIntBitSize)
if err != nil {
return 0, err
}
} else {
- sum, err = strconv.ParseInt(maxDataNum[:len(maxDataNum)-1], 10, 64)
+ sum, err = strconv.ParseInt(maxDataNum[:len(maxDataNum)-1], constants.DefaultIntBase, constants.DefaultIntBitSize)
if err != nil {
return 0, err
}
diff --git a/utils/log/logger.go b/utils/log/logger.go
index a1ab4598..c9b0f67d 100644
--- a/utils/log/logger.go
+++ b/utils/log/logger.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. 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.
@@ -23,17 +23,13 @@ import (
"fmt"
"io/ioutil"
"os"
- "sync"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
-var (
- logger LoggingInterface
- testInitLogger sync.Once
-)
+var logger LoggingInterface
type key string
@@ -121,8 +117,8 @@ func parseLogLevel(logLevel string) (logrus.Level, error) {
}
}
-// LoggingRequest use to init the logging service
-type LoggingRequest struct {
+// Config use to init the logging service
+type Config struct {
LogName string
LogFileSize string
LoggingModule string
@@ -135,7 +131,7 @@ var maxBackups uint
// InitLogging configures logging. Logs are written to a log file or stdout/stderr.
// Since logrus doesn't support multiple writers, each log stream is implemented as a hook.
-func InitLogging(req *LoggingRequest) error {
+func InitLogging(req *Config) error {
var tmpLogger loggerImpl
// initialize logrus in wrapper
diff --git a/utils/log/logger_mock.go b/utils/log/logger_mock.go
index bf64500b..d31e2404 100644
--- a/utils/log/logger_mock.go
+++ b/utils/log/logger_mock.go
@@ -34,7 +34,7 @@ var (
// MockInitLogging mock init the logging service
func MockInitLogging(logName string) {
- if err := InitLogging(&LoggingRequest{
+ if err := InitLogging(&Config{
LogName: logName,
LogFileSize: mockLogFileSize,
LoggingModule: mockLoggingModule,
diff --git a/utils/utils_test.go b/utils/utils_test.go
index ff369080..f857062c 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -23,11 +23,12 @@ import (
"reflect"
"testing"
+ "github.com/stretchr/testify/require"
+
"huawei-csi-driver/pkg/constants"
"github.com/agiledragon/gomonkey/v2"
"github.com/prashantv/gostub"
- . "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
@@ -167,39 +168,39 @@ func mockGetSecret(data map[string][]byte, err error) *gomonkey.Patches {
}
func TestGetPasswordFromSecret(t *testing.T) {
- Convey("TestGetPasswordFromSecret secret is nil case", t, func() {
+ t.Run("TestGetPasswordFromSecret secret is nil case", func(t *testing.T) {
m := mockGetSecret(nil, nil)
defer m.Reset()
_, err := GetPasswordFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
- Convey("TestGetPasswordFromSecret get secret error case", t, func() {
+ t.Run("TestGetPasswordFromSecret get secret error case", func(t *testing.T) {
m := mockGetSecret(nil, errors.New("mock error"))
defer m.Reset()
_, err := GetPasswordFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
- Convey("TestGetPasswordFromSecret secret data is nil case", t, func() {
+ t.Run("TestGetPasswordFromSecret secret data is nil case", func(t *testing.T) {
m := mockGetSecret(map[string][]byte{}, nil)
defer m.Reset()
_, err := GetPasswordFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
- Convey("TestGetPasswordFromSecret secret data dose not have password case", t, func() {
+ t.Run("TestGetPasswordFromSecret secret data dose not have password case", func(t *testing.T) {
m := mockGetSecret(map[string][]byte{"user": []byte("mock-user")}, nil)
defer m.Reset()
_, err := GetPasswordFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
- Convey("TestGetPasswordFromSecret normal case", t, func() {
+ t.Run("TestGetPasswordFromSecret normal case", func(t *testing.T) {
m := mockGetSecret(map[string][]byte{
"user": []byte("mock-user"),
"password": []byte("mock-pw"),
@@ -207,47 +208,47 @@ func TestGetPasswordFromSecret(t *testing.T) {
defer m.Reset()
pw, err := GetPasswordFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeNil)
- So(pw, ShouldEqual, "mock-pw")
+ require.NoError(t, err)
+ require.Equal(t, "mock-pw", pw)
})
}
func TestGetCertFromSecretFailed(t *testing.T) {
- Convey("TestGetCertFromSecret secret is nil case", t, func() {
+ t.Run("TestGetCertFromSecret secret is nil case", func(t *testing.T) {
m := mockGetSecret(nil, nil)
defer m.Reset()
_, err := GetCertFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
- Convey("TestGetCertFromSecret get secret error case", t, func() {
+ t.Run("TestGetCertFromSecret get secret error case", func(t *testing.T) {
m := mockGetSecret(nil, errors.New("mock error"))
defer m.Reset()
_, err := GetCertFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
- Convey("GetCertFromSecret secret data dose not have cert case", t, func() {
+ t.Run("GetCertFromSecret secret data dose not have cert case", func(t *testing.T) {
m := mockGetSecret(map[string][]byte{}, nil)
defer m.Reset()
_, err := GetCertFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeError)
+ require.Error(t, err)
})
}
func TestGetCertFromSecretSuccess(t *testing.T) {
- Convey("GetCertFromSecret normal case", t, func() {
+ t.Run("GetCertFromSecret normal case", func(t *testing.T) {
m := mockGetSecret(map[string][]byte{
"tls.crt": []byte("mock-cert"),
}, nil)
defer m.Reset()
pw, err := GetCertFromSecret(context.TODO(), "sec-name", "sec-namespace")
- So(err, ShouldBeNil)
- So(pw, ShouldResemble, []byte("mock-cert"))
+ require.NoError(t, err)
+ require.Equal(t, pw, []byte("mock-cert"))
})
}