Merge pull request #24 from democratic-csi/next

version auto-detection, delete race conditions, sudo, apiKey, multipath
This commit is contained in:
Travis Glenn Hansen 2020-12-03 16:45:31 -07:00 committed by GitHub
commit 148c8d9ac0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2040 additions and 915 deletions

View File

@ -1,4 +1,7 @@
chart
dev
examples
contrib
node_modules
Dockerfile*
TODO.md

View File

@ -13,6 +13,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.6.0
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v2
- name: docker build
run: |

View File

@ -1,16 +1,22 @@
FROM debian:10-slim
FROM debian:10-slim AS build
#FROM --platform=$BUILDPLATFORM debian:10-slim AS build
ENV DEBIAN_FRONTEND=noninteractive
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running build on $BUILDPLATFORM, building for $TARGETPLATFORM"
RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG=en_US.utf8 NODE_VERSION=v12.15.0
ENV LANG=en_US.utf8
ENV NODE_VERSION=v12.20.0
#ENV NODE_VERSION=v14.15.1
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM"
# install build deps
RUN apt-get update && apt-get install -y python make gcc g++
# install node
RUN apt-get update && apt-get install -y wget xz-utils
@ -18,26 +24,6 @@ ADD docker/node-installer.sh /usr/local/sbin
RUN chmod +x /usr/local/sbin/node-installer.sh && node-installer.sh
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
# node service requirements
RUN apt-get update && \
apt-get install -y xfsprogs fatresize dosfstools open-iscsi lsscsi sg3-utils multipath-tools scsitools nfs-common cifs-utils sudo && \
rm -rf /var/lib/apt/lists/*
# controller requirements
RUN apt-get update && \
apt-get install -y ansible && \
rm -rf /var/lib/apt/lists/*
# npm requirements
# gcc and g++ required by grpc-usd until proper upstream support
RUN apt-get update && \
apt-get install -y python make gcc g++ && \
rm -rf /var/lib/apt/lists/*
# install wrappers
ADD docker/iscsiadm /usr/local/sbin
RUN chmod +x /usr/local/sbin/iscsiadm
# Run as a non-root user
RUN useradd --create-home csi \
&& mkdir /home/csi/app \
@ -47,15 +33,65 @@ USER csi
COPY package*.json ./
RUN npm install
COPY --chown=csi:csi . .
RUN rm -rf docker
USER root
# remove build deps
######################
# actual image
######################
FROM debian:10-slim
ENV DEBIAN_FRONTEND=noninteractive
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on final $BUILDPLATFORM, building for $TARGETPLATFORM"
RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG=en_US.utf8
# install node
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
COPY --from=build /usr/local/lib/nodejs /usr/local/lib/nodejs
# node service requirements
# netbase is required by rpcbind/rpcinfo to work properly
# /etc/{services,rpc} are required
RUN apt-get update && \
apt-get install -y netbase socat e2fsprogs xfsprogs fatresize dosfstools nfs-common cifs-utils sudo && \
rm -rf /var/lib/apt/lists/*
# controller requirements
#RUN apt-get update && \
# apt-get purge -y python make gcc g++ && \
# apt-get install -y ansible && \
# rm -rf /var/lib/apt/lists/*
# install wrappers
ADD docker/iscsiadm /usr/local/sbin
RUN chmod +x /usr/local/sbin/iscsiadm
ADD docker/multipath /usr/local/sbin
RUN chmod +x /usr/local/sbin/multipath
## USE_HOST_MOUNT_TOOLS=1
ADD docker/mount /usr/local/bin/mount
RUN chmod +x /usr/local/bin/mount
## USE_HOST_MOUNT_TOOLS=1
ADD docker/umount /usr/local/bin/umount
RUN chmod +x /usr/local/bin/umount
# Run as a non-root user
RUN useradd --create-home csi \
&& chown -R csi: /home/csi
COPY --from=build --chown=csi:csi /home/csi/app /home/csi/app
WORKDIR /home/csi/app
EXPOSE 50051
ENTRYPOINT [ "bin/democratic-csi" ]

59
Dockerfile.unified Normal file
View File

@ -0,0 +1,59 @@
FROM debian:10-slim
ENV DEBIAN_FRONTEND=noninteractive
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \
&& localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG=en_US.utf8 NODE_VERSION=v12.20.0
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM"
# install node
RUN apt-get update && apt-get install -y wget xz-utils
ADD docker/node-installer.sh /usr/local/sbin
RUN chmod +x /usr/local/sbin/node-installer.sh && node-installer.sh
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
# node service requirements
RUN apt-get update && \
apt-get install -y e2fsprogs xfsprogs fatresize dosfstools nfs-common cifs-utils sudo && \
rm -rf /var/lib/apt/lists/*
# controller requirements
RUN apt-get update && \
apt-get install -y ansible && \
rm -rf /var/lib/apt/lists/*
# npm requirements
# gcc and g++ required by grpc-usd until proper upstream support
RUN apt-get update && \
apt-get install -y python make gcc g++ && \
rm -rf /var/lib/apt/lists/*
# install wrappers
ADD docker/iscsiadm /usr/local/sbin
RUN chmod +x /usr/local/sbin/iscsiadm
ADD docker/multipath /usr/local/sbin
RUN chmod +x /usr/local/sbin/multipath
# Run as a non-root user
RUN useradd --create-home csi \
&& mkdir /home/csi/app \
&& chown -R csi: /home/csi
WORKDIR /home/csi/app
USER csi
COPY package*.json ./
RUN npm install
COPY --chown=csi:csi . .
USER root
EXPOSE 50051
ENTRYPOINT [ "bin/democratic-csi" ]

View File

@ -40,6 +40,9 @@ You should install/configure the requirements for both nfs and iscsi.
Follow the instructions here: https://netapp-trident.readthedocs.io/en/stable-v20.04/kubernetes/operations/tasks/worker.html
Note that `multipath` is supported for the `iscsi`-based drivers. Simply setup
multipath to your liking and set multiple portals in the config as appropriate.
If you are running Kubernetes with rancher/rke please see the following:
- https://github.com/rancher/rke/issues/1846
@ -71,14 +74,43 @@ Server preparation depends slightly on which `driver` you are using.
### FreeNAS (freenas-nfs, freenas-iscsi, freenas-smb)
The recommended version of FreeNAS is 11.3+, however the driver should work
with much older versions as well.
Ensure the following services are configurged and running:
- ssh (if you use a password for authentication make sure it is allowed)
- ensure `zsh`, `bash`, or `sh` is set as the root shell, `csh` gives false errors due to quoting
- nfs
- iscsi
- when using the FreeNAS API concurrently the `/etc/ctl.conf` file on the
server can become invalid, some sample scripts are provided in the
`contrib` directory to clean things up
ie: copy the script to the server and directly and run - `./ctld-config-watchdog-db.sh | logger -t ctld-config-watchdog-db.sh &`
please read the scripts and set the variables as appropriate for your server.
- ensure you have pre-emptively created portal, group, auth
- smb
In addition, if you want to use a non-root user for the ssh operations you may
create a `csi` user and then run `visudo` directly from the console. Make sure
the line for the `csi` user has `NOPASSWD` added (note this can get reset by
FreeNAS if you alter the user via the GUI later):
```
csi ALL=(ALL) NOPASSWD:ALL
```
Starting with TrueNAS CORE 12 it is also possible to use an `apiKey` instead of
the `root` password for the http connection.
Issues to review:
- https://jira.ixsystems.com/browse/NAS-108519
- https://jira.ixsystems.com/browse/NAS-108520
- https://jira.ixsystems.com/browse/NAS-108521
- https://jira.ixsystems.com/browse/NAS-108522
- https://jira.ixsystems.com/browse/NAS-107219
### ZoL (zfs-generic-nfs, zfs-generic-iscsi)
Ensure ssh and zfs is installed on the server and that you have installed
@ -115,9 +147,10 @@ helm upgrade \
--namespace democratic-csi \
zfs-nfs democratic-csi/democratic-csi
```
### A note on non standard kubelet paths
Some distrobutions, such as `minikube` and `microk8s` uses a non-standard kubelet path.
Some distrobutions, such as `minikube` and `microk8s` uses a non-standard kubelet path.
In such cases it is necessary to provide a new kubelet host path, microk8s example below:
```bash

View File

@ -0,0 +1,54 @@
#!/bin/bash
WAIT_TIME_SECS=30
USERNAME="root"
PASSWORD="secret"
BASE_URL="http://localhost"
LIMIT=1000
while [ 1 ]; do
sleep "${WAIT_TIME_SECS}"
# ctl extents
CTL_EXTENT_COUNT=$(ctladm devlist | tail -n +2 | wc -l | sed 's/^[ \t]*//;s/[ \t]*$//')
echo "ctl extent count: ${CTL_EXTENT_COUNT}"
# ctl luns
CTL_LUN_COUNT=$(ctladm lunlist | wc -l | sed 's/^[ \t]*//;s/[ \t]*$//')
echo "ctl lun count: ${CTL_LUN_COUNT}"
# db targets
DB_TARGET_COUNT=$(curl --user "${USERNAME}:${PASSWORD}" "${BASE_URL}/api/v2.0/iscsi/target?limit=${LIMIT}" 2>/dev/null | jq length)
echo "DB target count: ${DB_TARGET_COUNT}"
# db extents
DB_EXTENT_COUNT=$(curl --user "${USERNAME}:${PASSWORD}" "${BASE_URL}/api/v2.0/iscsi/extent?limit=${LIMIT}" 2>/dev/null | jq length)
echo "DB extent count: ${DB_EXTENT_COUNT}"
# db luns
DB_LUN_COUNT=$(curl --user "${USERNAME}:${PASSWORD}" "${BASE_URL}/api/v2.0/iscsi/targetextent?limit=${LIMIT}" 2>/dev/null | jq length)
echo "DB lun count: ${DB_LUN_COUNT}"
REGEN=0
if [[ ${CTL_TARGET_COUNT} -ne ${DB_TARGET_COUNT} ]]; then
REGEN=1
fi
if [[ ${CTL_EXTENT_COUNT} -ne ${DB_EXTENT_COUNT} ]]; then
REGEN=1
fi
if [[ ${CTL_LUN_COUNT} -ne ${DB_LUN_COUNT} ]]; then
REGEN=1
fi
if [[ ${REGEN} -eq 1 ]]; then
echo "regen ctld config"
midclt call etc.generate ctld &>/dev/null
echo "reload ctld service"
/etc/rc.d/ctld reload &>/dev/null
fi
done

