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 | chart | ||||||
| dev | dev | ||||||
| examples | examples | ||||||
|  | contrib | ||||||
| node_modules | node_modules | ||||||
|  | Dockerfile* | ||||||
|  | TODO.md | ||||||
|  |  | ||||||
|  | @ -13,6 +13,10 @@ jobs: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
| 
 | 
 | ||||||
|     steps: |     steps: | ||||||
|  |       - name: Cancel Previous Runs | ||||||
|  |         uses: styfle/cancel-workflow-action@0.6.0 | ||||||
|  |         with: | ||||||
|  |           access_token: ${{ github.token }} | ||||||
|       - uses: actions/checkout@v2 |       - uses: actions/checkout@v2 | ||||||
|       - name: docker build |       - name: docker build | ||||||
|         run: | |         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 | ENV DEBIAN_FRONTEND=noninteractive | ||||||
| 
 | 
 | ||||||
| ARG TARGETPLATFORM | ARG TARGETPLATFORM | ||||||
| ARG BUILDPLATFORM | 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/* \ | 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 |         && 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 | # install node | ||||||
| RUN apt-get update && apt-get install -y wget xz-utils | 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 | RUN chmod +x /usr/local/sbin/node-installer.sh && node-installer.sh | ||||||
| ENV PATH=/usr/local/lib/nodejs/bin:$PATH | 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 as a non-root user | ||||||
| RUN useradd --create-home csi \ | RUN useradd --create-home csi \ | ||||||
|         && mkdir /home/csi/app \ |         && mkdir /home/csi/app \ | ||||||
|  | @ -47,15 +33,65 @@ USER csi | ||||||
| 
 | 
 | ||||||
| COPY package*.json ./ | COPY package*.json ./ | ||||||
| RUN npm install | RUN npm install | ||||||
| 
 |  | ||||||
| COPY --chown=csi:csi . . | 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 && \ | #RUN apt-get update && \ | ||||||
| #        apt-get purge -y python make gcc g++ && \ | #        apt-get install -y ansible && \ | ||||||
| #        rm -rf /var/lib/apt/lists/* | #        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 | EXPOSE 50051 | ||||||
| ENTRYPOINT [ "bin/democratic-csi" ] | 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" ] | ||||||
							
								
								
									
										33
									
								
								README.md
								
								
								
								
							
							
						
						
									
										33
									
								
								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 | 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: | If you are running Kubernetes with rancher/rke please see the following: | ||||||
| 
 | 
 | ||||||
| - https://github.com/rancher/rke/issues/1846 | - 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) | ### 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: | Ensure the following services are configurged and running: | ||||||
| 
 | 
 | ||||||
| - ssh (if you use a password for authentication make sure it is allowed) | - 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 | - ensure `zsh`, `bash`, or `sh` is set as the root shell, `csh` gives false errors due to quoting | ||||||
| - nfs | - nfs | ||||||
| - iscsi | - 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 | - 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) | ### ZoL (zfs-generic-nfs, zfs-generic-iscsi) | ||||||
| 
 | 
 | ||||||
| Ensure ssh and zfs is installed on the server and that you have installed | Ensure ssh and zfs is installed on the server and that you have installed | ||||||
|  | @ -115,6 +147,7 @@ helm upgrade \ | ||||||
| --namespace democratic-csi \ | --namespace democratic-csi \ | ||||||
| zfs-nfs democratic-csi/democratic-csi | zfs-nfs democratic-csi/democratic-csi | ||||||
| ``` | ``` | ||||||
|  | 
 | ||||||
| ### A note on non standard kubelet paths | ### 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. | ||||||
|  |  | ||||||
|  | @ -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 |   protocol: http | ||||||
|   host: server address |   host: server address | ||||||
|   port: 80 |   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 |   username: root | ||||||
|   password: |   password: | ||||||
|   allowInsecure: true |   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: | sshConnection: | ||||||
|   host: server address |   host: server address | ||||||
|   port: 22 |   port: 22 | ||||||
|  | @ -21,6 +28,9 @@ zfs: | ||||||
|   # can be used to override defaults if necessary |   # can be used to override defaults if necessary | ||||||
|   # the example below is useful for TrueNAS 12 |   # the example below is useful for TrueNAS 12 | ||||||
|   #cli: |   #cli: | ||||||
|  |   #  sudoEnabled: true | ||||||
|  |   # | ||||||
|  |   #  leave paths unset for auto-detection | ||||||
|   #  paths: |   #  paths: | ||||||
|   #    zfs: /usr/local/sbin/zfs |   #    zfs: /usr/local/sbin/zfs | ||||||
|   #    zpool: /usr/local/sbin/zpool |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |  | ||||||
|  | @ -4,9 +4,16 @@ httpConnection: | ||||||
|   protocol: http |   protocol: http | ||||||
|   host: server address |   host: server address | ||||||
|   port: 80 |   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 |   username: root | ||||||
|   password: |   password: | ||||||
|   allowInsecure: true |   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: | sshConnection: | ||||||
|   host: server address |   host: server address | ||||||
|   port: 22 |   port: 22 | ||||||
|  | @ -21,6 +28,9 @@ zfs: | ||||||
|   # can be used to override defaults if necessary |   # can be used to override defaults if necessary | ||||||
|   # the example below is useful for TrueNAS 12 |   # the example below is useful for TrueNAS 12 | ||||||
|   #cli: |   #cli: | ||||||
|  |   #  sudoEnabled: true | ||||||
|  |   # | ||||||
|  |   #  leave paths unset for auto-detection | ||||||
|   #  paths: |   #  paths: | ||||||
|   #    zfs: /usr/local/sbin/zfs |   #    zfs: /usr/local/sbin/zfs | ||||||
|   #    zpool: /usr/local/sbin/zpool |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |  | ||||||
|  | @ -4,9 +4,16 @@ httpConnection: | ||||||
|   protocol: http |   protocol: http | ||||||
|   host: server address |   host: server address | ||||||
|   port: 80 |   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 |   username: root | ||||||
|   password: |   password: | ||||||
|   allowInsecure: true |   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: | sshConnection: | ||||||
|   host: server address |   host: server address | ||||||
|   port: 22 |   port: 22 | ||||||
|  | @ -21,6 +28,9 @@ zfs: | ||||||
|   # can be used to override defaults if necessary |   # can be used to override defaults if necessary | ||||||
|   # the example below is useful for TrueNAS 12 |   # the example below is useful for TrueNAS 12 | ||||||
|   #cli: |   #cli: | ||||||
|  |   #  sudoEnabled: true | ||||||
|  |   # | ||||||
|  |   #  leave paths unset for auto-detection | ||||||
|   #  paths: |   #  paths: | ||||||
|   #    zfs: /usr/local/sbin/zfs |   #    zfs: /usr/local/sbin/zfs | ||||||
|   #    zpool: /usr/local/sbin/zpool |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ zfs: | ||||||
|   # can be used to override defaults if necessary |   # can be used to override defaults if necessary | ||||||
|   # the example below is useful for TrueNAS 12 |   # the example below is useful for TrueNAS 12 | ||||||
|   #cli: |   #cli: | ||||||
|  |   #  sudoEnabled: true | ||||||
|   #  paths: |   #  paths: | ||||||
|   #    zfs: /usr/local/sbin/zfs |   #    zfs: /usr/local/sbin/zfs | ||||||
|   #    zpool: /usr/local/sbin/zpool |   #    zpool: /usr/local/sbin/zpool | ||||||
|  | @ -50,7 +51,8 @@ iscsi: | ||||||
|   # http://www.linux-iscsi.org/wiki/ISCSI |   # http://www.linux-iscsi.org/wiki/ISCSI | ||||||
|   # https://bugzilla.redhat.com/show_bug.cgi?id=1659195 |   # 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/ |   # 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" |     basename: "iqn.2003-01.org.linux-iscsi.ubuntu-19.x8664" | ||||||
|     tpg: |     tpg: | ||||||
|       attributes: |       attributes: | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ zfs: | ||||||
|   # can be used to override defaults if necessary |   # can be used to override defaults if necessary | ||||||
|   # the example below is useful for TrueNAS 12 |   # the example below is useful for TrueNAS 12 | ||||||
|   #cli: |   #cli: | ||||||
|  |   #  sudoEnabled: true | ||||||
|   #  paths: |   #  paths: | ||||||
|   #    zfs: /usr/local/sbin/zfs |   #    zfs: /usr/local/sbin/zfs | ||||||
|   #    zpool: /usr/local/sbin/zpool |   #    zpool: /usr/local/sbin/zpool | ||||||
|  |  | ||||||
|  | @ -50,9 +50,9 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "@eslint/eslintrc": { |     "@eslint/eslintrc": { | ||||||
|       "version": "0.1.3", |       "version": "0.2.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", |       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", | ||||||
|       "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", |       "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "ajv": "^6.12.4", |         "ajv": "^6.12.4", | ||||||
|         "debug": "^4.1.1", |         "debug": "^4.1.1", | ||||||
|  | @ -67,9 +67,9 @@ | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "ajv": { |         "ajv": { | ||||||
|           "version": "6.12.4", |           "version": "6.12.6", | ||||||
|           "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", |           "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", | ||||||
|           "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", |           "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", | ||||||
|           "requires": { |           "requires": { | ||||||
|             "fast-deep-equal": "^3.1.1", |             "fast-deep-equal": "^3.1.1", | ||||||
|             "fast-json-stable-stringify": "^2.0.0", |             "fast-json-stable-stringify": "^2.0.0", | ||||||
|  | @ -169,11 +169,6 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", |       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", | ||||||
|       "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" |       "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": { |     "@types/long": { | ||||||
|       "version": "4.0.1", |       "version": "4.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", |       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", | ||||||
|  | @ -185,14 +180,14 @@ | ||||||
|       "integrity": "sha512-dJ9vXxJ8MEwzNn4GkoAGauejhXoKuJyYKegsA6Af25ZpEDXomeVXt5HUWUNVHk5UN7+U0f6ghC6otwt+7PdSDg==" |       "integrity": "sha512-dJ9vXxJ8MEwzNn4GkoAGauejhXoKuJyYKegsA6Af25ZpEDXomeVXt5HUWUNVHk5UN7+U0f6ghC6otwt+7PdSDg==" | ||||||
|     }, |     }, | ||||||
|     "acorn": { |     "acorn": { | ||||||
|       "version": "7.4.0", |       "version": "7.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", |       "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", | ||||||
|       "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==" |       "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" | ||||||
|     }, |     }, | ||||||
|     "acorn-jsx": { |     "acorn-jsx": { | ||||||
|       "version": "5.2.0", |       "version": "5.3.1", | ||||||
|       "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", |       "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", | ||||||
|       "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==" |       "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" | ||||||
|     }, |     }, | ||||||
|     "ajv": { |     "ajv": { | ||||||
|       "version": "6.12.3", |       "version": "6.12.3", | ||||||
|  | @ -324,11 +319,6 @@ | ||||||
|       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", |       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", | ||||||
|       "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" |       "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": { |     "caseless": { | ||||||
|       "version": "0.12.0", |       "version": "0.12.0", | ||||||
|       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", |       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", | ||||||
|  | @ -344,11 +334,10 @@ | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "ansi-styles": { |         "ansi-styles": { | ||||||
|           "version": "4.2.1", |           "version": "4.3.0", | ||||||
|           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", |           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | ||||||
|           "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", |           "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | ||||||
|           "requires": { |           "requires": { | ||||||
|             "@types/color-name": "^1.1.1", |  | ||||||
|             "color-convert": "^2.0.1" |             "color-convert": "^2.0.1" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|  | @ -381,13 +370,13 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "cliui": { |     "cliui": { | ||||||
|       "version": "6.0.0", |       "version": "7.0.4", | ||||||
|       "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", |       "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", | ||||||
|       "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", |       "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "string-width": "^4.2.0", |         "string-width": "^4.2.0", | ||||||
|         "strip-ansi": "^6.0.0", |         "strip-ansi": "^6.0.0", | ||||||
|         "wrap-ansi": "^6.2.0" |         "wrap-ansi": "^7.0.0" | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "emoji-regex": { |         "emoji-regex": { | ||||||
|  | @ -504,11 +493,11 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "debug": { |     "debug": { | ||||||
|       "version": "4.1.1", |       "version": "4.3.1", | ||||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", |       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", | ||||||
|       "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", |       "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "ms": "^2.1.1" |         "ms": "2.1.2" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "decamelize": { |     "decamelize": { | ||||||
|  | @ -570,27 +559,32 @@ | ||||||
|         "ansi-colors": "^4.1.1" |         "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": { |     "escape-string-regexp": { | ||||||
|       "version": "1.0.5", |       "version": "1.0.5", | ||||||
|       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", |       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", | ||||||
|       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" |       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" | ||||||
|     }, |     }, | ||||||
|     "eslint": { |     "eslint": { | ||||||
|       "version": "7.8.1", |       "version": "7.14.0", | ||||||
|       "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", |       "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", | ||||||
|       "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", |       "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "@babel/code-frame": "^7.0.0", |         "@babel/code-frame": "^7.0.0", | ||||||
|         "@eslint/eslintrc": "^0.1.3", |         "@eslint/eslintrc": "^0.2.1", | ||||||
|         "ajv": "^6.10.0", |         "ajv": "^6.10.0", | ||||||
|         "chalk": "^4.0.0", |         "chalk": "^4.0.0", | ||||||
|         "cross-spawn": "^7.0.2", |         "cross-spawn": "^7.0.2", | ||||||
|         "debug": "^4.0.1", |         "debug": "^4.0.1", | ||||||
|         "doctrine": "^3.0.0", |         "doctrine": "^3.0.0", | ||||||
|         "enquirer": "^2.3.5", |         "enquirer": "^2.3.5", | ||||||
|         "eslint-scope": "^5.1.0", |         "eslint-scope": "^5.1.1", | ||||||
|         "eslint-utils": "^2.1.0", |         "eslint-utils": "^2.1.0", | ||||||
|         "eslint-visitor-keys": "^1.3.0", |         "eslint-visitor-keys": "^2.0.0", | ||||||
|         "espree": "^7.3.0", |         "espree": "^7.3.0", | ||||||
|         "esquery": "^1.2.0", |         "esquery": "^1.2.0", | ||||||
|         "esutils": "^2.0.2", |         "esutils": "^2.0.2", | ||||||
|  | @ -620,11 +614,11 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "eslint-scope": { |     "eslint-scope": { | ||||||
|       "version": "5.1.0", |       "version": "5.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", |       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", | ||||||
|       "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", |       "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "esrecurse": "^4.1.0", |         "esrecurse": "^4.3.0", | ||||||
|         "estraverse": "^4.1.1" |         "estraverse": "^4.1.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  | @ -634,12 +628,19 @@ | ||||||
|       "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", |       "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "eslint-visitor-keys": "^1.1.0" |         "eslint-visitor-keys": "^1.1.0" | ||||||
|       } |  | ||||||
|       }, |       }, | ||||||
|  |       "dependencies": { | ||||||
|         "eslint-visitor-keys": { |         "eslint-visitor-keys": { | ||||||
|           "version": "1.3.0", |           "version": "1.3.0", | ||||||
|           "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", |           "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", | ||||||
|           "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" |           "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "eslint-visitor-keys": { | ||||||
|  |       "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": { |     "espree": { | ||||||
|       "version": "7.3.0", |       "version": "7.3.0", | ||||||
|  | @ -649,6 +650,13 @@ | ||||||
|         "acorn": "^7.4.0", |         "acorn": "^7.4.0", | ||||||
|         "acorn-jsx": "^5.2.0", |         "acorn-jsx": "^5.2.0", | ||||||
|         "eslint-visitor-keys": "^1.3.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": { |     "esprima": { | ||||||
|  | @ -739,15 +747,6 @@ | ||||||
|         "flat-cache": "^2.0.1" |         "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": { |     "flat-cache": { | ||||||
|       "version": "2.0.1", |       "version": "2.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", |       "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==" |       "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" | ||||||
|     }, |     }, | ||||||
|     "import-fresh": { |     "import-fresh": { | ||||||
|       "version": "3.2.1", |       "version": "3.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", |       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", | ||||||
|       "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", |       "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "parent-module": "^1.0.0", |         "parent-module": "^1.0.0", | ||||||
|         "resolve-from": "^4.0.0" |         "resolve-from": "^4.0.0" | ||||||
|  | @ -1055,14 +1054,6 @@ | ||||||
|         "type-check": "~0.4.0" |         "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": { |     "lodash": { | ||||||
|       "version": "4.17.20", |       "version": "4.17.20", | ||||||
|       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", |       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", | ||||||
|  | @ -1232,27 +1223,6 @@ | ||||||
|         "lcid": "^1.0.0" |         "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": { |     "parent-module": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", |       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", | ||||||
|  | @ -1261,11 +1231,6 @@ | ||||||
|         "callsites": "^3.0.0" |         "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": { |     "path-is-absolute": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", |       "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", |       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", | ||||||
|       "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" |       "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": { |     "resolve-from": { | ||||||
|       "version": "4.0.0", |       "version": "4.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", |       "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", |       "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", | ||||||
|       "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" |       "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": { |     "shebang-command": { | ||||||
|       "version": "2.0.0", |       "version": "2.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", |       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", | ||||||
|  | @ -1732,14 +1687,14 @@ | ||||||
|       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" |       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" | ||||||
|     }, |     }, | ||||||
|     "uuid": { |     "uuid": { | ||||||
|       "version": "8.3.0", |       "version": "8.3.1", | ||||||
|       "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", |       "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", | ||||||
|       "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" |       "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" | ||||||
|     }, |     }, | ||||||
|     "v8-compile-cache": { |     "v8-compile-cache": { | ||||||
|       "version": "2.1.1", |       "version": "2.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", |       "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", | ||||||
|       "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==" |       "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==" | ||||||
|     }, |     }, | ||||||
|     "verror": { |     "verror": { | ||||||
|       "version": "1.10.0", |       "version": "1.10.0", | ||||||
|  | @ -1759,11 +1714,6 @@ | ||||||
|         "isexe": "^2.0.0" |         "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": { |     "window-size": { | ||||||
|       "version": "0.1.4", |       "version": "0.1.4", | ||||||
|       "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", |       "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", | ||||||
|  | @ -1834,9 +1784,9 @@ | ||||||
|       "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" |       "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" | ||||||
|     }, |     }, | ||||||
|     "wrap-ansi": { |     "wrap-ansi": { | ||||||
|       "version": "6.2.0", |       "version": "7.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", |       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", | ||||||
|       "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", |       "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "ansi-styles": "^4.0.0", |         "ansi-styles": "^4.0.0", | ||||||
|         "string-width": "^4.1.0", |         "string-width": "^4.1.0", | ||||||
|  | @ -1844,11 +1794,10 @@ | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "ansi-styles": { |         "ansi-styles": { | ||||||
|           "version": "4.2.1", |           "version": "4.3.0", | ||||||
|           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", |           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | ||||||
|           "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", |           "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | ||||||
|           "requires": { |           "requires": { | ||||||
|             "@types/color-name": "^1.1.1", |  | ||||||
|             "color-convert": "^2.0.1" |             "color-convert": "^2.0.1" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|  | @ -1901,9 +1850,9 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "y18n": { |     "y18n": { | ||||||
|       "version": "4.0.0", |       "version": "5.0.5", | ||||||
|       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", |       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", | ||||||
|       "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" |       "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" | ||||||
|     }, |     }, | ||||||
|     "yallist": { |     "yallist": { | ||||||
|       "version": "4.0.0", |       "version": "4.0.0", | ||||||
|  | @ -1911,21 +1860,17 @@ | ||||||
|       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" |       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" | ||||||
|     }, |     }, | ||||||
|     "yargs": { |     "yargs": { | ||||||
|       "version": "15.4.1", |       "version": "16.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", |       "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.1.tgz", | ||||||
|       "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", |       "integrity": "sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "cliui": "^6.0.0", |         "cliui": "^7.0.2", | ||||||
|         "decamelize": "^1.2.0", |         "escalade": "^3.1.1", | ||||||
|         "find-up": "^4.1.0", |         "get-caller-file": "^2.0.5", | ||||||
|         "get-caller-file": "^2.0.1", |  | ||||||
|         "require-directory": "^2.1.1", |         "require-directory": "^2.1.1", | ||||||
|         "require-main-filename": "^2.0.0", |  | ||||||
|         "set-blocking": "^2.0.0", |  | ||||||
|         "string-width": "^4.2.0", |         "string-width": "^4.2.0", | ||||||
|         "which-module": "^2.0.0", |         "y18n": "^5.0.5", | ||||||
|         "y18n": "^4.0.0", |         "yargs-parser": "^20.2.2" | ||||||
|         "yargs-parser": "^18.1.2" |  | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "emoji-regex": { |         "emoji-regex": { | ||||||
|  | @ -1951,13 +1896,9 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "yargs-parser": { |     "yargs-parser": { | ||||||
|       "version": "18.1.3", |       "version": "20.2.4", | ||||||
|       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", |       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", | ||||||
|       "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", |       "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" | ||||||
|       "requires": { |  | ||||||
|         "camelcase": "^5.0.0", |  | ||||||
|         "decamelize": "^1.2.0" |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@grpc/proto-loader": "^0.5.5", |     "@grpc/proto-loader": "^0.5.5", | ||||||
|     "bunyan": "^1.8.14", |     "bunyan": "^1.8.14", | ||||||
|     "eslint": "^7.8.1", |     "eslint": "^7.14.0", | ||||||
|     "grpc-uds": "^0.1.4", |     "grpc-uds": "^0.1.4", | ||||||
|     "handlebars": "^4.7.6", |     "handlebars": "^4.7.6", | ||||||
|     "js-yaml": "^3.14.0", |     "js-yaml": "^3.14.0", | ||||||
|  | @ -28,8 +28,8 @@ | ||||||
|     "request": "^2.88.2", |     "request": "^2.88.2", | ||||||
|     "ssh2": "^0.8.9", |     "ssh2": "^0.8.9", | ||||||
|     "uri-js": "^4.4.0", |     "uri-js": "^4.4.0", | ||||||
|     "uuid": "^8.3.0", |     "uuid": "^8.3.1", | ||||||
|     "winston": "^3.3.3", |     "winston": "^3.3.3", | ||||||
|     "yargs": "^15.4.1" |     "yargs": "^16.1.1" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver { | ||||||
|    * @param {*} datasetName |    * @param {*} datasetName | ||||||
|    */ |    */ | ||||||
|   async createShare(call, datasetName) { |   async createShare(call, datasetName) { | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
|     const sshClient = this.getSshClient(); |     const sshClient = this.getSshClient(); | ||||||
| 
 | 
 | ||||||
|     let properties; |     let properties; | ||||||
|  | @ -105,25 +105,25 @@ class ControllerZfsGenericDriver extends ControllerZfsSshBaseDriver { | ||||||
| 
 | 
 | ||||||
|         switch (this.options.iscsi.shareStrategy) { |         switch (this.options.iscsi.shareStrategy) { | ||||||
|           case "targetCli": |           case "targetCli": | ||||||
|             basename = this.options.iscsi.shareStragetyTargetCli.basename; |             basename = this.options.iscsi.shareStrategyTargetCli.basename; | ||||||
|             let setAttributesText = ""; |             let setAttributesText = ""; | ||||||
|             let setAuthText = ""; |             let setAuthText = ""; | ||||||
|             if (this.options.iscsi.shareStragetyTargetCli.tpg) { |             if (this.options.iscsi.shareStrategyTargetCli.tpg) { | ||||||
|               if (this.options.iscsi.shareStragetyTargetCli.tpg.attributes) { |               if (this.options.iscsi.shareStrategyTargetCli.tpg.attributes) { | ||||||
|                 for (const attributeName in this.options.iscsi |                 for (const attributeName in this.options.iscsi | ||||||
|                   .shareStragetyTargetCli.tpg.attributes) { |                   .shareStrategyTargetCli.tpg.attributes) { | ||||||
|                   const attributeValue = this.options.iscsi |                   const attributeValue = this.options.iscsi | ||||||
|                     .shareStragetyTargetCli.tpg.attributes[attributeName]; |                     .shareStrategyTargetCli.tpg.attributes[attributeName]; | ||||||
|                   setAttributesText += "\n"; |                   setAttributesText += "\n"; | ||||||
|                   setAttributesText += `set attribute ${attributeName}=${attributeValue}`; |                   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 |                 for (const attributeName in this.options.iscsi | ||||||
|                   .shareStragetyTargetCli.tpg.auth) { |                   .shareStrategyTargetCli.tpg.auth) { | ||||||
|                   const attributeValue = this.options.iscsi |                   const attributeValue = this.options.iscsi | ||||||
|                     .shareStragetyTargetCli.tpg.auth[attributeName]; |                     .shareStrategyTargetCli.tpg.auth[attributeName]; | ||||||
|                   setAttributesText += "\n"; |                   setAttributesText += "\n"; | ||||||
|                   setAttributesText += `set auth ${attributeName}=${attributeValue}`; |                   setAttributesText += `set auth ${attributeName}=${attributeValue}`; | ||||||
|                 } |                 } | ||||||
|  | @ -178,7 +178,7 @@ create /backstores/block/${iscsiName} | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async deleteShare(call, datasetName) { |   async deleteShare(call, datasetName) { | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
|     const sshClient = this.getSshClient(); |     const sshClient = this.getSshClient(); | ||||||
| 
 | 
 | ||||||
|     let response; |     let response; | ||||||
|  | @ -210,7 +210,7 @@ create /backstores/block/${iscsiName} | ||||||
|         iscsiName = iscsiName.toLowerCase(); |         iscsiName = iscsiName.toLowerCase(); | ||||||
|         switch (this.options.iscsi.shareStrategy) { |         switch (this.options.iscsi.shareStrategy) { | ||||||
|           case "targetCli": |           case "targetCli": | ||||||
|             basename = this.options.iscsi.shareStragetyTargetCli.basename; |             basename = this.options.iscsi.shareStrategyTargetCli.basename; | ||||||
|             response = await this.targetCliCommand( |             response = await this.targetCliCommand( | ||||||
|               ` |               ` | ||||||
| cd /iscsi | cd /iscsi | ||||||
|  | @ -260,14 +260,21 @@ delete ${iscsiName} | ||||||
|     const sshClient = this.getSshClient(); |     const sshClient = this.getSshClient(); | ||||||
|     data = data.trim(); |     data = data.trim(); | ||||||
| 
 | 
 | ||||||
|  |     let command = "sh"; | ||||||
|     let args = ["-c"]; |     let args = ["-c"]; | ||||||
|     let command = []; |     let taregetCliCommand = []; | ||||||
|     command.push(`echo "${data}"`.trim()); |     taregetCliCommand.push(`echo "${data}"`.trim()); | ||||||
|     command.push("|"); |     taregetCliCommand.push("|"); | ||||||
|     command.push("targetcli"); |     taregetCliCommand.push("targetcli"); | ||||||
| 
 | 
 | ||||||
|     args.push("'" + command.join(" ") + "'"); |     if (this.options.iscsi.shareStrategyTargetCli.sudoEnabled) { | ||||||
|     return sshClient.exec(sshClient.buildCommand("sh", args)); |       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 sshClient = this.getSshClient(); | ||||||
|     const options = {}; |     const options = {}; | ||||||
|     options.executor = new ZfsSshProcessManager(sshClient); |     options.executor = new ZfsSshProcessManager(sshClient); | ||||||
|  | @ -130,14 +130,36 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
| 
 | 
 | ||||||
|     if ( |     if ( | ||||||
|       this.options.zfs.hasOwnProperty("cli") && |       this.options.zfs.hasOwnProperty("cli") && | ||||||
|  |       this.options.zfs.cli && | ||||||
|       this.options.zfs.cli.hasOwnProperty("paths") |       this.options.zfs.cli.hasOwnProperty("paths") | ||||||
|     ) { |     ) { | ||||||
|       options.paths = this.options.zfs.cli.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); |     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() { |   getDatasetParentName() { | ||||||
|     let datasetParentName = this.options.zfs.datasetParentName; |     let datasetParentName = this.options.zfs.datasetParentName; | ||||||
|     datasetParentName = datasetParentName.replace(/\/$/, ""); |     datasetParentName = datasetParentName.replace(/\/$/, ""); | ||||||
|  | @ -160,7 +182,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async removeSnapshotsFromDatatset(datasetName, options = {}) { |   async removeSnapshotsFromDatatset(datasetName, options = {}) { | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     await zb.zfs.destroy(datasetName + "@%", options); |     await zb.zfs.destroy(datasetName + "@%", options); | ||||||
|   } |   } | ||||||
|  | @ -250,7 +272,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const driverZfsResourceType = this.getDriverZfsResourceType(); |     const driverZfsResourceType = this.getDriverZfsResourceType(); | ||||||
|     const sshClient = this.getSshClient(); |     const sshClient = this.getSshClient(); | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let datasetParentName = this.getVolumeParentDatasetName(); |     let datasetParentName = this.getVolumeParentDatasetName(); | ||||||
|     let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName(); |     let snapshotParentDatasetName = this.getDetachedSnapshotParentDatasetName(); | ||||||
|  | @ -671,8 +693,20 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|             this.options.zfs.datasetPermissionsMode, |             this.options.zfs.datasetPermissionsMode, | ||||||
|             properties.mountpoint.value, |             properties.mountpoint.value, | ||||||
|           ]); |           ]); | ||||||
|  |           if (this.getSudoEnabled()) { | ||||||
|  |             command = (await this.getSudoPath()) + " " + command; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|           driver.ctx.logger.verbose("set permission command: %s", command); |           driver.ctx.logger.verbose("set permission command: %s", command); | ||||||
|           response = await sshClient.exec(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
 |         // set ownership
 | ||||||
|  | @ -690,8 +724,18 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|                 : ""), |                 : ""), | ||||||
|             properties.mountpoint.value, |             properties.mountpoint.value, | ||||||
|           ]); |           ]); | ||||||
|  |           if (this.getSudoEnabled()) { | ||||||
|  |             command = (await this.getSudoPath()) + " " + command; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|           driver.ctx.logger.verbose("set ownership command: %s", command); |           driver.ctx.logger.verbose("set ownership command: %s", command); | ||||||
|           response = await sshClient.exec(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
 |         // set acls
 | ||||||
|  | @ -703,8 +747,18 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|               acl, |               acl, | ||||||
|               properties.mountpoint.value, |               properties.mountpoint.value, | ||||||
|             ]); |             ]); | ||||||
|  |             if (this.getSudoEnabled()) { | ||||||
|  |               command = (await this.getSudoPath()) + " " + command; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             driver.ctx.logger.verbose("set acl command: %s", command); |             driver.ctx.logger.verbose("set acl command: %s", command); | ||||||
|             response = await sshClient.exec(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) { |   async DeleteVolume(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let datasetParentName = this.getVolumeParentDatasetName(); |     let datasetParentName = this.getVolumeParentDatasetName(); | ||||||
|     let name = call.request.volume_id; |     let name = call.request.volume_id; | ||||||
|  | @ -877,7 +931,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|   async ControllerExpandVolume(call) { |   async ControllerExpandVolume(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const driverZfsResourceType = this.getDriverZfsResourceType(); |     const driverZfsResourceType = this.getDriverZfsResourceType(); | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let datasetParentName = this.getVolumeParentDatasetName(); |     let datasetParentName = this.getVolumeParentDatasetName(); | ||||||
|     let name = call.request.volume_id; |     let name = call.request.volume_id; | ||||||
|  | @ -978,7 +1032,11 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|     await this.expandVolume(call, datasetName); |     await this.expandVolume(call, datasetName); | ||||||
| 
 | 
 | ||||||
|     return { |     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, |       node_expansion_required: driverZfsResourceType == "volume" ? true : false, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  | @ -990,7 +1048,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|    */ |    */ | ||||||
|   async GetCapacity(call) { |   async GetCapacity(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let datasetParentName = this.getVolumeParentDatasetName(); |     let datasetParentName = this.getVolumeParentDatasetName(); | ||||||
| 
 | 
 | ||||||
|  | @ -1027,7 +1085,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|   async ListVolumes(call) { |   async ListVolumes(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const driverZfsResourceType = this.getDriverZfsResourceType(); |     const driverZfsResourceType = this.getDriverZfsResourceType(); | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let datasetParentName = this.getVolumeParentDatasetName(); |     let datasetParentName = this.getVolumeParentDatasetName(); | ||||||
|     let entries = []; |     let entries = []; | ||||||
|  | @ -1212,7 +1270,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|   async ListSnapshots(call) { |   async ListSnapshots(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const driverZfsResourceType = this.getDriverZfsResourceType(); |     const driverZfsResourceType = this.getDriverZfsResourceType(); | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let entries = []; |     let entries = []; | ||||||
|     let entries_length = 0; |     let entries_length = 0; | ||||||
|  | @ -1444,7 +1502,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|   async CreateSnapshot(call) { |   async CreateSnapshot(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const driverZfsResourceType = this.getDriverZfsResourceType(); |     const driverZfsResourceType = this.getDriverZfsResourceType(); | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     let detachedSnapshot = false; |     let detachedSnapshot = false; | ||||||
|     try { |     try { | ||||||
|  | @ -1678,7 +1736,7 @@ class ControllerZfsSshBaseDriver extends CsiBaseDriver { | ||||||
|    */ |    */ | ||||||
|   async DeleteSnapshot(call) { |   async DeleteSnapshot(call) { | ||||||
|     const driver = this; |     const driver = this; | ||||||
|     const zb = this.getZetabyte(); |     const zb = await this.getZetabyte(); | ||||||
| 
 | 
 | ||||||
|     const snapshot_id = call.request.snapshot_id; |     const snapshot_id = call.request.snapshot_id; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ const USER_AGENT = "democratic-csi-driver"; | ||||||
| 
 | 
 | ||||||
| class Client { | class Client { | ||||||
|   constructor(options = {}) { |   constructor(options = {}) { | ||||||
|     this.options = options; |     this.options = JSON.parse(JSON.stringify(options)); | ||||||
|     this.logger = console; |     this.logger = console; | ||||||
| 
 | 
 | ||||||
|     // default to v1.0 for now
 |     // default to v1.0 for now
 | ||||||
|  | @ -19,7 +19,7 @@ class Client { | ||||||
|       host: server.host, |       host: server.host, | ||||||
|       port: server.port, |       port: server.port, | ||||||
|       //userinfo: server.username + ":" + server.password,
 |       //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); |     return URI.serialize(options); | ||||||
|   } |   } | ||||||
|  | @ -55,13 +55,13 @@ class Client { | ||||||
|         headers: { |         headers: { | ||||||
|           Accept: "application/json", |           Accept: "application/json", | ||||||
|           "User-Agent": USER_AGENT, |           "User-Agent": USER_AGENT, | ||||||
|           "Content-Type": "application/json" |           "Content-Type": "application/json", | ||||||
|         }, |         }, | ||||||
|         json: true, |         json: true, | ||||||
|         qs: data, |         qs: data, | ||||||
|         agentOptions: { |         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); |         client.log_repsonse(...arguments, options); | ||||||
|  | @ -70,7 +70,12 @@ class Client { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         resolve(res); |         resolve(res); | ||||||
|       }).auth(client.options.username, client.options.password); |       }).auth( | ||||||
|  |         client.options.username, | ||||||
|  |         client.options.password, | ||||||
|  |         true, | ||||||
|  |         client.options.apiKey | ||||||
|  |       ); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -87,13 +92,13 @@ class Client { | ||||||
|         headers: { |         headers: { | ||||||
|           Accept: "application/json", |           Accept: "application/json", | ||||||
|           "User-Agent": USER_AGENT, |           "User-Agent": USER_AGENT, | ||||||
|           "Content-Type": "application/json" |           "Content-Type": "application/json", | ||||||
|         }, |         }, | ||||||
|         json: true, |         json: true, | ||||||
|         body: data, |         body: data, | ||||||
|         agentOptions: { |         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); |         client.log_repsonse(...arguments, options); | ||||||
|  | @ -102,7 +107,12 @@ class Client { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         resolve(res); |         resolve(res); | ||||||
|       }).auth(client.options.username, client.options.password); |       }).auth( | ||||||
|  |         client.options.username, | ||||||
|  |         client.options.password, | ||||||
|  |         true, | ||||||
|  |         client.options.apiKey | ||||||
|  |       ); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -119,13 +129,13 @@ class Client { | ||||||
|         headers: { |         headers: { | ||||||
|           Accept: "application/json", |           Accept: "application/json", | ||||||
|           "User-Agent": USER_AGENT, |           "User-Agent": USER_AGENT, | ||||||
|           "Content-Type": "application/json" |           "Content-Type": "application/json", | ||||||
|         }, |         }, | ||||||
|         json: true, |         json: true, | ||||||
|         body: data, |         body: data, | ||||||
|         agentOptions: { |         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); |         client.log_repsonse(...arguments, options); | ||||||
|  | @ -134,7 +144,12 @@ class Client { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         resolve(res); |         resolve(res); | ||||||
|       }).auth(client.options.username, client.options.password); |       }).auth( | ||||||
|  |         client.options.username, | ||||||
|  |         client.options.password, | ||||||
|  |         true, | ||||||
|  |         client.options.apiKey | ||||||
|  |       ); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -151,13 +166,13 @@ class Client { | ||||||
|         headers: { |         headers: { | ||||||
|           Accept: "application/json", |           Accept: "application/json", | ||||||
|           "User-Agent": USER_AGENT, |           "User-Agent": USER_AGENT, | ||||||
|           "Content-Type": "application/json" |           "Content-Type": "application/json", | ||||||
|         }, |         }, | ||||||
|         json: true, |         json: true, | ||||||
|         body: data, |         body: data, | ||||||
|         agentOptions: { |         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); |         client.log_repsonse(...arguments, options); | ||||||
|  | @ -166,7 +181,12 @@ class Client { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         resolve(res); |         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 |    * @param {*} call | ||||||
|    */ |    */ | ||||||
|   async NodeStageVolume(call) { |   async NodeStageVolume(call) { | ||||||
|  |     const driver = this; | ||||||
|     const mount = new Mount(); |     const mount = new Mount(); | ||||||
|     const filesystem = new Filesystem(); |     const filesystem = new Filesystem(); | ||||||
|     const iscsi = new ISCSI(); |     const iscsi = new ISCSI(); | ||||||
|  | @ -310,6 +311,32 @@ class CsiBaseDriver { | ||||||
|         device = `//${volume_context.server}/${volume_context.share}`; |         device = `//${volume_context.server}/${volume_context.share}`; | ||||||
|         break; |         break; | ||||||
|       case "iscsi": |       case "iscsi": | ||||||
|  |         let portals = []; | ||||||
|  |         if (volume_context.portal) { | ||||||
|  |           portals.push(volume_context.portal.trim()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (volume_context.portals) { | ||||||
|  |           volume_context.portals.split(",").forEach((portal) => { | ||||||
|  |             portals.push(portal.trim()); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 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
 |           // create DB entry
 | ||||||
|           // https://library.netapp.com/ecmdocs/ECMP1654943/html/GUID-8EC685B4-8CB6-40D8-A8D5-031A3899BCDC.html
 |           // 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)
 |           // put these options in place to force targets managed by csi to be explicitly attached (in the case of unclearn shutdown etc)
 | ||||||
|  | @ -319,37 +346,110 @@ class CsiBaseDriver { | ||||||
|           const nodeDBKeyPrefix = "node-db."; |           const nodeDBKeyPrefix = "node-db."; | ||||||
|           for (const key in normalizedSecrets) { |           for (const key in normalizedSecrets) { | ||||||
|             if (key.startsWith(nodeDBKeyPrefix)) { |             if (key.startsWith(nodeDBKeyPrefix)) { | ||||||
|             nodeDB[key.substr(nodeDBKeyPrefix.length)] = normalizedSecrets[key]; |               nodeDB[key.substr(nodeDBKeyPrefix.length)] = | ||||||
|  |                 normalizedSecrets[key]; | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|           await iscsi.iscsiadm.createNodeDBEntry( |           await iscsi.iscsiadm.createNodeDBEntry( | ||||||
|             volume_context.iqn, |             volume_context.iqn, | ||||||
|           volume_context.portal, |             portal, | ||||||
|             nodeDB |             nodeDB | ||||||
|           ); |           ); | ||||||
|           // login
 |           // login
 | ||||||
|         await iscsi.iscsiadm.login(volume_context.iqn, volume_context.portal); |           await iscsi.iscsiadm.login(volume_context.iqn, portal); | ||||||
| 
 | 
 | ||||||
|           // find device name
 |           // find device name
 | ||||||
|         device = `/dev/disk/by-path/ip-${volume_context.portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`; |           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
 |           // can take some time for device to show up, loop for some period
 | ||||||
|           result = await filesystem.pathExists(device); |           result = await filesystem.pathExists(device); | ||||||
|           let timer_start = Math.round(new Date().getTime() / 1000); |           let timer_start = Math.round(new Date().getTime() / 1000); | ||||||
|           let timer_max = 30; |           let timer_max = 30; | ||||||
|  |           let deviceCreated = result; | ||||||
|           while (!result) { |           while (!result) { | ||||||
|             await sleep(2000); |             await sleep(2000); | ||||||
|             result = await filesystem.pathExists(device); |             result = await filesystem.pathExists(device); | ||||||
|  | 
 | ||||||
|  |             if (result) { | ||||||
|  |               deviceCreated = true; | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             let current_time = Math.round(new Date().getTime() / 1000); |             let current_time = Math.round(new Date().getTime() / 1000); | ||||||
|             if (!result && current_time - timer_start > timer_max) { |             if (!result && current_time - timer_start > timer_max) { | ||||||
|             throw new GrpcError( |               driver.ctx.logger.warn( | ||||||
|               grpc.status.UNKNOWN, |  | ||||||
|                 `hit timeout waiting for device node to appear: ${device}` |                 `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; |         break; | ||||||
|       default: |       default: | ||||||
|         throw new GrpcError( |         throw new GrpcError( | ||||||
|  | @ -463,6 +563,7 @@ class CsiBaseDriver { | ||||||
|     const iscsi = new ISCSI(); |     const iscsi = new ISCSI(); | ||||||
|     let result; |     let result; | ||||||
|     let is_block = false; |     let is_block = false; | ||||||
|  |     let is_device_mapper = false; | ||||||
|     let block_device_info; |     let block_device_info; | ||||||
|     let access_type = "mount"; |     let access_type = "mount"; | ||||||
| 
 | 
 | ||||||
|  | @ -505,7 +606,28 @@ class CsiBaseDriver { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (is_block) { |     if (is_block) { | ||||||
|       if (block_device_info.tran == "iscsi") { |       let realBlockDeviceInfos = []; | ||||||
|  |       // detect if is a multipath device
 | ||||||
|  |       is_device_mapper = await filesystem.isDeviceMapperDevice( | ||||||
|  |         block_device_info.path | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       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
 |           // figure out which iscsi session this belongs to and logout
 | ||||||
|           // scan /dev/disk/by-path/ip-*?
 |           // scan /dev/disk/by-path/ip-*?
 | ||||||
|           // device = `/dev/disk/by-path/ip-${volume_context.portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`;
 |           // device = `/dev/disk/by-path/ip-${volume_context.portal}-iscsi-${volume_context.iqn}-lun-${volume_context.lun}`;
 | ||||||
|  | @ -522,7 +644,7 @@ class CsiBaseDriver { | ||||||
|             ) { |             ) { | ||||||
|               is_attached_to_session = session.attached_scsi_devices.host.devices.some( |               is_attached_to_session = session.attached_scsi_devices.host.devices.some( | ||||||
|                 (device) => { |                 (device) => { | ||||||
|                 if (device.attached_scsi_disk == block_device_info.name) { |                   if (device.attached_scsi_disk == block_device_info_i.name) { | ||||||
|                     return true; |                     return true; | ||||||
|                   } |                   } | ||||||
|                   return false; |                   return false; | ||||||
|  | @ -584,6 +706,7 @@ class CsiBaseDriver { | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (access_type == "block") { |     if (access_type == "block") { | ||||||
|       // remove touched file
 |       // remove touched file
 | ||||||
|  | @ -817,6 +940,7 @@ class CsiBaseDriver { | ||||||
|     let is_block = false; |     let is_block = false; | ||||||
|     let is_formatted; |     let is_formatted; | ||||||
|     let fs_type; |     let fs_type; | ||||||
|  |     let is_device_mapper = false; | ||||||
| 
 | 
 | ||||||
|     const volume_id = call.request.volume_id; |     const volume_id = call.request.volume_id; | ||||||
|     const volume_path = call.request.volume_path; |     const volume_path = call.request.volume_path; | ||||||
|  | @ -853,7 +977,24 @@ class CsiBaseDriver { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (is_block) { |     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") { |       if (is_formatted && access_type == "mount") { | ||||||
|         fs_info = await filesystem.getDeviceFilesystemInfo(device); |         fs_info = await filesystem.getDeviceFilesystemInfo(device); | ||||||
|         fs_type = fs_info.type; |         fs_type = fs_info.type; | ||||||
|  |  | ||||||
|  | @ -48,14 +48,165 @@ class Filesystem { | ||||||
|     const device_path = await filesystem.realpath(device); |     const device_path = await filesystem.realpath(device); | ||||||
|     const blockdevices = await filesystem.getAllBlockDevices(); |     const blockdevices = await filesystem.getAllBlockDevices(); | ||||||
| 
 | 
 | ||||||
|     return blockdevices.some((i) => { |     return blockdevices.some(async (i) => { | ||||||
|       if (i.path == device_path) { |       if ((await filesystem.realpath(i.path)) == device_path) { | ||||||
|         return true; |         return true; | ||||||
|       } |       } | ||||||
|       return false; |       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 |    * create symlink | ||||||
|    * |    * | ||||||
|  | @ -264,13 +415,20 @@ class Filesystem { | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     let is_device_mapper_device = await filesystem.isDeviceMapperDevice(device); | ||||||
|     result = await filesystem.realpath(device); |     result = await filesystem.realpath(device); | ||||||
|  | 
 | ||||||
|  |     if (is_device_mapper_device) { | ||||||
|  |       // multipath -r /dev/dm-0
 | ||||||
|  |       result = await filesystem.exec("multipath", ["-r", device]); | ||||||
|  |     } else { | ||||||
|       device_name = result.split("/").pop(); |       device_name = result.split("/").pop(); | ||||||
| 
 | 
 | ||||||
|       // echo 1 > /sys/block/sdb/device/rescan
 |       // echo 1 > /sys/block/sdb/device/rescan
 | ||||||
|       const sys_file = `/sys/block/${device_name}/device/rescan`; |       const sys_file = `/sys/block/${device_name}/device/rescan`; | ||||||
|       fs.writeFileSync(sys_file, "1"); |       fs.writeFileSync(sys_file, "1"); | ||||||
|     } |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * expand a give filesystem |    * expand a give filesystem | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ class ISCSI { | ||||||
| 
 | 
 | ||||||
|     if (!options.executor) { |     if (!options.executor) { | ||||||
|       options.executor = { |       options.executor = { | ||||||
|         spawn: cp.spawn |         spawn: cp.spawn, | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -47,7 +47,7 @@ class ISCSI { | ||||||
|         const entries = result.stdout.trim().split("\n"); |         const entries = result.stdout.trim().split("\n"); | ||||||
|         const interfaces = []; |         const interfaces = []; | ||||||
|         let fields; |         let fields; | ||||||
|         entries.forEach(entry => { |         entries.forEach((entry) => { | ||||||
|           fields = entry.split(" "); |           fields = entry.split(" "); | ||||||
|           interfaces.push({ |           interfaces.push({ | ||||||
|             iface_name: fields[0], |             iface_name: fields[0], | ||||||
|  | @ -55,7 +55,7 @@ class ISCSI { | ||||||
|             hwaddress: getIscsiValue(fields[1].split(",")[1]), |             hwaddress: getIscsiValue(fields[1].split(",")[1]), | ||||||
|             ipaddress: getIscsiValue(fields[1].split(",")[2]), |             ipaddress: getIscsiValue(fields[1].split(",")[2]), | ||||||
|             net_ifacename: getIscsiValue(fields[1].split(",")[3]), |             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 entries = result.stdout.trim().split("\n"); | ||||||
|         const i = {}; |         const i = {}; | ||||||
|         let fields, key, value; |         let fields, key, value; | ||||||
|         entries.forEach(entry => { |         entries.forEach((entry) => { | ||||||
|           if (entry.startsWith("#")) return; |           if (entry.startsWith("#")) return; | ||||||
|           fields = entry.split("="); |           fields = entry.split("="); | ||||||
|           key = fields[0].trim(); |           key = fields[0].trim(); | ||||||
|  | @ -103,7 +103,7 @@ class ISCSI { | ||||||
|           "-p", |           "-p", | ||||||
|           portal, |           portal, | ||||||
|           "-o", |           "-o", | ||||||
|           "new" |           "new", | ||||||
|         ]); |         ]); | ||||||
|         await iscsi.exec(options.paths.iscsiadm, args); |         await iscsi.exec(options.paths.iscsiadm, args); | ||||||
|         for (let attribute in attributes) { |         for (let attribute in attributes) { | ||||||
|  | @ -120,7 +120,7 @@ class ISCSI { | ||||||
|             "--name", |             "--name", | ||||||
|             attribute, |             attribute, | ||||||
|             "--value", |             "--value", | ||||||
|             attributes[attribute] |             attributes[attribute], | ||||||
|           ]); |           ]); | ||||||
|           await iscsi.exec(options.paths.iscsiadm, args); |           await iscsi.exec(options.paths.iscsiadm, args); | ||||||
|         } |         } | ||||||
|  | @ -142,7 +142,7 @@ class ISCSI { | ||||||
|           "-p", |           "-p", | ||||||
|           portal, |           portal, | ||||||
|           "-o", |           "-o", | ||||||
|           "delete" |           "delete", | ||||||
|         ]); |         ]); | ||||||
|         await iscsi.exec(options.paths.iscsiadm, args); |         await iscsi.exec(options.paths.iscsiadm, args); | ||||||
|       }, |       }, | ||||||
|  | @ -174,7 +174,7 @@ class ISCSI { | ||||||
|         const entries = result.stdout.trim().split("\n"); |         const entries = result.stdout.trim().split("\n"); | ||||||
|         const sessions = []; |         const sessions = []; | ||||||
|         let fields; |         let fields; | ||||||
|         entries.forEach(entry => { |         entries.forEach((entry) => { | ||||||
|           fields = entry.split(" "); |           fields = entry.split(" "); | ||||||
|           sessions.push({ |           sessions.push({ | ||||||
|             protocol: entry.split(":")[0], |             protocol: entry.split(":")[0], | ||||||
|  | @ -182,7 +182,7 @@ class ISCSI { | ||||||
|             portal: fields[2].split(",")[0], |             portal: fields[2].split(",")[0], | ||||||
|             target_portal_group_tag: fields[2].split(",")[1], |             target_portal_group_tag: fields[2].split(",")[1], | ||||||
|             iqn: fields[3].split(":")[0], |             iqn: fields[3].split(":")[0], | ||||||
|             target: fields[3].split(":")[1] |             target: fields[3].split(":")[1], | ||||||
|           }); |           }); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | @ -212,6 +212,7 @@ class ISCSI { | ||||||
|           return []; |           return []; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         let currentTarget; | ||||||
|         let sessionGroups = []; |         let sessionGroups = []; | ||||||
|         let currentSession = []; |         let currentSession = []; | ||||||
| 
 | 
 | ||||||
|  | @ -221,13 +222,21 @@ class ISCSI { | ||||||
|         entries.shift(); |         entries.shift(); | ||||||
|         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++) { |         for (let i = 0; i < entries.length; i++) { | ||||||
|           let entry = entries[i]; |           let entry = entries[i]; | ||||||
|           if (entry.startsWith("Target:")) { |           if (entry.startsWith("Target:")) { | ||||||
|  |             currentTarget = entry; | ||||||
|  |           } else if (entry.trim().startsWith("Current Portal:")) { | ||||||
|             if (currentSession.length > 0) { |             if (currentSession.length > 0) { | ||||||
|               sessionGroups.push(currentSession); |               sessionGroups.push(currentSession); | ||||||
|             } |             } | ||||||
|             currentSession = [entry]; |             currentSession = [currentTarget, entry]; | ||||||
|           } else { |           } else { | ||||||
|             currentSession.push(entry); |             currentSession.push(entry); | ||||||
|           } |           } | ||||||
|  | @ -261,11 +270,7 @@ class ISCSI { | ||||||
|               .trim() |               .trim() | ||||||
|               .replace(/ /g, "_") |               .replace(/ /g, "_") | ||||||
|               .replace(/\W/g, ""); |               .replace(/\W/g, ""); | ||||||
|             let value = line |             let value = line.split(":").slice(1).join(":").trim(); | ||||||
|               .split(":") |  | ||||||
|               .slice(1) |  | ||||||
|               .join(":") |  | ||||||
|               .trim(); |  | ||||||
| 
 | 
 | ||||||
|             if (currentSection) { |             if (currentSection) { | ||||||
|               session[currentSection] = session[currentSection] || {}; |               session[currentSection] = session[currentSection] || {}; | ||||||
|  | @ -282,7 +287,7 @@ class ISCSI { | ||||||
|                         .split(":") |                         .split(":") | ||||||
|                         .slice(1) |                         .slice(1) | ||||||
|                         .join(":") |                         .join(":") | ||||||
|                         .trim() |                         .trim(), | ||||||
|                     }; |                     }; | ||||||
|                     while ( |                     while ( | ||||||
|                       sessionLines[j + 1] && |                       sessionLines[j + 1] && | ||||||
|  | @ -308,7 +313,7 @@ class ISCSI { | ||||||
|                             .split(":") |                             .split(":") | ||||||
|                             .slice(1) |                             .slice(1) | ||||||
|                             .join(":") |                             .join(":") | ||||||
|                             .trim() |                             .trim(), | ||||||
|                         }); |                         }); | ||||||
|                       } |                       } | ||||||
| 
 | 
 | ||||||
|  | @ -322,7 +327,7 @@ class ISCSI { | ||||||
|                   key = key.charAt(0).toLowerCase() + key.slice(1); |                   key = key.charAt(0).toLowerCase() + key.slice(1); | ||||||
|                   key = key.replace( |                   key = key.replace( | ||||||
|                     /[A-Z]/g, |                     /[A-Z]/g, | ||||||
|                     letter => `_${letter.toLowerCase()}` |                     (letter) => `_${letter.toLowerCase()}` | ||||||
|                   ); |                   ); | ||||||
|                   break; |                   break; | ||||||
|               } |               } | ||||||
|  | @ -367,12 +372,12 @@ class ISCSI { | ||||||
| 
 | 
 | ||||||
|         const entries = result.stdout.trim().split("\n"); |         const entries = result.stdout.trim().split("\n"); | ||||||
|         const targets = []; |         const targets = []; | ||||||
|         entries.forEach(entry => { |         entries.forEach((entry) => { | ||||||
|           targets.push({ |           targets.push({ | ||||||
|             portal: entry.split(",")[0], |             portal: entry.split(",")[0], | ||||||
|             target_portal_group_tag: entry.split(" ")[0].split(",")[1], |             target_portal_group_tag: entry.split(" ")[0].split(",")[1], | ||||||
|             iqn: entry.split(" ")[1].split(":")[0], |             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; |         return true; | ||||||
|       } |       }, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue