mirror of
				https://gitea.publichub.eu/oscar.krause/fastapi-dls.git
				synced 2025-11-04 07:26:07 +00:00 
			
		
		
		
	Merge branch 'dev' into 'main'
1.3.3 See merge request oscar.krause/fastapi-dls!21
This commit is contained in:
		@@ -1,2 +1 @@
 | 
			
		||||
/etc/fastapi-dls/env
 | 
			
		||||
/etc/systemd/system/fastapi-dls.service
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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."
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
echo -e "> Starting uninstallation of 'fastapi-dls'!"
 | 
			
		||||
 | 
			
		||||
# todo
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										73
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								README.md
									
									
									
									
									
								
							@@ -206,13 +206,17 @@ Packages are available here:
 | 
			
		||||
```shell
 | 
			
		||||
pacman -Sy
 | 
			
		||||
FILENAME=/opt/fastapi-dls.pkg.tar.zst
 | 
			
		||||
url -o $FILENAME <download-url>
 | 
			
		||||
 | 
			
		||||
curl -o $FILENAME <download-url>
 | 
			
		||||
# or
 | 
			
		||||
wget -O $FILENAME <download-url>
 | 
			
		||||
 | 
			
		||||
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://<dls-hostname-or-ip>/-/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://<dls-hostname-or-ip>/-/client-token
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Restart `nvidia-gridd` service:
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
curl --insecure -L -X GET https://<dls-hostname-or-ip>/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://<dls-hostname-or-ip>/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://<dls-hostname-or-ip>/-/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 /`
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								app/orm.py
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								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]
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
VERSION=1.3.2
 | 
			
		||||
VERSION=1.3.3
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user