diff --git a/.UNRAID/FastAPI-DLS.xml b/.UNRAID/FastAPI-DLS.xml new file mode 100644 index 0000000..f6bf52b --- /dev/null +++ b/.UNRAID/FastAPI-DLS.xml @@ -0,0 +1,48 @@ + + + FastAPI-DLS + collinwebdesigns/fastapi-dls:latest + https://hub.docker.com/r/collinwebdesigns/fastapi-dls + br0 + + sh + false + + + Source: +https://git.collinwebdesigns.de/oscar.krause/fastapi-dls#docker + +Make sure you create these certificates before starting the container for the first time: +``` +# Check https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/tree/main/#docker for more information: +WORKING_DIR=/mnt/user/appdata/fastapi-dls/cert +mkdir -p $WORKING_DIR +cd $WORKING_DIR +# create instance private and public key for singing JWT's +openssl genrsa -out $WORKING_DIR/instance.private.pem 2048 +openssl rsa -in $WORKING_DIR/instance.private.pem -outform PEM -pubout -out $WORKING_DIR/instance.public.pem +# create ssl certificate for integrated webserver (uvicorn) - because clients rely on ssl +openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $WORKING_DIR/webserver.key -out $WORKING_DIR/webserver.crt +``` + + + https://[IP]:[PORT:443] + + https://git.collinwebdesigns.de/uploads/-/system/project/avatar/106/png-transparent-nvidia-grid-logo-business-nvidia-electronics-text-trademark.png?width=64 + --restart always + + + 1679161568 + + + + 443 + /mnt/user/appdata/fastapi-dls/cert + 443 + /mnt/user/appdata/fastapi-dls/data + + + sqlite:////app/database/db.sqlite + true + 90 + diff --git a/.UNRAID/setup_vgpu_license.sh b/.UNRAID/setup_vgpu_license.sh new file mode 100644 index 0000000..c3c93a0 --- /dev/null +++ b/.UNRAID/setup_vgpu_license.sh @@ -0,0 +1,197 @@ +#!/bin/bash + +# This script automates the licensing of the vGPU guest driver +# on Unraid boot. Set the Schedule to: "At Startup of Array". +# +# Relies on FastAPI-DLS for the licensing. +# It assumes FeatureType=1 (vGPU), change it as you see fit in line <114> +# +# Requires `eflutils` to be installed in the system for `nvidia-gridd` to run +# To Install it: +# 1) You might find it here: https://packages.slackware.com/ (choose the 64bit version of Slackware) +# 2) Download the package and put it in /boot/extra to be installed on boot +# 3) a. Reboot to install it, OR +# b. Run `upgradepkg --install-new /boot/extra/elfutils*` +# [i]: Make sure to have only one version of elfutils, otherwise you might run into issues + +# Sources and docs: +# https://docs.nvidia.com/grid/15.0/grid-vgpu-user-guide/index.html#configuring-nls-licensed-client-on-linux +# + +################################################ +# MAKE SURE YOU CHANGE THESE VARIABLES # +################################################ + +###### CHANGE ME! +# IP and PORT of FastAPI-DLS +DLS_IP=192.168.0.123 +DLS_PORT=443 +# Token folder, must be on a filesystem that supports +# linux filesystem permissions (eg: ext4,xfs,btrfs...) +TOKEN_PATH=/mnt/user/system/nvidia +PING=$(which ping) + +# Check if the License is applied +if [[ "$(nvidia-smi -q | grep "Expiry")" == *Expiry* ]]; then + echo " [i] Your vGPU Guest drivers are already licensed." + echo " [i] $(nvidia-smi -q | grep "Expiry")" + echo " [<] Exiting..." + exit 0 +fi + +# Check if the FastAPI-DLS server is reachable +# Check if the License is applied +MAX_RETRIES=30 +for i in $(seq 1 $MAX_RETRIES); do + echo -ne "\r [>] Attempt $i to connect to $DLS_IP." + if ping -c 1 $DLS_IP >/dev/null 2>&1; then + echo -e "\n [*] Connection successful." + break + fi + if [ $i -eq $MAX_RETRIES ]; then + echo -e "\n [!] Connection failed after $MAX_RETRIES attempts." + echo -e "\n [<] Exiting..." + exit 1 + fi + sleep 1 +done + +# Check if the token folder exists +if [ -d "${TOKEN_PATH}" ]; then + echo " [*] Token Folder exists. Proceeding..." +else + echo " [!] Token Folder does not exists or not ready yet. Exiting." + echo " [!] Token Folder Specified: ${TOKEN_PATH}" + exit 1 +fi + +# Check if elfutils are installed, otherwise nvidia-gridd service +# wont start +if [ "$(grep -R "elfutils" /var/log/packages/* | wc -l)" != 0 ]; then + echo " [*] Elfutils is installed, proceeding..." +else + echo " [!] Elfutils is not installed, downloading and installing..." + echo " [!] Downloading elfutils to /boot/extra" + echo " [i] This script will download elfutils from slackware64-15.0 repository." + echo " [i] If you have a different version of Unraid (6.11.5), you might want to" + echo " [i] download and install a suitable version manually from the slackware" + echo " [i] repository, and put it in /boot/extra to be install on boot." + echo " [i] You may also install it by running: " + echo " [i] upgradepkg --install-new /path/to/elfutils-*.txz" + echo "" + echo " [>] Downloading elfutils from slackware64-15.0 repository:" + wget -q -nc --show-progress --progress=bar:force:noscroll -P /boot/extra https://slackware.uk/slackware/slackware64-15.0/slackware64/l/elfutils-0.186-x86_64-1.txz 2>/dev/null \ + || { echo " [!] Error while downloading elfutils, please download it and install it manually."; exit 1; } + echo "" + if upgradepkg --install-new /boot/extra/elfutils-0.186-x86_64-1.txz + then + echo " [*] Elfutils installed and will be installed automatically on boot" + else + echo " [!] Error while installing, check logs..." + exit 1 + fi +fi + +echo " [~] Sleeping for 60 seconds before continuing..." +echo " [i] The script is waiting until the boot process settles down." + +for i in {60..1}; do + printf "\r [~] %d seconds remaining" "$i" + sleep 1 +done + +printf "\n" + +create_token () { + echo " [>] Creating new token..." + if ${PING} -c1 ${DLS_IP} > /dev/null 2>&1 + then + # curl --insecure -L -X GET https://${DLS_IP}:${DLS_PORT}/-/client-token -o ${TOKEN_PATH}/client_configuration_token_"$(date '+%d-%m-%Y-%H-%M-%S')".tok || { echo " [!] Could not get the token, please check the server."; exit 1;} + wget -q -nc -4c --no-check-certificate --show-progress --progress=bar:force:noscroll -O "${TOKEN_PATH}"/client_configuration_token_"$(date '+%d-%m-%Y-%H-%M-%S')".tok https://${DLS_IP}:${DLS_PORT}/-/client-token \ + || { echo " [!] Could not get the token, please check the server."; exit 1;} + chmod 744 "${TOKEN_PATH}"/*.tok || { echo " [!] Could not chmod the tokens."; exit 1; } + echo "" + echo " [*] Token downloaded and stored in ${TOKEN_PATH}." + else + echo " [!] Could not get token, DLS server unavailable ." + exit 1 + fi +} + +setup_run () { + echo " [>] Setting up gridd.conf" + cp /etc/nvidia/gridd.conf.template /etc/nvidia/gridd.conf || { echo " [!] Error configuring gridd.conf, did you install the drivers correctly?"; exit 1; } + sed -i 's/FeatureType=0/FeatureType=1/g' /etc/nvidia/gridd.conf + echo "ClientConfigTokenPath=${TOKEN_PATH}" >> /etc/nvidia/gridd.conf + echo " [>] Creating /var/lib/nvidia folder structure" + mkdir -p /var/lib/nvidia/GridLicensing + echo " [>] Starting nvidia-gridd" + if pgrep nvidia-gridd >/dev/null 2>&1; then + echo " [!] nvidia-gridd service is running. Closing." + sh /usr/lib/nvidia/sysv/nvidia-gridd stop + stop_exit_code=$? + if [ $stop_exit_code -eq 0 ]; then + echo " [*] nvidia-gridd service stopped successfully." + else + echo " [!] Error while stopping nvidia-gridd service." + exit 1 + fi + + # Kill the service if it does not close + if pgrep nvidia-gridd >/dev/null 2>&1; then + kill -9 "$(pgrep nvidia-gridd)" || { + echo " [!] Error while closing nvidia-gridd service" + exit 1 + } + fi + + echo " [*] Restarting nvidia-gridd service." + sh /usr/lib/nvidia/sysv/nvidia-gridd start + + if pgrep nvidia-gridd >/dev/null 2>&1; then + echo " [*] Service started, PID: $(pgrep nvidia-gridd)" + else + echo -e " [!] Error while starting nvidia-gridd service. Use strace -f nvidia-gridd to debug.\n [i] Check if elfutils is installed.\n [i] strace is not installed by default." + exit 1 + fi + else + sh /usr/lib/nvidia/sysv/nvidia-gridd start + + if pgrep nvidia-gridd >/dev/null 2>&1; then + echo " [*] Service started, PID: $(pgrep nvidia-gridd)" + else + echo -e " [!] Error while starting nvidia-gridd service. Use strace -f nvidia-gridd to debug.\n [i] Check if elfutils is installed.\n [i] strace is not installed by default." + exit 1 + fi + fi +} + +for token in "${TOKEN_PATH}"/*; do + if [ "${token: -4}" == ".tok" ] + then + echo " [*] Tokens found..." + setup_run + else + echo " [!] No Tokens found..." + create_token + setup_run + fi +done + +while true; do + if nvidia-smi -q | grep "Expiry" >/dev/null 2>&1; then + echo " [>] vGPU licensed!" + echo " [i] $(nvidia-smi -q | grep "Expiry")" + break + else + echo -ne " [>] vGPU not licensed yet... Checking again in 5 seconds\c" + for i in {1..5}; do + sleep 1 + echo -ne ".\c" + done + echo -ne "\r\c" + fi +done + +echo " [>] Done..." +exit 0 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 93188ca..69d8ba9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,12 +20,13 @@ build:docker: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' tags: [ docker ] before_script: - - echo -e "VERSION=$CI_BUILD_REF_NAME\nCOMMIT=$CI_COMMIT_SHA" > version.env # COMMIT=`git rev-parse HEAD` + - docker buildx inspect + - docker buildx create --use script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - IMAGE=$CI_REGISTRY/$CI_PROJECT_PATH/$CI_BUILD_REF_NAME:$CI_BUILD_REF - - docker build . --tag $IMAGE - - docker push $IMAGE + - docker buildx build --progress=plain --platform linux/amd64,linux/arm64 --build-arg VERSION=$CI_BUILD_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE --push . + - docker buildx imagetools inspect $IMAGE - echo "CS_IMAGE=$IMAGE" > container_scanning.env artifacts: reports: @@ -217,6 +218,8 @@ secret_detection: - if: $SECRET_DETECTION_DISABLED when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" + before_script: + - git config --global --add safe.directory $CI_PROJECT_DIR semgrep-sast: rules: @@ -262,21 +265,22 @@ deploy:docker: extends: .deploy stage: deploy before_script: - - echo -e "VERSION=$CI_BUILD_REF_NAME\nCOMMIT=$CI_COMMIT_SHA" > version.env - echo "Building docker image for commit $CI_COMMIT_SHA with version $CI_BUILD_REF_NAME" script: - - echo "GitLab-Registry" + - echo "========== GitLab-Registry ==========" - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - - docker build . --tag $CI_REGISTRY/$CI_PROJECT_PATH/$CI_BUILD_REF_NAME:$CI_BUILD_REF_NAME - - docker build . --tag $CI_REGISTRY/$CI_PROJECT_PATH/$CI_BUILD_REF_NAME:latest - - docker push $CI_REGISTRY/$CI_PROJECT_PATH/$CI_BUILD_REF_NAME:$CI_BUILD_REF_NAME - - docker push $CI_REGISTRY/$CI_PROJECT_PATH/$CI_BUILD_REF_NAME:latest - - echo "Docker-Hub" + - IMAGE=$CI_REGISTRY/$CI_PROJECT_PATH/$CI_BUILD_REF_NAME + - docker build . --build-arg VERSION=$CI_BUILD_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:$CI_BUILD_REF_NAME + - docker build . --build-arg VERSION=$CI_BUILD_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:latest + - docker push $IMAGE:$CI_BUILD_REF_NAME + - docker push $IMAGE:latest + - echo "========== Docker-Hub ==========" - docker login -u $PUBLIC_REGISTRY_USER -p $PUBLIC_REGISTRY_TOKEN - - docker build . --tag $PUBLIC_REGISTRY_USER/$CI_PROJECT_NAME:$CI_BUILD_REF_NAME - - docker build . --tag $PUBLIC_REGISTRY_USER/$CI_PROJECT_NAME:latest - - docker push $PUBLIC_REGISTRY_USER/$CI_PROJECT_NAME:$CI_BUILD_REF_NAME - - docker push $PUBLIC_REGISTRY_USER/$CI_PROJECT_NAME:latest + - IMAGE=$PUBLIC_REGISTRY_USER/$CI_PROJECT_NAME + - docker build . --build-arg VERSION=$CI_BUILD_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:$CI_BUILD_REF_NAME + - docker build . --build-arg VERSION=$CI_BUILD_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:latest + - docker push $IMAGE:$CI_BUILD_REF_NAME + - docker push $IMAGE:latest deploy:apt: # doc: https://git.collinwebdesigns.de/help/user/packages/debian_repository/index.md#install-a-package diff --git a/Dockerfile b/Dockerfile index e92f5dc..99c76bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,9 @@ FROM python:3.11-alpine +ARG VERSION +ARG COMMIT="" +RUN echo -e "VERSION=$VERSION\nCOMMIT=$COMMIT" > /version.env + COPY requirements.txt /tmp/requirements.txt RUN apk update \ @@ -11,7 +15,6 @@ RUN apk update \ && apk del build-deps COPY app /app -COPY version.env /version.env COPY README.md /README.md HEALTHCHECK --start-period=30s --interval=10s --timeout=5s --retries=3 CMD curl --insecure --fail https://localhost/-/health || exit 1 diff --git a/README.md b/README.md index add9f24..4d78b1e 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Only the clients need a connection to this service on configured port. **Official Links** -- https://git.collinwebdesigns.de/oscar.krause/fastapi-dls -- https://gitea.publichub.eu/oscar.krause/fastapi-dls -- Docker Image `collinwebdesigns/fastapi-dls:latest` +- https://git.collinwebdesigns.de/oscar.krause/fastapi-dls (Private Git) +- https://gitea.publichub.eu/oscar.krause/fastapi-dls (Public Git) +- https://hub.docker.com/r/collinwebdesigns/fastapi-dls (Docker-Hub `collinwebdesigns/fastapi-dls:latest`) *All other repositories are forks! (which is no bad - just for information and bug reports)* @@ -145,9 +145,9 @@ This is only to test whether the service starts successfully. ```shell cd /opt/fastapi-dls/app -su - www-data -c "/opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app" +sudo -u www-data /opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app # or -sudo -u www-data -c "/opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app" +su - www-data -c "/opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app" ``` **Create config file** @@ -247,6 +247,8 @@ This is only to test whether the service starts successfully. BASE_DIR=/opt/fastapi-dls SERVICE_USER=dls cd ${BASE_DIR} +sudo -u ${SERVICE_USER} ${BASE_DIR}/venv/bin/uvicorn main:app --app-dir=${BASE_DIR}/app +# or su - ${SERVICE_USER} -c "${BASE_DIR}/venv/bin/uvicorn main:app --app-dir=${BASE_DIR}/app" ``` @@ -308,7 +310,7 @@ Packages are available here: Successful tested with: -- Debian 12 (Bookworm) (works but not recommended because it is currently in *testing* state) +- Debian 12 (Bookworm) - Ubuntu 22.10 (Kinetic Kudu) Not working with: @@ -352,6 +354,19 @@ pacman -U --noconfirm fastapi-dls.pkg.tar.zst Start with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`. +## unRAID + +1. Download [this xml file](.UNRAID/FastAPI-DLS.xml) +2. Put it in /boot/config/plugins/dockerMan/templates-user/ +3. Go to Docker page, scroll down to `Add Container`, click on Template list and choose `FastAPI-DLS` +4. Open terminal/ssh, follow the instructions in overview description +5. Setup your container `IP`, `Port`, `DLS_URL` and `DLS_PORT` +6. Apply and let it boot up + +*Unraid users must also make sure they have Host access to custom networks enabled if unraid is the vgpu guest*. + +Continue [here](#unraid-guest) for docker guest setup. + ## Let's Encrypt Certificate (optional) If you're using installation via docker, you can use `traefik`. Please refer to their documentation. @@ -402,6 +417,7 @@ Successfully tested with this package versions: | vGPU Suftware | vGPU Manager | Linux Driver | Windows Driver | Release Date | |---------------|--------------|--------------|----------------|---------------| +| `15.2` | `525.105.14` | `525.105.17` | `528.89` | March 2023 | | `15.1` | `525.85.07` | `525.85.05` | `528.24` | January 2023 | | `15.0` | `525.60.12` | `525.60.13` | `527.41` | December 2022 | | `14.4` | `510.108.03` | `510.108.03` | `514.08` | December 2022 | @@ -459,7 +475,7 @@ Restart-Service NVDisplay.ContainerLocalSystem Check licensing status: ```shell -& 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe' -q | Select-String "License" +& 'nvidia-smi' -q | Select-String "License" ``` Output should be something like: @@ -471,6 +487,19 @@ vGPU Software Licensed Product Done. For more information check [troubleshoot section](#troubleshoot). +## unRAID Guest + +1. Make sure you create a folder in a linux filesystem (BTRFS/XFS/EXT4...), I recommend `/mnt/user/system/nvidia` (this is where docker and libvirt preferences are saved, so it's a good place to have that) +2. Edit the script to put your `DLS_IP`, `DLS_PORT` and `TOKEN_PATH`, properly +3. Install `User Scripts` plugin from *Community Apps* (the Apps page, or google User Scripts Unraid if you're not using CA) +4. Go to `Settings > Users Scripts > Add New Script` +5. Give it a name (the name must not contain spaces preferably) +6. Click on the *gear icon* to the left of the script name then edit script +7. Paste the script and save +8. Set schedule to `At First Array Start Only` +9. Click on Apply + + # Endpoints ### `GET /` @@ -673,4 +702,8 @@ The error message can safely be ignored (since we have no license limitation :P) Thanks to vGPU community and all who uses this project and report bugs. -Special thanks to @samicrusader who created build file for ArchLinux and @cyrus who wrote the section for openSUSE. +Special thanks to + +- @samicrusader who created build file for ArchLinux +- @cyrus who wrote the section for openSUSE +- @midi who wrote the section for unRAID diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..60e0a87 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,27 @@ +# Roadmap + +I am planning to implement the following features in the future. + + +## HA - High Availability + +Support Failover-Mode (secondary ip address) as in official DLS. + +**Note**: There is no Load-Balancing / Round-Robin HA Mode supported! If you want to use that, consider to use +Docker-Swarm with shared/cluster database (e.g. postgres). + +*See [ha branch](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/tree/ha) for current status.* + + +## UI - User Interface + +Add a user interface to manage origins and leases. + +*See [ui branch](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/tree/ui) for current status.* + + +## Config Database + +Instead of using environment variables, configuration files and manually create certificates, store configs and +certificates in database (like origins and leases). Also, there should be provided a startup assistant to prefill +required attributes and create instance-certificates. This is more user-friendly and should improve fist setup.