diff --git a/.DEBIAN/conffiles b/.DEBIAN/conffiles index cd8fdd7..008d731 100644 --- a/.DEBIAN/conffiles +++ b/.DEBIAN/conffiles @@ -1,2 +1 @@ /etc/fastapi-dls/env -/etc/systemd/system/fastapi-dls.service diff --git a/.DEBIAN/postinst b/.DEBIAN/postinst index fbf9b82..8213736 100644 --- a/.DEBIAN/postinst +++ b/.DEBIAN/postinst @@ -3,7 +3,7 @@ WORKING_DIR=/usr/share/fastapi-dls CONFIG_DIR=/etc/fastapi-dls -if [[ ! -f $CONFIG_DIR/instance.private.pem ]]; then +if [ ! -f $CONFIG_DIR/instance.private.pem ]; then echo "> Create dls-instance keypair ..." openssl genrsa -out $CONFIG_DIR/instance.private.pem 2048 openssl rsa -in $CONFIG_DIR/instance.private.pem -outform PEM -pubout -out $CONFIG_DIR/instance.public.pem @@ -12,8 +12,8 @@ else fi while true; do - [[ -f $CONFIG_DIR/webserver.key ]] && default_answer="N" || default_answer="Y" - [[ $default_answer == "Y" ]] && V="Y/n" || V="y/N" + [ -f $CONFIG_DIR/webserver.key ] && default_answer="N" || default_answer="Y" + [ $default_answer == "Y" ] && V="Y/n" || V="y/N" read -p "> Do you wish to create self-signed webserver certificate? [${V}]" yn yn=${yn:-$default_answer} # ${parameter:-word} If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted. case $yn in @@ -27,7 +27,7 @@ while true; do esac done -if [[ -f $CONFIG_DIR/webserver.key ]]; then +if [ -f $CONFIG_DIR/webserver.key ]; then echo "> Starting service ..." systemctl start fastapi-dls.service diff --git a/.DEBIAN/postrm b/.DEBIAN/postrm index b99d0fa..9c715dd 100755 --- a/.DEBIAN/postrm +++ b/.DEBIAN/postrm @@ -1,8 +1,9 @@ #!/bin/bash -if [[ -f /etc/systemd/system/fastapi-dls.service ]]; then - echo "> Removing service file." - rm /etc/systemd/system/fastapi-dls.service -fi +# is removed automatically +#if [ "$1" = purge ] && [ -d /usr/share/fastapi-dls ]; then +# echo "> Removing app." +# rm -r /usr/share/fastapi-dls +#fi -# todo +echo -e "> Done." diff --git a/.DEBIAN/prerm b/.DEBIAN/prerm index 296c995..3678725 100755 --- a/.DEBIAN/prerm +++ b/.DEBIAN/prerm @@ -1,5 +1,3 @@ #!/bin/bash echo -e "> Starting uninstallation of 'fastapi-dls'!" - -# todo diff --git a/README.md b/README.md index 86f27ce..28a7bdc 100644 --- a/README.md +++ b/README.md @@ -206,13 +206,17 @@ Packages are available here: ```shell pacman -Sy FILENAME=/opt/fastapi-dls.pkg.tar.zst -url -o $FILENAME + +curl -o $FILENAME +# or +wget -O $FILENAME + pacman -U --noconfirm fastapi-dls.pkg.tar.zst ``` Start with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`. -## Let's Encrypt Certificate +## Let's Encrypt Certificate (optional) If you're using installation via docker, you can use `traefik`. Please refer to their documentation. @@ -266,26 +270,67 @@ Successfully tested with this package versions: ## Linux +Download *client-token* and place it into `/etc/nvidia/ClientConfigToken`: + +```shell +curl --insecure -L -X GET https:///-/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok +# or +wget --no-check-certificate -O /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok https:///-/client-token +``` + +Restart `nvidia-gridd` service: + ```shell -curl --insecure -L -X GET https:///client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok service nvidia-gridd restart +``` + +Check licensing status: + +```shell nvidia-smi -q | grep "License" ``` -## Windows +Output should be something like: -Download file and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`. -Now restart `NvContainerLocalSystem` service. - -**Power-Shell** - -```Shell -curl.exe --insecure -L -X GET https:///client-token -o "C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken\client_configuration_token_$($(Get-Date).tostring('dd-MM-yy-hh-mm-ss')).tok" -Restart-Service NVDisplay.ContainerLocalSystem -'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe' -q | Select-String "License" +```text +vGPU Software Licensed Product + License Status : Licensed (Expiry: YYYY-M-DD hh:mm:ss GMT) ``` -## Endpoints +Done. For more information check [troubleshoot section](#troubleshoot). + +## Windows + +**Power-Shell** (run as administrator!) + +Download *client-token* and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`: + +```shell +curl.exe --insecure -L -X GET https:///-/client-token -o "C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken\client_configuration_token_$($(Get-Date).tostring('dd-MM-yy-hh-mm-ss')).tok" +``` + +Restart `NvContainerLocalSystem` service: + +```Shell +Restart-Service NVDisplay.ContainerLocalSystem +``` + +Check licensing status: + +```shell +& 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe' -q | Select-String "License" +``` + +Output should be something like: + +```text +vGPU Software Licensed Product + License Status : Licensed (Expiry: YYYY-M-DD hh:mm:ss GMT) +``` + +Done. For more information check [troubleshoot section](#troubleshoot). + +# Endpoints ### `GET /` diff --git a/app/main.py b/app/main.py index 46d8a65..204a55a 100644 --- a/app/main.py +++ b/app/main.py @@ -9,7 +9,7 @@ from dotenv import load_dotenv from fastapi import FastAPI from fastapi.requests import Request from json import loads as json_loads -from datetime import datetime +from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta from calendar import timegm from jose import jws, jwk, jwt, JWTError @@ -43,6 +43,7 @@ INSTANCE_KEY_PUB = load_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__), TOKEN_EXPIRE_DELTA = relativedelta(days=int(env('TOKEN_EXPIRE_DAYS', 1)), hours=int(env('TOKEN_EXPIRE_HOURS', 0))) LEASE_EXPIRE_DELTA = relativedelta(days=int(env('LEASE_EXPIRE_DAYS', 90)), hours=int(env('LEASE_EXPIRE_HOURS', 0))) LEASE_RENEWAL_PERIOD = float(env('LEASE_RENEWAL_PERIOD', 0.15)) +LEASE_RENEWAL_DELTA = timedelta(days=int(env('LEASE_EXPIRE_DAYS', 90)), hours=int(env('LEASE_EXPIRE_HOURS', 0))) CORS_ORIGINS = str(env('CORS_ORIGINS', '')).split(',') if (env('CORS_ORIGINS')) else [f'https://{DLS_URL}'] jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) @@ -151,7 +152,8 @@ async def _origins(request: Request, leases: bool = False): for origin in session.query(Origin).all(): x = origin.serialize() if leases: - x['leases'] = list(map(lambda _: _.serialize(), Lease.find_by_origin_ref(db, origin.origin_ref))) + serialize = dict(renewal_period=LEASE_RENEWAL_PERIOD, renewal_delta=LEASE_RENEWAL_DELTA) + x['leases'] = list(map(lambda _: _.serialize(**serialize), Lease.find_by_origin_ref(db, origin.origin_ref))) response.append(x) session.close() return JSONr(response) @@ -168,7 +170,8 @@ async def _leases(request: Request, origin: bool = False): session = sessionmaker(bind=db)() response = [] for lease in session.query(Lease).all(): - x = lease.serialize() + serialize = dict(renewal_period=LEASE_RENEWAL_PERIOD, renewal_delta=LEASE_RENEWAL_DELTA) + x = lease.serialize(**serialize) if origin: lease_origin = session.query(Origin).filter(Origin.origin_ref == lease.origin_ref).first() if lease_origin is not None: diff --git a/app/orm.py b/app/orm.py index aadc0ef..bfd6557 100644 --- a/app/orm.py +++ b/app/orm.py @@ -1,4 +1,5 @@ -import datetime +from datetime import datetime, timedelta +from dateutil.relativedelta import relativedelta from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect from sqlalchemy.ext.declarative import declarative_base @@ -81,7 +82,10 @@ class Lease(Base): def __repr__(self): return f'Lease(origin_ref={self.origin_ref}, lease_ref={self.lease_ref}, expires={self.lease_expires})' - def serialize(self) -> dict: + def serialize(self, renewal_period: float, renewal_delta: timedelta) -> dict: + lease_renewal = int(Lease.calculate_renewal(renewal_period, renewal_delta).total_seconds()) + lease_renewal = self.lease_updated + relativedelta(seconds=lease_renewal) + return { 'lease_ref': self.lease_ref, 'origin_ref': self.origin_ref, @@ -89,6 +93,7 @@ class Lease(Base): 'lease_created': self.lease_created.isoformat(), 'lease_expires': self.lease_expires.isoformat(), 'lease_updated': self.lease_updated.isoformat(), + 'lease_renewal': lease_renewal.isoformat(), } @staticmethod @@ -133,7 +138,7 @@ class Lease(Base): return entity @staticmethod - def renew(engine: Engine, lease: "Lease", lease_expires: datetime.datetime, lease_updated: datetime.datetime): + def renew(engine: Engine, lease: "Lease", lease_expires: datetime, lease_updated: datetime): session = sessionmaker(bind=engine)() x = dict(lease_expires=lease_expires, lease_updated=lease_updated) session.execute(update(Lease).where(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).values(**x)) @@ -156,6 +161,20 @@ class Lease(Base): session.close() return deletions + @staticmethod + def calculate_renewal(renewal_period: float, delta: timedelta) -> timedelta: + """ + import datetime + LEASE_RENEWAL_PERIOD=0.2 # 20% + delta = datetime.timedelta(days=1) + renew = delta.total_seconds() * LEASE_RENEWAL_PERIOD + renew = datetime.timedelta(seconds=renew) + expires = delta - renew # 19.2 + """ + renew = delta.total_seconds() * renewal_period + renew = timedelta(seconds=renew) + return renew + def init(engine: Engine): tables = [Origin, Lease] diff --git a/version.env b/version.env index 0c794cf..93176fc 100644 --- a/version.env +++ b/version.env @@ -1 +1 @@ -VERSION=1.3.2 +VERSION=1.3.3