View File

@ -0,0 +1,19 @@
#!/bin/bash
# under certain circumstances high concurrency requests to the FreeNAS/TrueNAS
# API can result in an invalid /etc/ctl.conf written to disk
# this script attempts to mitigate those failures by forcing a rebuild of the
# file using info strictly from the sqlite DB
# can test with this
# logger -t ctld "error in configuration file"
while [ 1 ]; do
egrep -m 1 "ctld.*error in configuration file" <(tail -n 0 -F /var/log/messages) &>/dev/null
echo "regen ctld config"
midclt call etc.generate ctld &>/dev/null
echo "reload ctld service"
/etc/rc.d/ctld reload &>/dev/null
done

View File

@ -0,0 +1,16 @@
#!/bin/bash
# watch the ctld pid file and ensure the service is actually running
while [ 1 ]; do
sleep 5
ps -p $(cat /var/run/ctld.pid) | grep ctld &>/dev/null || {
echo "ctld not running, restarting"
echo "regen ctld config"
midclt call etc.generate ctld &>/dev/null
echo "restart ctld service"
/etc/rc.d/ctld restart &>/dev/null
}
done

7
docker/mount Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if [[ ${USE_HOST_MOUNT_TOOLS} -eq 1 ]];then
chroot /host /usr/bin/env -i PATH="/sbin:/bin:/usr/bin:/usr/sbin" mount "${@:1}"
else
/usr/bin/env -i PATH="/sbin:/bin:/usr/bin:/usr/sbin" mount "${@:1}"
fi

3
docker/multipath Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
chroot /host /usr/bin/env -i PATH="/sbin:/bin:/usr/sbin:/usr/bin" multipath "${@:1}"

7
docker/umount Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if [[ ${USE_HOST_MOUNT_TOOLS} -eq 1 ]];then
chroot /host /usr/bin/env -i PATH="/sbin:/bin:/usr/bin:/usr/sbin" umount "${@:1}"
else
/usr/bin/env -i PATH="/sbin:/bin:/usr/bin:/usr/sbin" umount "${@:1}"
fi

View File

@ -4,9 +4,16 @@ httpConnection:
protocol: http
host: server address
port: 80
# use only 1 of apiKey or username/password
# if both are present, apiKey is preferred
# apiKey is only available starting in TrueNAS-12
#apiKey:
username: root
password:
allowInsecure: true
# use apiVersion 2 for TrueNAS-12 and up (will work on 11.x in some scenarios as well)
# leave unset for auto-detection
#apiVersion: 2
sshConnection:
host: server address
port: 22
@ -21,6 +28,9 @@ zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# sudoEnabled: true
#
# leave paths unset for auto-detection
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool

