Merge pull request #24 from democratic-csi/next
version auto-detection, delete race conditions, sudo, apiKey, multipath
This commit is contained in:
commit
148c8d9ac0
|
|
@ -1,4 +1,7 @@
|
|||
chart
|
||||
dev
|
||||
examples
|
||||
contrib
|
||||
node_modules
|
||||
Dockerfile*
|
||||
TODO.md
|
||||
|
|
|
|||
|
|
@ -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: |
|
||||
|
|
|
|||
90
Dockerfile
90
Dockerfile
|
|
@ -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" ]
|
||||
|
|
|
|||
|
|
@ -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" ]
|
||||
35
README.md
35
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
chroot /host /usr/bin/env -i PATH="/sbin:/bin:/usr/sbin:/usr/bin" multipath "${@:1}"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue