diff --git a/Dockerfile b/Dockerfile index e593dcb..70e1955 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # Copyright 2021 Synology Inc. ############## Build stage ############## -FROM golang:1.20.3-alpine as builder +FROM golang:1.20.5-alpine as builder RUN apk add --no-cache alpine-sdk WORKDIR /go/src/synok8scsiplugin @@ -23,7 +23,7 @@ FROM alpine:latest as driver LABEL maintainers="Synology Authors" \ description="Synology CSI Plugin" -RUN <<-EOF +RUN <<-EOF apk add --no-cache \ bash \ blkid \ diff --git a/Makefile b/Makefile index cbd815c..675b43b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ REGISTRY_NAME=ghcr.io/zebernst IMAGE_NAME=synology-csi -IMAGE_VERSION=v1.1.2 +IMAGE_VERSION=v1.1.3 IMAGE_TAG=$(REGISTRY_NAME)/$(IMAGE_NAME):$(IMAGE_VERSION) # For now, only build linux/amd64 platform diff --git a/README.md b/README.md index cd8aa80..6e279c3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ modified for support with [Talos Linux](https://www.talos.dev) Driver Name: csi.san.synology.com | Driver Version | Image | Supported K8s Version | | -------------------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------- | -| [v1.1.2](https://github.com/SynologyOpenSource/synology-csi/tree/release-v1.1.2) | [synology-csi:v1.1.2](https://hub.docker.com/r/synology/synology-csi) | 1.20+ | +| [v1.1.3](https://github.com/SynologyOpenSource/synology-csi/tree/release-v1.1.3) | [synology-csi:v1.1.3](https://hub.docker.com/r/synology/synology-csi) | 1.20+ | @@ -117,9 +117,10 @@ Create and apply StorageClasses with the properties you want. name: synostorage provisioner: csi.san.synology.com parameters: - fsType: 'ext4' + fsType: 'btrfs' dsm: '192.168.1.1' location: '/volume1' + formatOptions: '--nodiscard' reclaimPolicy: Retain allowVolumeExpansion: true ``` diff --git a/charts/synology-csi/Chart.yaml b/charts/synology-csi/Chart.yaml index c689a0c..c7850cb 100644 --- a/charts/synology-csi/Chart.yaml +++ b/charts/synology-csi/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -appVersion: v1.1.2 +appVersion: v1.1.3 name: synology-csi description: A Helm chart for the Synology CSI Driver keywords: @@ -7,6 +7,6 @@ keywords: - CSI Driver kubeVersion: ">= 1.20.0" sources: - - https://github.com/christian-schlichtherle/synology-csi-chart/tree/main + - https://github.com/zebernst/synology-csi-talos/tree/main - https://github.com/SynologyOpenSource/synology-csi/tree/main -version: 0.9.3 +version: 0.9.4 diff --git a/charts/synology-csi/templates/node.yaml b/charts/synology-csi/templates/node.yaml index 7633ceb..f99fa64 100644 --- a/charts/synology-csi/templates/node.yaml +++ b/charts/synology-csi/templates/node.yaml @@ -83,7 +83,7 @@ spec: fieldRef: fieldPath: spec.nodeName - name: REGISTRATION_PATH - value: /var/lib/kubelet/plugins/csi.san.synology.com/csi.sock + value: {{ $.Values.node.kubeletPath }}/plugins/csi.san.synology.com/csi.sock {{- with .nodeDriverRegistrar }} image: {{ .image }}:{{ .tag }} imagePullPolicy: {{ .pullPolicy }} @@ -123,7 +123,7 @@ spec: - name: host-root mountPath: /host - name: kubelet-dir - mountPath: /var/lib/kubelet + mountPath: {{ $.Values.node.kubeletPath }} mountPropagation: Bidirectional - name: plugin-dir mountPath: /csi @@ -148,15 +148,15 @@ spec: type: Directory - name: kubelet-dir hostPath: - path: /var/lib/kubelet + path: {{ .kubeletPath }} type: Directory - name: plugin-dir hostPath: - path: /var/lib/kubelet/plugins/csi.san.synology.com + path: {{ .kubeletPath }}/plugins/csi.san.synology.com type: DirectoryOrCreate - name: registration-dir hostPath: - path: /var/lib/kubelet/plugins_registry + path: {{ .kubeletPath }}/plugins_registry type: Directory {{- end }} {{- end }} diff --git a/charts/synology-csi/templates/test.yaml b/charts/synology-csi/templates/test.yaml index a0fe7ba..0b369ac 100644 --- a/charts/synology-csi/templates/test.yaml +++ b/charts/synology-csi/templates/test.yaml @@ -10,7 +10,7 @@ metadata: spec: accessModes: - ReadWriteOnce - storageClassName: {{ include "synology-csi.fullname" $ }}-delete + storageClassName: synology-iscsi-storage-delete resources: requests: storage: 1Gi diff --git a/charts/synology-csi/values.yaml b/charts/synology-csi/values.yaml index bea80df..37a0875 100644 --- a/charts/synology-csi/values.yaml +++ b/charts/synology-csi/values.yaml @@ -55,6 +55,9 @@ node: affinity: { } nodeSelector: { } tolerations: [ ] + # If your kubelet path is not standard, specify it here : + ## example for miocrok8s distrib : /var/snap/microk8s/common/var/lib/kubelet + kubeletPath: /var/lib/kubelet # Specifies affinity, nodeSelector and tolerations for the snapshotter StatefulSet snapshotter: affinity: { } diff --git a/deploy/kubernetes/v1.19/controller.yml b/deploy/kubernetes/v1.19/controller.yml index 2c4fab8..e0af4ad 100644 --- a/deploy/kubernetes/v1.19/controller.yml +++ b/deploy/kubernetes/v1.19/controller.yml @@ -96,6 +96,7 @@ spec: - --timeout=60s - --csi-address=$(ADDRESS) - --v=5 + - --extra-create-metadata env: - name: ADDRESS value: /var/lib/csi/sockets/pluginproxy/csi.sock @@ -143,7 +144,7 @@ spec: capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true - image: synology/synology-csi:v1.1.2 + image: synology/synology-csi:v1.1.3 args: - --nodeid=NotUsed - --endpoint=$(CSI_ENDPOINT) diff --git a/deploy/kubernetes/v1.19/node.yml b/deploy/kubernetes/v1.19/node.yml index b1c3069..6c41392 100644 --- a/deploy/kubernetes/v1.19/node.yml +++ b/deploy/kubernetes/v1.19/node.yml @@ -86,7 +86,7 @@ spec: securityContext: privileged: true imagePullPolicy: IfNotPresent - image: synology/synology-csi:v1.1.2 + image: synology/synology-csi:v1.1.3 args: - --nodeid=$(KUBE_NODE_NAME) - --endpoint=$(CSI_ENDPOINT) diff --git a/deploy/kubernetes/v1.19/snapshotter/snapshotter.yaml b/deploy/kubernetes/v1.19/snapshotter/snapshotter.yaml index 19d568a..f219d86 100644 --- a/deploy/kubernetes/v1.19/snapshotter/snapshotter.yaml +++ b/deploy/kubernetes/v1.19/snapshotter/snapshotter.yaml @@ -81,7 +81,7 @@ spec: capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true - image: synology/synology-csi:v1.1.2 + image: synology/synology-csi:v1.1.3 args: - --nodeid=NotUsed - --endpoint=$(CSI_ENDPOINT) diff --git a/deploy/kubernetes/v1.20/controller.yml b/deploy/kubernetes/v1.20/controller.yml index bf65222..9e2d61c 100644 --- a/deploy/kubernetes/v1.20/controller.yml +++ b/deploy/kubernetes/v1.20/controller.yml @@ -96,6 +96,7 @@ spec: - --timeout=60s - --csi-address=$(ADDRESS) - --v=5 + - --extra-create-metadata env: - name: ADDRESS value: /var/lib/csi/sockets/pluginproxy/csi.sock @@ -143,7 +144,7 @@ spec: capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true - image: ghcr.io/zebernst/synology-csi:v1.1.2 + image: ghcr.io/zebernst/synology-csi:v1.1.3 args: - --nodeid=NotUsed - --endpoint=$(CSI_ENDPOINT) diff --git a/deploy/kubernetes/v1.20/node.yml b/deploy/kubernetes/v1.20/node.yml index 43614d5..738a031 100644 --- a/deploy/kubernetes/v1.20/node.yml +++ b/deploy/kubernetes/v1.20/node.yml @@ -87,7 +87,7 @@ spec: securityContext: privileged: true imagePullPolicy: IfNotPresent - image: ghcr.io/zebernst/synology-csi:v1.1.2 + image: ghcr.io/zebernst/synology-csi:v1.1.3 args: - --nodeid=$(KUBE_NODE_NAME) - --endpoint=$(CSI_ENDPOINT) diff --git a/deploy/kubernetes/v1.20/snapshotter/snapshotter.yaml b/deploy/kubernetes/v1.20/snapshotter/snapshotter.yaml index bc107ca..ea11293 100644 --- a/deploy/kubernetes/v1.20/snapshotter/snapshotter.yaml +++ b/deploy/kubernetes/v1.20/snapshotter/snapshotter.yaml @@ -81,7 +81,7 @@ spec: capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true - image: ghcr.io/zebernst/synology-csi:v1.1.2 + image: ghcr.io/zebernst/synology-csi:v1.1.3 args: - --nodeid=NotUsed - --endpoint=$(CSI_ENDPOINT) diff --git a/go.mod b/go.mod index 916c51b..69d8ef7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/kubernetes-csi/csi-test/v4 v4.3.0 github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.1.3 + golang.org/x/sys v0.5.0 google.golang.org/grpc v1.34.0 google.golang.org/protobuf v1.25.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/pkg/driver/controllerserver.go b/pkg/driver/controllerserver.go index 1811296..a60ffd5 100644 --- a/pkg/driver/controllerserver.go +++ b/pkg/driver/controllerserver.go @@ -132,10 +132,20 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol // used only in NodeStageVolume though VolumeContext formatOptions := params["formatOptions"] + lunDescription := "" + if _, ok := params["csi.storage.k8s.io/pvc/name"]; ok { + // if the /pvc/name is present, the namespace is present too + // as these parameters are reserved by external-provisioner + pvcNamespace := params["csi.storage.k8s.io/pvc/namespace"] + pvcName := params["csi.storage.k8s.io/pvc/name"] + lunDescription = pvcNamespace + "/" + pvcName + } + spec := &models.CreateK8sVolumeSpec{ DsmIp: params["dsm"], K8sVolumeName: volName, LunName: models.GenLunName(volName), + LunDescription: lunDescription, ShareName: models.GenShareName(volName), Location: params["location"], Size: sizeInByte, diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index 713e513..1859b3a 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -25,7 +25,7 @@ import ( const ( DriverName = "csi.san.synology.com" // CSI dirver name - DriverVersion = "1.1.2" + DriverVersion = "1.1.3" ) var ( @@ -79,8 +79,8 @@ func NewControllerAndNodeDriver(nodeID string, endpoint string, dsmService inter d.addNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{ csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME, csi.NodeServiceCapability_RPC_EXPAND_VOLUME, + csi.NodeServiceCapability_RPC_GET_VOLUME_STATS, // csi.NodeServiceCapability_RPC_VOLUME_MOUNT_GROUP, - // csi.NodeServiceCapability_RPC_GET_VOLUME_STATS, //TODO }) log.Infof("New driver created: name=%s, nodeID=%s, version=%s, endpoint=%s", d.name, d.nodeID, d.version, d.endpoint) @@ -138,4 +138,4 @@ func (d *Driver) getVolumeCapabilityAccessModes() []*csi.VolumeCapability_Access func isProtocolSupport(protocol string) bool { return utils.SliceContains(supportedProtocolList, protocol) -} \ No newline at end of file +} diff --git a/pkg/driver/initiator.go b/pkg/driver/initiator.go index 5b3d142..2bbd147 100644 --- a/pkg/driver/initiator.go +++ b/pkg/driver/initiator.go @@ -29,7 +29,7 @@ import ( ) type initiatorDriver struct { - chapUser string + chapUser string chapPassword string } @@ -123,6 +123,21 @@ func iscsiadm_login(iqn, portal string) error { return nil } +func iscsiadm_update_node_startup(iqn, portal string) error { + cmd := iscsiadm( + "-m", "node", + "--targetname", iqn, + "--portal", portal, + "--op", "update", + "--name", "node.startup", + "--value", "manual") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s (%v)", string(out), err) + } + return nil +} + func iscsiadm_logout(iqn string) error { cmd := iscsiadm( "-m", "node", @@ -169,8 +184,8 @@ func listSessionsByIqn(targetIqn string) (matchedSessions []iscsiSession) { return matchedSessions } -func (d *initiatorDriver) login(targetIqn string, portal string) error{ - if (hasSession(targetIqn, portal)) { +func (d *initiatorDriver) login(targetIqn string, portal string) error { + if hasSession(targetIqn, portal) { log.Infof("Session[%s] already exists.", targetIqn) return nil } @@ -185,13 +200,17 @@ func (d *initiatorDriver) login(targetIqn string, portal string) error{ return err } + if err := iscsiadm_update_node_startup(targetIqn, portal); err != nil { + log.Warnf("Failed to update target node.startup to manual: %v", err) + } + log.Infof("Login target portal [%s], iqn [%s].", portal, targetIqn) return nil } -func (d *initiatorDriver) logout(targetIqn string, ip string) error{ - if (!hasSession(targetIqn, "")) { +func (d *initiatorDriver) logout(targetIqn string, ip string) error { + if !hasSession(targetIqn, "") { log.Infof("Session[%s] doesn't exist.", targetIqn) return nil } @@ -208,8 +227,8 @@ func (d *initiatorDriver) logout(targetIqn string, ip string) error{ return nil } -func (d *initiatorDriver) rescan(targetIqn string) error{ - if (!hasSession(targetIqn, "")) { +func (d *initiatorDriver) rescan(targetIqn string) error { + if !hasSession(targetIqn, "") { msg := fmt.Sprintf("Session[%s] doesn't exist.", targetIqn) log.Error(msg) return errors.New(msg) diff --git a/pkg/driver/nodeserver.go b/pkg/driver/nodeserver.go index 9b9678f..6ec97e7 100644 --- a/pkg/driver/nodeserver.go +++ b/pkg/driver/nodeserver.go @@ -29,6 +29,7 @@ import ( log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "golang.org/x/sys/unix" "k8s.io/mount-utils" "github.com/SynologyOpenSource/synology-csi/pkg/dsm/webapi" @@ -584,14 +585,39 @@ func (ns *nodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVo }, nil } - lun := k8sVolume.Lun + // If we are dealing with a LUN use statfs + statfs := &unix.Statfs_t{} + err = unix.Statfs(volumePath, statfs) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get fs info on path %s: %v", req.VolumePath, err) + } + + // Available is blocks available * fragment size + available := int64(statfs.Bavail) * int64(statfs.Bsize) + + // Capacity is total block count * fragment size + capacity := int64(statfs.Blocks) * int64(statfs.Bsize) + + // Usage is block being used * fragment size (aka block size). + usage := (int64(statfs.Blocks) - int64(statfs.Bfree)) * int64(statfs.Bsize) + + inodes := int64(statfs.Files) + inodesFree := int64(statfs.Ffree) + inodesUsed := inodes - inodesFree + return &csi.NodeGetVolumeStatsResponse{ Usage: []*csi.VolumeUsage{ - &csi.VolumeUsage{ - Available: int64(lun.Size - lun.Used), - Total: int64(lun.Size), - Used: int64(lun.Used), + { Unit: csi.VolumeUsage_BYTES, + Available: available, + Total: capacity, + Used: usage, + }, + { + Unit: csi.VolumeUsage_INODES, + Available: inodesFree, + Total: inodes, + Used: inodesUsed, }, }, }, nil diff --git a/pkg/dsm/service/dsm.go b/pkg/dsm/service/dsm.go index e8305c7..122a545 100644 --- a/pkg/dsm/service/dsm.go +++ b/pkg/dsm/service/dsm.go @@ -168,7 +168,7 @@ func (service *DsmService) createMappingTarget(dsm *webapi.DSM, spec *models.Cre } targetSpec := webapi.TargetCreateSpec{ Name: spec.TargetName, - Iqn: genTargetIqn(), + Iqn: genTargetIqn(), } log.Debugf("TargetCreate spec: %v", targetSpec) @@ -224,10 +224,11 @@ func (service *DsmService) createVolumeByDsm(dsm *webapi.DSM, spec *models.Creat // 3. Create LUN lunSpec := webapi.LunCreateSpec{ - Name: spec.LunName, - Location: spec.Location, - Size: spec.Size, - Type: lunType, + Name: spec.LunName, + Description: spec.LunDescription, + Location: spec.Location, + Size: spec.Size, + Type: lunType, } log.Debugf("LunCreate spec: %v", lunSpec) diff --git a/pkg/dsm/webapi/dsmwebapi.go b/pkg/dsm/webapi/dsmwebapi.go index 1fa3fda..2661da4 100644 --- a/pkg/dsm/webapi/dsmwebapi.go +++ b/pkg/dsm/webapi/dsmwebapi.go @@ -9,20 +9,21 @@ import ( "encoding/json" "fmt" "io/ioutil" - log "github.com/sirupsen/logrus" "net/http" "net/url" "regexp" + "github.com/SynologyOpenSource/synology-csi/pkg/logger" + log "github.com/sirupsen/logrus" ) type DSM struct { - Ip string - Port int - Username string - Password string - Sid string - Https bool + Ip string + Port int + Username string + Password string + Sid string + Https bool Controller string //new } @@ -44,13 +45,13 @@ type Response struct { func (dsm *DSM) sendRequest(data string, apiTemplate interface{}, params url.Values, cgiPath string) (Response, error) { resp, err := dsm.sendRequestWithoutConnectionCheck(data, apiTemplate, params, cgiPath) - if err != nil && (resp.ErrorCode == 105 || resp.ErrorCode == 119) { // 105: WEBAPI_ERR_NO_PERMISSION, 119: WEBAPI_ERR_SID_NOT_FOUND + if err != nil && (resp.ErrorCode == 105 || resp.ErrorCode == 106 || resp.ErrorCode == 119) { // 105: WEBAPI_ERR_NO_PERMISSION, 106: session timeout, 119: WEBAPI_ERR_SID_NOT_FOUND // Re-login if err := dsm.Login(); err != nil { return Response{}, fmt.Errorf("Failed to re-login to DSM: [%s]. err: %v", dsm.Ip, err) } log.Info("Re-login succeeded.") - return dsm.sendRequestWithoutConnectionCheck(data, apiTemplate, params, cgiPath); + return dsm.sendRequestWithoutConnectionCheck(data, apiTemplate, params, cgiPath) } return resp, err diff --git a/pkg/dsm/webapi/iscsi.go b/pkg/dsm/webapi/iscsi.go index 178b6f1..6520111 100644 --- a/pkg/dsm/webapi/iscsi.go +++ b/pkg/dsm/webapi/iscsi.go @@ -67,11 +67,12 @@ type LunDevAttrib struct { } type LunCreateSpec struct { - Name string - Location string - Size int64 - Type string - DevAttribs []LunDevAttrib + Name string + Description string + Location string + Size int64 + Type string + DevAttribs []LunDevAttrib } type LunUpdateSpec struct { @@ -165,6 +166,7 @@ func (dsm *DSM) LunCreate(spec LunCreateSpec) (string, error) { params.Add("size", strconv.FormatInt(int64(spec.Size), 10)) params.Add("type", spec.Type) params.Add("location", spec.Location) + params.Add("description", spec.Description) js, err := json.Marshal(spec.DevAttribs) if err != nil { @@ -497,4 +499,4 @@ func (dsm *DSM) SnapshotClone(spec SnapshotCloneSpec) (string, error) { } return snapshotCloneResp.Uuid, nil -} \ No newline at end of file +} diff --git a/pkg/models/dsm_req_spec.go b/pkg/models/dsm_req_spec.go index 8055735..3ef0571 100644 --- a/pkg/models/dsm_req_spec.go +++ b/pkg/models/dsm_req_spec.go @@ -12,6 +12,7 @@ type CreateK8sVolumeSpec struct { DsmIp string K8sVolumeName string LunName string + LunDescription string ShareName string Location string Size int64