View File

@ -4,9 +4,16 @@ httpConnection:
protocol: http
host: server address
port: 80
# use only 1 of apiKey or username/password
# if both are present, apiKey is preferred
# apiKey is only available starting in TrueNAS-12
#apiKey:
username: root
password:
allowInsecure: true
# use apiVersion 2 for TrueNAS-12 and up (will work on 11.x in some scenarios as well)
# leave unset for auto-detection
#apiVersion: 2
sshConnection:
host: server address
port: 22
@ -21,6 +28,9 @@ zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# sudoEnabled: true
#
# leave paths unset for auto-detection
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool

View File

@ -4,9 +4,16 @@ httpConnection:
protocol: http
host: server address
port: 80
# use only 1 of apiKey or username/password
# if both are present, apiKey is preferred
# apiKey is only available starting in TrueNAS-12
#apiKey:
username: root
password:
allowInsecure: true
# use apiVersion 2 for TrueNAS-12 and up (will work on 11.x in some scenarios as well)
# leave unset for auto-detection
#apiVersion: 2
sshConnection:
host: server address
port: 22
@ -21,6 +28,9 @@ zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# sudoEnabled: true
#
# leave paths unset for auto-detection
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool

View File

@ -17,6 +17,7 @@ zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# sudoEnabled: true
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool
@ -50,7 +51,8 @@ iscsi:
# http://www.linux-iscsi.org/wiki/ISCSI
# https://bugzilla.redhat.com/show_bug.cgi?id=1659195
# http://atodorov.org/blog/2015/04/07/how-to-configure-iscsi-target-on-red-hat-enterprise-linux-7/
shareStragetyTargetCli:
shareStrategyTargetCli:
#sudoEnabled: true
basename: "iqn.2003-01.org.linux-iscsi.ubuntu-19.x8664"
tpg:
attributes:

View File

@ -17,6 +17,7 @@ zfs:
# can be used to override defaults if necessary
# the example below is useful for TrueNAS 12
#cli:
# sudoEnabled: true
# paths:
# zfs: /usr/local/sbin/zfs
# zpool: /usr/local/sbin/zpool

227
package-lock.json generated
View File

@ -50,9 +50,9 @@
}
},
"@eslint/eslintrc": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz",
"integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==",
"requires": {
"ajv": "^6.12.4",
"debug": "^4.1.1",
@ -67,9 +67,9 @@
},
"dependencies": {
"ajv": {
"version": "6.12.4",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -169,11 +169,6 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
},
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
},
"@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
@ -185,14 +180,14 @@
"integrity": "sha512-dJ9vXxJ8MEwzNn4GkoAGauejhXoKuJyYKegsA6Af25ZpEDXomeVXt5HUWUNVHk5UN7+U0f6ghC6otwt+7PdSDg=="
},
"acorn": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
},
"acorn-jsx": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
"integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ=="
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng=="
},
"ajv": {
"version": "6.12.3",
@ -324,11 +319,6 @@
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@ -344,11 +334,10 @@
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
@ -381,13 +370,13 @@
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
"wrap-ansi": "^7.0.0"
},
"dependencies": {
"emoji-regex": {
@ -504,11 +493,11 @@
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"requires": {
"ms": "^2.1.1"
"ms": "2.1.2"
}
},
"decamelize": {
@ -570,27 +559,32 @@
"ansi-colors": "^4.1.1"
}
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"eslint": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz",
"integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==",
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz",
"integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==",
"requires": {
"@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.1.3",
"@eslint/eslintrc": "^0.2.1",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"enquirer": "^2.3.5",
"eslint-scope": "^5.1.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^1.3.0",
"eslint-visitor-keys": "^2.0.0",
"espree": "^7.3.0",
"esquery": "^1.2.0",
"esutils": "^2.0.2",
@ -620,11 +614,11 @@
}
},
"eslint-scope": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz",
"integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"requires": {
"esrecurse": "^4.1.0",
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
}
},
@ -634,12 +628,19 @@
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
"requires": {
"eslint-visitor-keys": "^1.1.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
}
}
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
"integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ=="
},
"espree": {
"version": "7.3.0",
@ -649,6 +650,13 @@
"acorn": "^7.4.0",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.3.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
}
}
},
"esprima": {
@ -739,15 +747,6 @@
"flat-cache": "^2.0.1"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@ -903,9 +902,9 @@
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
"integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@ -1055,14 +1054,6 @@
"type-check": "~0.4.0"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"requires": {
"p-locate": "^4.1.0"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
@ -1232,27 +1223,6 @@
"lcid": "^1.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"requires": {
"p-limit": "^2.2.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -1261,11 +1231,6 @@
"callsites": "^3.0.0"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -1452,11 +1417,6 @@
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@ -1507,11 +1467,6 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -1732,14 +1687,14 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"uuid": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
"integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q=="
},
"verror": {
"version": "1.10.0",
@ -1759,11 +1714,6 @@
"isexe": "^2.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"window-size": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
@ -1834,9 +1784,9 @@
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@ -1844,11 +1794,10 @@
},
"dependencies": {
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
@ -1901,9 +1850,9 @@
}
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
"integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg=="
},
"yallist": {
"version": "4.0.0",
@ -1911,21 +1860,17 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"version": "16.1.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.1.tgz",
"integrity": "sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w==",
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"dependencies": {
"emoji-regex": {
@ -1951,13 +1896,9 @@
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
"version": "20.2.4",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
"integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA=="
}
}
}

View File

@ -20,7 +20,7 @@
"dependencies": {
"@grpc/proto-loader": "^0.5.5",
"bunyan": "^1.8.14",
"eslint": "^7.8.1",
"eslint": "^7.14.0",
"grpc-uds": "^0.1.4",
"handlebars": "^4.7.6",
"js-yaml": "^3.14.0",
@ -28,8 +28,8 @@
"request": "^2.88.2",
"ssh2": "^0.8.9",
"uri-js": "^4.4.0",
"uuid": "^8.3.0",
"uuid": "^8.3.1",
"winston": "^3.3.3",
"yargs": "^15.4.1"
"yargs": "^16.1.1"
}
}

View File

@ -26,7 +26,7 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver {
* @param {*} datasetName
*/
async createShare(call, datasetName) {
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
const sshClient = this.getSshClient();
let properties;
@ -105,25 +105,25 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver {
switch (this.options.iscsi.shareStrategy) {
case "targetCli":
basename = this.options.iscsi.shareStragetyTargetCli.basename;
basename = this.options.iscsi.shareStrategyTargetCli.basename;
let setAttributesText = "";
let setAuthText = "";
if (this.options.iscsi.shareStragetyTargetCli.tpg) {
if (this.options.iscsi.shareStragetyTargetCli.tpg.attributes) {
if (this.options.iscsi.shareStrategyTargetCli.tpg) {
if (this.options.iscsi.shareStrategyTargetCli.tpg.attributes) {
for (const attributeName in this.options.iscsi
.shareStragetyTargetCli.tpg.attributes) {
.shareStrategyTargetCli.tpg.attributes) {
const attributeValue = this.options.iscsi
.shareStragetyTargetCli.tpg.attributes[attributeName];
.shareStrategyTargetCli.tpg.attributes[attributeName];
setAttributesText += "\n";
setAttributesText += `set attribute ${attributeName}=${attributeValue}`;
}
}
if (this.options.iscsi.shareStragetyTargetCli.tpg.auth) {
if (this.options.iscsi.shareStrategyTargetCli.tpg.auth) {
for (const attributeName in this.options.iscsi
.shareStragetyTargetCli.tpg.auth) {
.shareStrategyTargetCli.tpg.auth) {
const attributeValue = this.options.iscsi
.shareStragetyTargetCli.tpg.auth[attributeName];
.shareStrategyTargetCli.tpg.auth[attributeName];
setAttributesText += "\n";
setAttributesText += `set auth ${attributeName}=${attributeValue}`;
}
@ -178,7 +178,7 @@ create /backstores/block/${iscsiName}
}
async deleteShare(call, datasetName) {
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
const sshClient = this.getSshClient();
let response;
@ -210,7 +210,7 @@ create /backstores/block/${iscsiName}
iscsiName = iscsiName.toLowerCase();
switch (this.options.iscsi.shareStrategy) {
case "targetCli":
basename = this.options.iscsi.shareStragetyTargetCli.basename;
basename = this.options.iscsi.shareStrategyTargetCli.basename;
response = await this.targetCliCommand(
`
cd /iscsi
@ -260,14 +260,21 @@ delete ${iscsiName}
const sshClient = this.getSshClient();
data = data.trim();
let command = "sh";
let args = ["-c"];
let command = [];
command.push(`echo "${data}"`.trim());
command.push("|");
command.push("targetcli");
let taregetCliCommand = [];
taregetCliCommand.push(`echo "${data}"`.trim());
taregetCliCommand.push("|");
taregetCliCommand.push("targetcli");
args.push("'" + command.join(" ") + "'");
return sshClient.exec(sshClient.buildCommand("sh", args));
if (this.options.iscsi.shareStrategyTargetCli.sudoEnabled) {
command = "sudo";
args.unshift("sh");
}
args.push("'" + taregetCliCommand.join(" ") + "'");
return sshClient.exec(sshClient.buildCommand(command, args));
}
}

View File

@ -122,7 +122,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
});
}
getZetabyte() {
async getZetabyte() {
const sshClient = this.getSshClient();
const options = {};
options.executor = new ZfsSshProcessManager(sshClient);
@ -130,14 +130,36 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli &&
this.options.zfs.cli.hasOwnProperty("paths")
) {
options.paths = this.options.zfs.cli.paths;
}
if (
this.options.zfs.hasOwnProperty("cli") &&
this.options.zfs.cli &&
this.options.zfs.cli.hasOwnProperty("sudoEnabled")
) {
options.sudo = this.getSudoEnabled();
}
if (typeof this.setZetabyteCustomOptions === "function") {
await this.setZetabyteCustomOptions(options);
}
return new Zetabyte(options);
}
getSudoEnabled() {
return this.options.zfs.cli && this.options.zfs.cli.sudoEnabled === true;
}
async getSudoPath() {
const zb = await this.getZetabyte();
return zb.options.paths.sudo || "/usr/bin/sudo";
}
getDatasetParentName() {
let datasetParentName = this.options.zfs.datasetParentName;
datasetParentName = datasetParentName.replace(/\/$/, "");
@ -160,7 +182,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
}
async removeSnapshotsFromDatatset(datasetName, options = {}) {
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
await zb.zfs.destroy(datasetName + "@%", options);
}
@ -250,7 +272,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
const driver = this;
const driverZfsResourceType = this.getDriverZfsResourceType();
const sshClient = this.getSshClient();
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let datasetParentName = this.getVolumeParentDatasetName();
let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName();
@ -671,8 +693,20 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
this.options.zfs.datasetPermissionsMode,
properties.mountpoint.value,
]);
if (this.getSudoEnabled()) {
command = (await this.getSudoPath()) + " " + command;
}
driver.ctx.logger.verbose("set permission command: %s", command);
response = await sshClient.exec(command);
if (response.code != 0) {
throw new GrpcError(
grpc.status.UNKNOWN,
`error setting permissions on dataset: ${JSON.stringify(
response
)}`
);
}
}
// set ownership
@ -690,8 +724,18 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
: ""),
properties.mountpoint.value,
]);
if (this.getSudoEnabled()) {
command = (await this.getSudoPath()) + " " + command;
}
driver.ctx.logger.verbose("set ownership command: %s", command);
response = await sshClient.exec(command);
if (response.code != 0) {
throw new GrpcError(
grpc.status.UNKNOWN,
`error setting ownership on dataset: ${JSON.stringify(response)}`
);
}
}
// set acls
@ -703,8 +747,18 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
acl,
properties.mountpoint.value,
]);
if (this.getSudoEnabled()) {
command = (await this.getSudoPath()) + " " + command;
}
driver.ctx.logger.verbose("set acl command: %s", command);
response = await sshClient.exec(command);
if (response.code != 0) {
throw new GrpcError(
grpc.status.UNKNOWN,
`error setting acl on dataset: ${JSON.stringify(response)}`
);
}
}
}
@ -772,7 +826,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
*/
async DeleteVolume(call) {
const driver = this;
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let datasetParentName = this.getVolumeParentDatasetName();
let name = call.request.volume_id;
@ -877,7 +931,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
async ControllerExpandVolume(call) {
const driver = this;
const driverZfsResourceType = this.getDriverZfsResourceType();
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let datasetParentName = this.getVolumeParentDatasetName();
let name = call.request.volume_id;
@ -978,7 +1032,11 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
await this.expandVolume(call, datasetName);
return {
capacity_bytes: this.options.zfs.datasetEnableQuotas ? capacity_bytes : 0,
capacity_bytes:
this.options.zfs.datasetEnableQuotas ||
driverZfsResourceType == "volume"
? capacity_bytes
: 0,
node_expansion_required: driverZfsResourceType == "volume" ? true : false,
};
}
@ -990,7 +1048,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
*/
async GetCapacity(call) {
const driver = this;
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let datasetParentName = this.getVolumeParentDatasetName();
@ -1027,7 +1085,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
async ListVolumes(call) {
const driver = this;
const driverZfsResourceType = this.getDriverZfsResourceType();
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let datasetParentName = this.getVolumeParentDatasetName();
let entries = [];
@ -1212,7 +1270,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
async ListSnapshots(call) {
const driver = this;
const driverZfsResourceType = this.getDriverZfsResourceType();
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let entries = [];
let entries_length = 0;
@ -1444,7 +1502,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
async CreateSnapshot(call) {
const driver = this;
const driverZfsResourceType = this.getDriverZfsResourceType();
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
let detachedSnapshot = false;
try {
@ -1678,7 +1736,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver {
*/
async DeleteSnapshot(call) {
const driver = this;
const zb = this.getZetabyte();
const zb = await this.getZetabyte();
const snapshot_id = call.request.snapshot_id;

View File

@ -4,7 +4,7 @@ const USER_AGENT = "democratic-csi-driver";
class Client {
constructor(options = {}) {
this.options = options;
this.options = JSON.parse(JSON.stringify(options));
this.logger = console;
// default to v1.0 for now
@ -19,7 +19,7 @@ class Client {
host: server.host,
port: server.port,
//userinfo: server.username + ":" + server.password,
path: server.apiVersion == 1 ? "/api/v1.0" : "/api/v2.0"
path: server.apiVersion == 1 ? "/api/v1.0" : "/api/v2.0",
};
return URI.serialize(options);
}
@ -55,22 +55,27 @@ class Client {
headers: {
Accept: "application/json",
"User-Agent": USER_AGENT,
"Content-Type": "application/json"
"Content-Type": "application/json",
},
json: true,
qs: data,
agentOptions: {
rejectUnauthorized: !!!client.options.allowInsecure
}
rejectUnauthorized: !!!client.options.allowInsecure,
},
};
request(options, function(err, res, body) {
request(options, function (err, res, body) {
client.log_repsonse(...arguments, options);
if (err) {
reject(err);
}
resolve(res);
}).auth(client.options.username, client.options.password);
}).auth(
client.options.username,
client.options.password,
true,
client.options.apiKey
);
});
}
@ -87,22 +92,27 @@ class Client {
headers: {
Accept: "application/json",
"User-Agent": USER_AGENT,
"Content-Type": "application/json"
"Content-Type": "application/json",
},
json: true,
body: data,
agentOptions: {
rejectUnauthorized: !!!client.options.allowInsecure
}
rejectUnauthorized: !!!client.options.allowInsecure,
},
};
request(options, function(err, res, body) {
request(options, function (err, res, body) {
client.log_repsonse(...arguments, options);
if (err) {
reject(err);
}
resolve(res);
}).auth(client.options.username, client.options.password);
}).auth(
client.options.username,
client.options.password,
true,
client.options.apiKey
);
});
}
@ -119,22 +129,27 @@ class Client {
headers: {
Accept: "application/json",
"User-Agent": USER_AGENT,
"Content-Type": "application/json"
"Content-Type": "application/json",
},
json: true,
body: data,
agentOptions: {
rejectUnauthorized: !!!client.options.allowInsecure
}
rejectUnauthorized: !!!client.options.allowInsecure,
},
};
request(options, function(err, res, body) {
request(options, function (err, res, body) {
client.log_repsonse(...arguments, options);
if (err) {
reject(err);
}
resolve(res);
}).auth(client.options.username, client.options.password);
}).auth(
client.options.username,
client.options.password,
true,
client.options.apiKey
);
});
}
@ -151,22 +166,27 @@ class Client {
headers: {
Accept: "application/json",
"User-Agent": USER_AGENT,
"Content-Type": "application/json"
"Content-Type": "application/json",
},
json: true,
body: data,
agentOptions: {
rejectUnauthorized: !!!client.options.allowInsecure
}
rejectUnauthorized: !!!client.options.allowInsecure,
},
};
request(options, function(err, res, body) {
request(options, function (err, res, body) {
client.log_repsonse(...arguments, options);
if (err) {
reject(err);
}
resolve(res);
}).auth(client.options.username, client.options.password);
}).auth(
client.options.username,
client.options.password,
true,
client.options.apiKey
);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -254,6 +254,7 @@ class CsiBaseDriver {
* @param {*} call
*/
async NodeStageVolume(call) {
const driver = this;
const mount = new Mount();
const filesystem = new Filesystem();
const iscsi = new ISCSI();
@ -310,46 +311,145 @@ class CsiBaseDriver {
device = `//${volume_context.server}/${volume_context.share}`;
break;
case "iscsi":
// create DB entry
// https://library.netapp.com/ecmdocs/ECMP1654943/html/GUID-8EC685B4-8CB6-40D8-A8D5-031A3899BCDC.html
// put these options in place to force targets managed by csi to be explicitly attached (in the case of unclearn shutdown etc)
let nodeDB = {
"node.startup": "manual",
};
const nodeDBKeyPrefix = "node-db.";
for (const key in normalizedSecrets) {
if (key.startsWith(nodeDBKeyPrefix)) {
nodeDB[key.substr(nodeDBKeyPrefix.length)] = normalizedSecrets[key];
}
let portals = [];
if (volume_context.portal) {
portals.push(volume_context.portal.trim());
}
await iscsi.iscsiadm.createNodeDBEntry(
volume_context.iqn,
volume_context.portal,
nodeDB
);
// login
await iscsi.iscsiadm.login(volume_context.iqn, volume_context.portal);
// find device name
device = `/dev/disk/by-path/ip-${volume_context.portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`;
if (volume_context.portals) {
volume_context.portals.split(",").forEach((portal) => {
portals.push(portal.trim());
});
}
// can take some time for device to show up, loop for some period
result = await filesystem.pathExists(device);
let timer_start = Math.round(new Date().getTime() / 1000);
let timer_max = 30;
while (!result) {
await sleep(2000);
// ensure full portal value
portals = portals.map((value) => {
if (!value.includes(":")) {
value += ":3260";
}
return value.trim();
});
// ensure unique entries only
portals = [...new Set(portals)];
let iscsiDevices = [];
for (let portal of portals) {
// create DB entry
// https://library.netapp.com/ecmdocs/ECMP1654943/html/GUID-8EC685B4-8CB6-40D8-A8D5-031A3899BCDC.html
// put these options in place to force targets managed by csi to be explicitly attached (in the case of unclearn shutdown etc)
let nodeDB = {
"node.startup": "manual",
};
const nodeDBKeyPrefix = "node-db.";
for (const key in normalizedSecrets) {
if (key.startsWith(nodeDBKeyPrefix)) {
nodeDB[key.substr(nodeDBKeyPrefix.length)] =
normalizedSecrets[key];
}
}
await iscsi.iscsiadm.createNodeDBEntry(
volume_context.iqn,
portal,
nodeDB
);
// login
await iscsi.iscsiadm.login(volume_context.iqn, portal);
// find device name
device = `/dev/disk/by-path/ip-${portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`;
let deviceByPath = device;
// can take some time for device to show up, loop for some period
result = await filesystem.pathExists(device);
let current_time = Math.round(new Date().getTime() / 1000);
if (!result && current_time - timer_start > timer_max) {
throw new GrpcError(
grpc.status.UNKNOWN,
`hit timeout waiting for device node to appear: ${device}`
let timer_start = Math.round(new Date().getTime() / 1000);
let timer_max = 30;
let deviceCreated = result;
while (!result) {
await sleep(2000);
result = await filesystem.pathExists(device);
if (result) {
deviceCreated = true;
break;
}
let current_time = Math.round(new Date().getTime() / 1000);
if (!result && current_time - timer_start > timer_max) {
driver.ctx.logger.warn(
`hit timeout waiting for device node to appear: ${device}`
);
break;
}
}
if (deviceCreated) {
device = await filesystem.realpath(device);
iscsiDevices.push(device);
driver.ctx.logger.info(
`successfully logged into portal ${portal} and created device ${deviceByPath} with realpath ${device}`
);
}
}
device = await filesystem.realpath(device);
// let things settle
// this will help in dm scenarios
await sleep(2000);
// filter duplicates
iscsiDevices = iscsiDevices.filter((value, index, self) => {
return self.indexOf(value) === index;
});
// only throw an error if we were not able to attach to *any* devices
if (iscsiDevices.length < 1) {
throw new GrpcError(
grpc.status.UNKNOWN,
`unable to attach any iscsi devices`
);
}
if (iscsiDevices.length != portals.length) {
driver.ctx.logger.warn(
`failed to attach all iscsi devices/targets/portals`
);
// TODO: allow a parameter to control this behavior in some form
if (false) {
throw new GrpcError(
grpc.status.UNKNOWN,
`unable to attach all iscsi devices`
);
}
}
// compare all device-mapper slaves with the newly created devices
// if any of the new devices are device-mapper slaves treat this as a
// multipath scenario
let allDeviceMapperSlaves = await filesystem.getAllDeviceMapperSlaveDevices();
let commonDevices = allDeviceMapperSlaves.filter((value) =>
iscsiDevices.includes(value)
);
const useMultipath = portals.length > 1 || commonDevices.length > 0;
// discover multipath device to use
if (useMultipath) {
device = await filesystem.getDeviceMapperDeviceFromSlaves(
iscsiDevices,
false
);
if (!device) {
throw new GrpcError(
grpc.status.UNKNOWN,
`failed to discover multipath device`
);
}
}
break;
default:
throw new GrpcError(
@ -463,6 +563,7 @@ class CsiBaseDriver {
const iscsi = new ISCSI();
let result;
let is_block = false;
let is_device_mapper = false;
let block_device_info;
let access_type = "mount";
@ -505,78 +606,100 @@ class CsiBaseDriver {
}
if (is_block) {
if (block_device_info.tran == "iscsi") {
// figure out which iscsi session this belongs to and logout
// scan /dev/disk/by-path/ip-*?
// device = `/dev/disk/by-path/ip-${volume_context.portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`;
// parse output from `iscsiadm -m session -P 3`
let sessions = await iscsi.iscsiadm.getSessionsDetails();
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let is_attached_to_session = false;
let realBlockDeviceInfos = [];
// detect if is a multipath device
is_device_mapper = await filesystem.isDeviceMapperDevice(
block_device_info.path
);
if (
session.attached_scsi_devices &&
session.attached_scsi_devices.host &&
session.attached_scsi_devices.host.devices
) {
is_attached_to_session = session.attached_scsi_devices.host.devices.some(
(device) => {
if (device.attached_scsi_disk == block_device_info.name) {
return true;
if (is_device_mapper) {
let realBlockDevices = await filesystem.getDeviceMapperDeviceSlaves(
block_device_info.path
);
for (const realBlockDevice of realBlockDevices) {
realBlockDeviceInfos.push(
await filesystem.getBlockDevice(realBlockDevice)
);
}
} else {
realBlockDeviceInfos = [block_device_info];
}
// TODO: this could be made async to detach all simultaneously
for (const block_device_info_i of realBlockDeviceInfos) {
if (block_device_info_i.tran == "iscsi") {
// figure out which iscsi session this belongs to and logout
// scan /dev/disk/by-path/ip-*?
// device = `/dev/disk/by-path/ip-${volume_context.portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`;
// parse output from `iscsiadm -m session -P 3`
let sessions = await iscsi.iscsiadm.getSessionsDetails();
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let is_attached_to_session = false;
if (
session.attached_scsi_devices &&
session.attached_scsi_devices.host &&
session.attached_scsi_devices.host.devices
) {
is_attached_to_session = session.attached_scsi_devices.host.devices.some(
(device) => {
if (device.attached_scsi_disk == block_device_info_i.name) {
return true;
}
return false;
}
return false;
}
);
}
if (is_attached_to_session) {
let timer_start;
let timer_max;
timer_start = Math.round(new Date().getTime() / 1000);
timer_max = 30;
let loggedOut = false;
while (!loggedOut) {
try {
await iscsi.iscsiadm.logout(session.target, [
session.persistent_portal,
]);
loggedOut = true;
} catch (err) {
await sleep(2000);
let current_time = Math.round(new Date().getTime() / 1000);
if (current_time - timer_start > timer_max) {
// not throwing error for now as future invocations would not enter code path anyhow
loggedOut = true;
//throw new GrpcError(
// grpc.status.UNKNOWN,
// `hit timeout trying to logout of iscsi target: ${session.persistent_portal}`
//);
}
}
);
}
timer_start = Math.round(new Date().getTime() / 1000);
timer_max = 30;
let deletedEntry = false;
while (!deletedEntry) {
try {
await iscsi.iscsiadm.deleteNodeDBEntry(
session.target,
session.persistent_portal
);
deletedEntry = true;
} catch (err) {
await sleep(2000);
let current_time = Math.round(new Date().getTime() / 1000);
if (current_time - timer_start > timer_max) {
// not throwing error for now as future invocations would not enter code path anyhow
if (is_attached_to_session) {
let timer_start;
let timer_max;
timer_start = Math.round(new Date().getTime() / 1000);
timer_max = 30;
let loggedOut = false;
while (!loggedOut) {
try {
await iscsi.iscsiadm.logout(session.target, [
session.persistent_portal,
]);
loggedOut = true;
} catch (err) {
await sleep(2000);
let current_time = Math.round(new Date().getTime() / 1000);
if (current_time - timer_start > timer_max) {
// not throwing error for now as future invocations would not enter code path anyhow
loggedOut = true;
//throw new GrpcError(
// grpc.status.UNKNOWN,
// `hit timeout trying to logout of iscsi target: ${session.persistent_portal}`
//);
}
}
}
timer_start = Math.round(new Date().getTime() / 1000);
timer_max = 30;
let deletedEntry = false;
while (!deletedEntry) {
try {
await iscsi.iscsiadm.deleteNodeDBEntry(
session.target,
session.persistent_portal
);
deletedEntry = true;
//throw new GrpcError(
// grpc.status.UNKNOWN,
// `hit timeout trying to delete iscsi node DB entry: ${session.target}, ${session.persistent_portal}`
//);
} catch (err) {
await sleep(2000);
let current_time = Math.round(new Date().getTime() / 1000);
if (current_time - timer_start > timer_max) {
// not throwing error for now as future invocations would not enter code path anyhow
deletedEntry = true;
//throw new GrpcError(
// grpc.status.UNKNOWN,
// `hit timeout trying to delete iscsi node DB entry: ${session.target}, ${session.persistent_portal}`
//);
}
}
}
}
@ -817,6 +940,7 @@ class CsiBaseDriver {
let is_block = false;
let is_formatted;
let fs_type;
let is_device_mapper = false;
const volume_id = call.request.volume_id;
const volume_path = call.request.volume_path;
@ -853,7 +977,24 @@ class CsiBaseDriver {
}
if (is_block) {
await filesystem.rescanDevice(device);
let rescan_devices = [];
// detect if is a multipath device
is_device_mapper = await filesystem.isDeviceMapperDevice(device);
if (is_device_mapper) {
// NOTE: want to make sure we scan the dm device *after* all the underlying slaves
rescan_devices = await filesystem.getDeviceMapperDeviceSlaves(device);
}
rescan_devices.push(device);
for (let sdevice of rescan_devices) {
await filesystem.rescanDevice(sdevice);
}
// let things settle
// it appears the dm devices can take a second to figure things out
await sleep(2000);
if (is_formatted && access_type == "mount") {
fs_info = await filesystem.getDeviceFilesystemInfo(device);
fs_type = fs_info.type;

View File

@ -48,14 +48,165 @@ class Filesystem {
const device_path = await filesystem.realpath(device);
const blockdevices = await filesystem.getAllBlockDevices();
return blockdevices.some((i) => {
if (i.path == device_path) {
return blockdevices.some(async (i) => {
if ((await filesystem.realpath(i.path)) == device_path) {
return true;
}
return false;
});
}
/**
* Attempt to discover if the device is a device-mapper device
*
* @param {*} device
*/
async isDeviceMapperDevice(device) {
const filesystem = this;
const isBlock = await filesystem.isBlockDevice(device);
if (!isBlock) {
return false;
}
device = await filesystem.realpath(device);
return device.includes("dm-");
}
async isDeviceMapperSlaveDevice(device) {
const filesystem = this;
device = await filesystem.realpath(device);
}
/**
* Get all device-mapper devices (ie: dm-0, dm-1, dm-N...)
*/
async getAllDeviceMapperDevices() {
const filesystem = this;
let result;
let devices = [];
let args = [
"-c",
'for file in $(ls -la /dev/mapper/* | grep "\\->" | grep -oP "\\-> .+" | grep -oP " .+"); do echo $(F=$(echo $file | grep -oP "[a-z0-9-]+");echo $F":"$(ls "/sys/block/${F}/slaves/");); done;',
];
try {
result = await filesystem.exec("sh", args);
for (const dm of result.stdout.trim().split("\n")) {
if (dm.length < 1) {
continue;
}
devices.push("/dev/" + dm.split(":")[0].trim());
}
return devices;
} catch (err) {
throw err;
}
}
async getAllDeviceMapperSlaveDevices() {
const filesystem = this;
let result;
let args = [
"-c",
'for file in $(ls -la /dev/mapper/* | grep "\\->" | grep -oP "\\-> .+" | grep -oP " .+"); do echo $(F=$(echo $file | grep -oP "[a-z0-9-]+");echo $F":"$(ls "/sys/block/${F}/slaves/");); done;',
];
let slaves = [];
try {
result = await filesystem.exec("sh", args);
for (const dm of result.stdout.trim().split("\n")) {
if (dm.length < 1) {
continue;
}
const realDevices = dm
.split(":")[1]
.split(" ")
.map((value) => {
return "/dev/" + value.trim();
});
slaves.push(...realDevices);
}
return slaves;
} catch (err) {
throw err;
}
}
/**
* Get all slave devices connected to a device-mapper device
*
* @param {*} device
*/
async getDeviceMapperDeviceSlaves(device) {
const filesystem = this;
device = await filesystem.realpath(device);
let device_info = await filesystem.getBlockDevice(device);
const slaves = [];
let result;
let args = [`/sys/block/${device_info.kname}/slaves/`];
try {
result = await filesystem.exec("ls", args);
for (const entry of result.stdout.split("\n")) {
if (entry.trim().length < 1) {
continue;
}
slaves.push("/dev/" + entry.trim());
}
return slaves;
} catch (err) {
throw err;
}
}
async getDeviceMapperDeviceFromSlaves(slaves, matchAll = true) {
const filesystem = this;
let result;
// get mapping of dm devices to real devices
let args = [
"-c",
'for file in $(ls -la /dev/mapper/* | grep "\\->" | grep -oP "\\-> .+" | grep -oP " .+"); do echo $(F=$(echo $file | grep -oP "[a-z0-9-]+");echo $F":"$(ls "/sys/block/${F}/slaves/");); done;',
];
result = await filesystem.exec("sh", args);
for (const dm of result.stdout.trim().split("\n")) {
if (dm.length < 1) {
continue;
}
const dmDevice = "/dev/" + dm.split(":")[0].trim();
const realDevices = dm
.split(":")[1]
.split(" ")
.map((value) => {
return "/dev/" + value.trim();
});
const intersectDevices = slaves.filter((value) =>
realDevices.includes(value)
);
if (matchAll === false && intersectDevices.length > 0) {
return dmDevice;
}
// if all 3 have the same elements we have a winner
if (
intersectDevices.length == realDevices.length &&
realDevices.length == slaves.length
) {
return dmDevice;
}
}
}
/**
* create symlink
*
@ -264,12 +415,19 @@ class Filesystem {
);
}
let is_device_mapper_device = await filesystem.isDeviceMapperDevice(device);
result = await filesystem.realpath(device);
device_name = result.split("/").pop();
// echo 1 > /sys/block/sdb/device/rescan
const sys_file = `/sys/block/${device_name}/device/rescan`;
fs.writeFileSync(sys_file, "1");
if (is_device_mapper_device) {
// multipath -r /dev/dm-0
result = await filesystem.exec("multipath", ["-r", device]);
} else {
device_name = result.split("/").pop();
// echo 1 > /sys/block/sdb/device/rescan
const sys_file = `/sys/block/${device_name}/device/rescan`;
fs.writeFileSync(sys_file, "1");
}
}
/**

View File

@ -25,7 +25,7 @@ class ISCSI {
if (!options.executor) {
options.executor = {
spawn: cp.spawn
spawn: cp.spawn,
};
}
@ -47,7 +47,7 @@ class ISCSI {
const entries = result.stdout.trim().split("\n");
const interfaces = [];
let fields;
entries.forEach(entry => {
entries.forEach((entry) => {
fields = entry.split(" ");
interfaces.push({
iface_name: fields[0],
@ -55,7 +55,7 @@ class ISCSI {
hwaddress: getIscsiValue(fields[1].split(",")[1]),
ipaddress: getIscsiValue(fields[1].split(",")[2]),
net_ifacename: getIscsiValue(fields[1].split(",")[3]),
initiatorname: getIscsiValue(fields[1].split(",")[4])
initiatorname: getIscsiValue(fields[1].split(",")[4]),
});
});
@ -75,7 +75,7 @@ class ISCSI {
const entries = result.stdout.trim().split("\n");
const i = {};
let fields, key, value;
entries.forEach(entry => {
entries.forEach((entry) => {
if (entry.startsWith("#")) return;
fields = entry.split("=");
key = fields[0].trim();
@ -103,7 +103,7 @@ class ISCSI {
"-p",
portal,
"-o",
"new"
"new",
]);
await iscsi.exec(options.paths.iscsiadm, args);
for (let attribute in attributes) {
@ -120,7 +120,7 @@ class ISCSI {
"--name",
attribute,
"--value",
attributes[attribute]
attributes[attribute],
]);
await iscsi.exec(options.paths.iscsiadm, args);
}
@ -142,7 +142,7 @@ class ISCSI {
"-p",
portal,
"-o",
"delete"
"delete",
]);
await iscsi.exec(options.paths.iscsiadm, args);
},
@ -174,7 +174,7 @@ class ISCSI {
const entries = result.stdout.trim().split("\n");
const sessions = [];
let fields;
entries.forEach(entry => {
entries.forEach((entry) => {
fields = entry.split(" ");
sessions.push({
protocol: entry.split(":")[0],
@ -182,7 +182,7 @@ class ISCSI {
portal: fields[2].split(",")[0],
target_portal_group_tag: fields[2].split(",")[1],
iqn: fields[3].split(":")[0],
target: fields[3].split(":")[1]
target: fields[3].split(":")[1],
});
});
@ -212,6 +212,7 @@ class ISCSI {
return [];
}
let currentTarget;
let sessionGroups = [];
let currentSession = [];
@ -221,13 +222,21 @@ class ISCSI {
entries.shift();
entries.shift();
// this should break up the lines into groups of lines
// where each group is the full details of a single session
// note that the output of the command bundles/groups all sessions
// by target so extra logic is needed to hanle that
// alternatively we could get all sessions using getSessions()
// and then invoke `iscsiadm -m session -P 3 -r <session id>` in a loop
for (let i = 0; i < entries.length; i++) {
let entry = entries[i];
if (entry.startsWith("Target:")) {
currentTarget = entry;
} else if (entry.trim().startsWith("Current Portal:")) {
if (currentSession.length > 0) {
sessionGroups.push(currentSession);
}
currentSession = [entry];
currentSession = [currentTarget, entry];
} else {
currentSession.push(entry);
}
@ -261,11 +270,7 @@ class ISCSI {
.trim()
.replace(/ /g, "_")
.replace(/\W/g, "");
let value = line
.split(":")
.slice(1)
.join(":")
.trim();
let value = line.split(":").slice(1).join(":").trim();
if (currentSection) {
session[currentSection] = session[currentSection] || {};
@ -282,7 +287,7 @@ class ISCSI {
.split(":")
.slice(1)
.join(":")
.trim()
.trim(),
};
while (
sessionLines[j + 1] &&
@ -308,7 +313,7 @@ class ISCSI {
.split(":")
.slice(1)
.join(":")
.trim()
.trim(),
});
}
@ -322,7 +327,7 @@ class ISCSI {
key = key.charAt(0).toLowerCase() + key.slice(1);
key = key.replace(
/[A-Z]/g,
letter => `_${letter.toLowerCase()}`
(letter) => `_${letter.toLowerCase()}`
);
break;
}
@ -367,12 +372,12 @@ class ISCSI {
const entries = result.stdout.trim().split("\n");
const targets = [];
entries.forEach(entry => {
entries.forEach((entry) => {
targets.push({
portal: entry.split(",")[0],
target_portal_group_tag: entry.split(" ")[0].split(",")[1],
iqn: entry.split(" ")[1].split(":")[0],
target: entry.split(" ")[1].split(":")[1]
target: entry.split(" ")[1].split(":")[1],
});
});
@ -432,7 +437,7 @@ class ISCSI {
}
return true;
}
},
};
}
@ -460,15 +465,15 @@ class ISCSI {
}
return new Promise((resolve, reject) => {
child.stdout.on("data", function(data) {
child.stdout.on("data", function (data) {
stdout = stdout + data;
});
child.stderr.on("data", function(data) {
child.stderr.on("data", function (data) {
stderr = stderr + data;
});
child.on("close", function(code) {
child.on("close", function (code) {
const result = { code, stdout, stderr };
if (timeout) {
clearTimeout(timeout);