mirror of
				https://gitea.publichub.eu/oscar.krause/fastapi-dls.git
				synced 2025-11-04 01:26:06 +00:00 
			
		
		
		
	Merge branch 'dev' into 'main'
v0.1 See merge request oscar.krause/fastapi-dls!1
This commit is contained in:
		
							
								
								
									
										7
									
								
								.codeclimate.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.codeclimate.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					plugins:
 | 
				
			||||||
 | 
					  bandit:
 | 
				
			||||||
 | 
					    enabled: true
 | 
				
			||||||
 | 
					  sonar-python:
 | 
				
			||||||
 | 
					    enabled: true
 | 
				
			||||||
 | 
					  pylint:
 | 
				
			||||||
 | 
					    enabled: true
 | 
				
			||||||
							
								
								
									
										28
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					root = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[*]
 | 
				
			||||||
 | 
					insert_final_newline = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Matches multiple files with brace expansion notation
 | 
				
			||||||
 | 
					# Set default charset
 | 
				
			||||||
 | 
					[*.{js,py}]
 | 
				
			||||||
 | 
					charset = utf-8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 4 space indentation
 | 
				
			||||||
 | 
					[*.py]
 | 
				
			||||||
 | 
					indent_style = space
 | 
				
			||||||
 | 
					indent_size = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Tab indentation (no size specified)
 | 
				
			||||||
 | 
					[Makefile]
 | 
				
			||||||
 | 
					indent_style = tab
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Indentation override for all JS under lib directory
 | 
				
			||||||
 | 
					[lib/**.js]
 | 
				
			||||||
 | 
					indent_style = space
 | 
				
			||||||
 | 
					indent_size = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Matches the exact files either package.json or .travis.yml
 | 
				
			||||||
 | 
					[{package.json,.travis.yml}]
 | 
				
			||||||
 | 
					indent_style = space
 | 
				
			||||||
 | 
					indent_size = 2
 | 
				
			||||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
 | 
					app/cert/*.*
 | 
				
			||||||
							
								
								
									
										30
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					cache:
 | 
				
			||||||
 | 
					  key: one-key-to-rule-them-all
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build:
 | 
				
			||||||
 | 
					  image: docker:dind
 | 
				
			||||||
 | 
					  interruptible: true
 | 
				
			||||||
 | 
					  stage: build
 | 
				
			||||||
 | 
					  rules:
 | 
				
			||||||
 | 
					    - if: $CI_COMMIT_BRANCH
 | 
				
			||||||
 | 
					  tags: [ docker ]
 | 
				
			||||||
 | 
					  script:
 | 
				
			||||||
 | 
					    - 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}
 | 
				
			||||||
 | 
					    - docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_BUILD_REF_NAME}:${CI_BUILD_REF}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test:
 | 
				
			||||||
 | 
					  stage: test
 | 
				
			||||||
 | 
					  script:
 | 
				
			||||||
 | 
					    - echo "Nothing to do ..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					deploy:
 | 
				
			||||||
 | 
					  stage: deploy
 | 
				
			||||||
 | 
					  rules:
 | 
				
			||||||
 | 
					    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
 | 
				
			||||||
 | 
					  script:
 | 
				
			||||||
 | 
					    - docker login -u $PUBLIC_REGISTRY_USER -p $PUBLIC_REGISTRY_TOKEN
 | 
				
			||||||
 | 
					    - docker build . --tag $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:${CI_BUILD_REF}
 | 
				
			||||||
 | 
					    - docker build . --tag $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:latest
 | 
				
			||||||
 | 
					    - docker push $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:${CI_BUILD_REF}
 | 
				
			||||||
 | 
					    - docker push $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:latest
 | 
				
			||||||
							
								
								
									
										16
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					FROM python:3.10-alpine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY requirements.txt /tmp/requirements.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN apk update \
 | 
				
			||||||
 | 
					 && apk add --no-cache --virtual build-deps gcc g++ python3-dev musl-dev \
 | 
				
			||||||
 | 
					 && apk add --no-cache postgresql postgresql-dev mariadb-connector-c-dev \
 | 
				
			||||||
 | 
					 && pip install --no-cache-dir --upgrade uvicorn \
 | 
				
			||||||
 | 
					 && pip install --no-cache-dir psycopg2==2.9.3 mysqlclient==2.1.1 pysqlite3==0.4.7 \
 | 
				
			||||||
 | 
					 && pip install --no-cache-dir -r /tmp/requirements.txt \
 | 
				
			||||||
 | 
					 && apk del build-deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY app /app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HEALTHCHECK --start-period=30s --interval=10s --timeout=5s --retries=3 CMD curl --fail http://localhost/status || exit 1
 | 
				
			||||||
 | 
					CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "443", "--app-dir", "/app", "--proxy-headers", "--ssl-keyfile", "/app/cert/webserver.key", "--ssl-certfile", "/app/cert/webserver.crt"]
 | 
				
			||||||
							
								
								
									
										77
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								README.md
									
									
									
									
									
								
							@@ -1,3 +1,78 @@
 | 
				
			|||||||
# FastAPI-DLS
 | 
					# FastAPI-DLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Minimal Licencing Servie.
 | 
					Minimal Delegated License Service (DLS).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Setup (Docker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Run this on the Docker-Host**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```shell
 | 
				
			||||||
 | 
					WORKING_DIR=/opt/docker/fastapi-dls/cert
 | 
				
			||||||
 | 
					mkdir -p $WORKING_DIR
 | 
				
			||||||
 | 
					cd $WORKING_DIR
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout  $WORKING_DIR/webserver.key -out $WORKING_DIR/webserver.crt
 | 
				
			||||||
 | 
					docker run -e DLS_URL=`hostname -i` -e DLS_PORT=443 -p 443:443 -v $WORKING_DIR:/app/cert collinwebdesigns/fastapi-dls:latest
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**The token file has to be copied! It's not enough to C&P file contents, because there can be special characters.**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```shell
 | 
				
			||||||
 | 
					curl --insecure -X GET https://<dls-hostname-or-ip>/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token.tok
 | 
				
			||||||
 | 
					service nvidia-gridd restart
 | 
				
			||||||
 | 
					nvidia-smi -q | grep "License"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Download file and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`.
 | 
				
			||||||
 | 
					Now restart `NvContainerLocalSystem` service.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Troubleshoot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Logs are available with `journalctl -u nvidia-gridd -f`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Logs are available in `C:\Users\Public\Documents\Nvidia\LoggingLog.NVDisplay.Container.exe.log`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Known Issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Currently, there are no known issues.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					On Windows there is currently a problem returning the license. As you can see the license is installed successfully
 | 
				
			||||||
 | 
					after
 | 
				
			||||||
 | 
					a few minutes. About the time of the first *lease period* the driver gets a *Mismatch between client and server with
 | 
				
			||||||
 | 
					respect to licenses held*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<details>
 | 
				
			||||||
 | 
					  <summary>Log</summary>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Tue Dec 20 05:55:52 2022:<2>:NLS initialized
 | 
				
			||||||
 | 
					Tue Dec 20 05:55:57 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
 | 
				
			||||||
 | 
					Tue Dec 20 05:55:58 2022:<2>:License returned successfully. (Info: 192.168.178.33)
 | 
				
			||||||
 | 
					Tue Dec 20 05:56:20 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
 | 
				
			||||||
 | 
					Tue Dec 20 05:56:21 2022:<2>:License returned successfully. (Info: 192.168.178.33)
 | 
				
			||||||
 | 
					Tue Dec 20 05:56:46 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
 | 
				
			||||||
 | 
					Tue Dec 20 05:56:47 2022:<2>:License returned successfully. (Info: 192.168.178.33)
 | 
				
			||||||
 | 
					Tue Dec 20 05:56:54 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:11:54 GMT)
 | 
				
			||||||
 | 
					Tue Dec 20 05:57:17 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
 | 
				
			||||||
 | 
					Tue Dec 20 05:57:18 2022:<2>:License returned successfully. (Info: 192.168.178.33)
 | 
				
			||||||
 | 
					Tue Dec 20 05:59:20 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:14:20 GMT)
 | 
				
			||||||
 | 
					Tue Dec 20 06:01:45 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:16:45 GMT)
 | 
				
			||||||
 | 
					Tue Dec 20 06:04:10 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:19:10 GMT)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</details>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								ToDo.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ToDo.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					# FastAPI-DLS Server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## ToDo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Create Client Token (`.tok`-file)
 | 
				
			||||||
 | 
					- Docker Image
 | 
				
			||||||
 | 
					- Create Certificates if not exist
 | 
				
			||||||
 | 
					- Keep track of clients and Leases
 | 
				
			||||||
							
								
								
									
										20
									
								
								app/helper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/helper.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					from Crypto.PublicKey import RSA
 | 
				
			||||||
 | 
					from Crypto.PublicKey.RSA import RsaKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def load_file(filename) -> bytes:
 | 
				
			||||||
 | 
					    with open(filename, 'rb') as file:
 | 
				
			||||||
 | 
					        content = file.read()
 | 
				
			||||||
 | 
					    return content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def load_key(filename) -> RsaKey:
 | 
				
			||||||
 | 
					    return RSA.import_key(extern_key=load_file(filename), passphrase=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def private_bytes(rsa: RsaKey) -> bytes:
 | 
				
			||||||
 | 
					    return rsa.export_key(format='PEM', passphrase=None, protection=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def public_key(rsa: RsaKey) -> bytes:
 | 
				
			||||||
 | 
					    return rsa.public_key().export_key(format='PEM')
 | 
				
			||||||
							
								
								
									
										295
									
								
								app/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								app/main.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,295 @@
 | 
				
			|||||||
 | 
					from base64 import b64encode
 | 
				
			||||||
 | 
					from hashlib import sha256
 | 
				
			||||||
 | 
					from uuid import uuid4
 | 
				
			||||||
 | 
					from os.path import join, dirname
 | 
				
			||||||
 | 
					from os import getenv
 | 
				
			||||||
 | 
					from fastapi import FastAPI, HTTPException
 | 
				
			||||||
 | 
					from fastapi.requests import Request
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					from dateutil.relativedelta import relativedelta
 | 
				
			||||||
 | 
					from calendar import timegm
 | 
				
			||||||
 | 
					from jose import jws, jwk, jwt
 | 
				
			||||||
 | 
					from jose.constants import ALGORITHMS
 | 
				
			||||||
 | 
					from starlette.responses import StreamingResponse, JSONResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from helper import load_key, private_bytes, public_key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# todo: initialize certificate (or should be done by user, and passed through "volumes"?)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app = FastAPI()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LEASE_EXPIRE_DELTA = relativedelta(minutes=15)  # days=90
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DLS_URL = str(getenv('DLS_URL', 'localhost'))
 | 
				
			||||||
 | 
					DLS_PORT = int(getenv('DLS_PORT', '443'))
 | 
				
			||||||
 | 
					SITE_KEY_XID = getenv('SITE_KEY_XID', '00000000-0000-0000-0000-000000000000')
 | 
				
			||||||
 | 
					INSTANCE_KEY_RSA = load_key(join(dirname(__file__), 'cert/instance.private.pem'))
 | 
				
			||||||
 | 
					INSTANCE_KEY_PUB = load_key(join(dirname(__file__), 'cert/instance.public.pem'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.get('/')
 | 
				
			||||||
 | 
					async def index():
 | 
				
			||||||
 | 
					    return JSONResponse({'hello': 'world'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.get('/status')
 | 
				
			||||||
 | 
					async def status(request: Request):
 | 
				
			||||||
 | 
					    return JSONResponse({'status': 'up'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_core_service_instance/service_instance_token_manager.py
 | 
				
			||||||
 | 
					@app.get('/client-token')
 | 
				
			||||||
 | 
					async def client_token():
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    exp_time = cur_time + relativedelta(years=12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    service_instance_public_key_configuration = {
 | 
				
			||||||
 | 
					        "service_instance_public_key_me": {
 | 
				
			||||||
 | 
					            "mod": hex(INSTANCE_KEY_PUB.public_key().n)[2:],
 | 
				
			||||||
 | 
					            "exp": INSTANCE_KEY_PUB.public_key().e,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "service_instance_public_key_pem": INSTANCE_KEY_PUB.export_key().decode('utf-8'),
 | 
				
			||||||
 | 
					        "key_retention_mode": "LATEST_ONLY"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    payload = {
 | 
				
			||||||
 | 
					        "jti": str(uuid4()),
 | 
				
			||||||
 | 
					        "iss": "NLS Service Instance",
 | 
				
			||||||
 | 
					        "aud": "NLS Licensed Client",
 | 
				
			||||||
 | 
					        "iat": timegm(cur_time.timetuple()),
 | 
				
			||||||
 | 
					        "nbf": timegm(cur_time.timetuple()),
 | 
				
			||||||
 | 
					        "exp": timegm(exp_time.timetuple()),
 | 
				
			||||||
 | 
					        "update_mode": "ABSOLUTE",
 | 
				
			||||||
 | 
					        "scope_ref_list": [str(uuid4())],
 | 
				
			||||||
 | 
					        "fulfillment_class_ref_list": [],
 | 
				
			||||||
 | 
					        "service_instance_configuration": {
 | 
				
			||||||
 | 
					            "nls_service_instance_ref": "00000000-0000-0000-0000-000000000000",
 | 
				
			||||||
 | 
					            "svc_port_set_list": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "idx": 0,
 | 
				
			||||||
 | 
					                    "d_name": "DLS",
 | 
				
			||||||
 | 
					                    "svc_port_map": [
 | 
				
			||||||
 | 
					                        {"service": "auth", "port": DLS_PORT},
 | 
				
			||||||
 | 
					                        {"service": "lease", "port": DLS_PORT}
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "node_url_list": [{"idx": 0, "url": DLS_URL, "url_qr": DLS_URL, "svc_port_set_idx": 0}]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "service_instance_public_key_configuration": service_instance_public_key_configuration,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
 | 
				
			||||||
 | 
					    data = jws.sign(payload, key=key, headers=None, algorithm='RS256')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response = StreamingResponse(iter([data]), media_type="text/plain")
 | 
				
			||||||
 | 
					    filename = f'client_configuration_token_{datetime.now().strftime("%d-%m-%y-%H-%M-%S")}'
 | 
				
			||||||
 | 
					    response.headers["Content-Disposition"] = f'attachment; filename={filename}'
 | 
				
			||||||
 | 
					    return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py
 | 
				
			||||||
 | 
					@app.post('/auth/v1/origin')
 | 
				
			||||||
 | 
					async def auth_origin(request: Request):
 | 
				
			||||||
 | 
					    body = await request.body()
 | 
				
			||||||
 | 
					    body = body.decode('utf-8')
 | 
				
			||||||
 | 
					    j = json.loads(body)
 | 
				
			||||||
 | 
					    # {"candidate_origin_ref":"00112233-4455-6677-8899-aabbccddeeff","environment":{"fingerprint":{"mac_address_list":["ff:ff:ff:ff:ff:ff"]},"hostname":"my-hostname","ip_address_list":["192.168.178.123","fe80::","fe80::1%enp6s18"],"guest_driver_version":"510.85.02","os_platform":"Debian GNU/Linux 11 (bullseye) 11","os_version":"11 (bullseye)"},"registration_pending":false,"update_pending":false}
 | 
				
			||||||
 | 
					    print(f'> [  origin  ]: {j}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        "origin_ref": j['candidate_origin_ref'],
 | 
				
			||||||
 | 
					        "environment": j['environment'],
 | 
				
			||||||
 | 
					        "svc_port_set_list": None,
 | 
				
			||||||
 | 
					        "node_url_list": None,
 | 
				
			||||||
 | 
					        "node_query_order": None,
 | 
				
			||||||
 | 
					        "prompts": None,
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_core_auth/auth.py - CodeResponse
 | 
				
			||||||
 | 
					@app.post('/auth/v1/code')
 | 
				
			||||||
 | 
					async def auth_code(request: Request):
 | 
				
			||||||
 | 
					    body = await request.body()
 | 
				
			||||||
 | 
					    body = body.decode('utf-8')
 | 
				
			||||||
 | 
					    j = json.loads(body)
 | 
				
			||||||
 | 
					    # {"code_challenge":"...","origin_ref":"00112233-4455-6677-8899-aabbccddeeff"}
 | 
				
			||||||
 | 
					    print(f'> [   code   ]: {j}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    expires = cur_time + relativedelta(days=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    payload = {
 | 
				
			||||||
 | 
					        'iat': timegm(cur_time.timetuple()),
 | 
				
			||||||
 | 
					        'exp': timegm(expires.timetuple()),
 | 
				
			||||||
 | 
					        'challenge': j['code_challenge'],
 | 
				
			||||||
 | 
					        'origin_ref': j['code_challenge'],
 | 
				
			||||||
 | 
					        'key_ref': SITE_KEY_XID,
 | 
				
			||||||
 | 
					        'kid': SITE_KEY_XID
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    headers = None
 | 
				
			||||||
 | 
					    kid = payload.get('kid')
 | 
				
			||||||
 | 
					    if kid:
 | 
				
			||||||
 | 
					        headers = {'kid': kid}
 | 
				
			||||||
 | 
					    key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
 | 
				
			||||||
 | 
					    auth_code = jws.sign(payload, key, headers=headers, algorithm='RS256')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        "auth_code": auth_code,
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat(),
 | 
				
			||||||
 | 
					        "prompts": None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_core_auth/auth.py - TokenResponse
 | 
				
			||||||
 | 
					@app.post('/auth/v1/token')
 | 
				
			||||||
 | 
					async def auth_token(request: Request):
 | 
				
			||||||
 | 
					    body = await request.body()
 | 
				
			||||||
 | 
					    body = body.decode('utf-8')
 | 
				
			||||||
 | 
					    j = json.loads(body)
 | 
				
			||||||
 | 
					    # {"auth_code":"...","code_verifier":"..."}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # payload = self._security.get_valid_payload(req.auth_code)  # todo
 | 
				
			||||||
 | 
					    key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
 | 
				
			||||||
 | 
					    payload = jwt.decode(token=j['auth_code'], key=key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # validate the code challenge
 | 
				
			||||||
 | 
					    if payload['challenge'] != b64encode(sha256(j['code_verifier'].encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'):
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=403, detail='expected challenge did not match verifier')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    access_expires_on = cur_time + relativedelta(days=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new_payload = {
 | 
				
			||||||
 | 
					        'iat': timegm(cur_time.timetuple()),
 | 
				
			||||||
 | 
					        'nbf': timegm(cur_time.timetuple()),
 | 
				
			||||||
 | 
					        'iss': 'https://cls.nvidia.org',
 | 
				
			||||||
 | 
					        'aud': 'https://cls.nvidia.org',
 | 
				
			||||||
 | 
					        'exp': timegm(access_expires_on.timetuple()),
 | 
				
			||||||
 | 
					        'origin_ref': payload['origin_ref'],
 | 
				
			||||||
 | 
					        'key_ref': SITE_KEY_XID,
 | 
				
			||||||
 | 
					        'kid': SITE_KEY_XID,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    headers = None
 | 
				
			||||||
 | 
					    kid = payload.get('kid')
 | 
				
			||||||
 | 
					    if kid:
 | 
				
			||||||
 | 
					        headers = {'kid': kid}
 | 
				
			||||||
 | 
					    key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
 | 
				
			||||||
 | 
					    auth_token = jwt.encode(new_payload, key=key, headers=headers, algorithm='RS256')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        "expires": access_expires_on.isoformat(),
 | 
				
			||||||
 | 
					        "auth_token": auth_token,
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.post('/leasing/v1/lessor')
 | 
				
			||||||
 | 
					async def leasing_lessor(request: Request):
 | 
				
			||||||
 | 
					    body = await request.body()
 | 
				
			||||||
 | 
					    body = body.decode('utf-8')
 | 
				
			||||||
 | 
					    j = json.loads(body)
 | 
				
			||||||
 | 
					    # {'fulfillment_context': {'fulfillment_class_ref_list': []}, 'lease_proposal_list': [{'license_type_qualifiers': {'count': 1}, 'product': {'name': 'NVIDIA RTX Virtual Workstation'}}], 'proposal_evaluation_mode': 'ALL_OF', 'scope_ref_list': ['00112233-4455-6677-8899-aabbccddeeff']}
 | 
				
			||||||
 | 
					    print(f'> [  lessor  ]: {j}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    # todo: keep track of leases, to return correct list on '/leasing/v1/lessor/leases'
 | 
				
			||||||
 | 
					    lease_result_list = []
 | 
				
			||||||
 | 
					    for scope_ref in j['scope_ref_list']:
 | 
				
			||||||
 | 
					        lease_result_list.append({
 | 
				
			||||||
 | 
					            "ordinal": 0,
 | 
				
			||||||
 | 
					            "lease": {
 | 
				
			||||||
 | 
					                "ref": scope_ref,
 | 
				
			||||||
 | 
					                "created": cur_time.isoformat(),
 | 
				
			||||||
 | 
					                "expires": (cur_time + LEASE_EXPIRE_DELTA).isoformat(),
 | 
				
			||||||
 | 
					                "recommended_lease_renewal": 0.15,
 | 
				
			||||||
 | 
					                "offline_lease": "true",
 | 
				
			||||||
 | 
					                "license_type": "CONCURRENT_COUNTED_SINGLE"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        "lease_result_list": lease_result_list,
 | 
				
			||||||
 | 
					        "result_code": "SUCCESS",
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat(),
 | 
				
			||||||
 | 
					        "prompts": None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
 | 
				
			||||||
 | 
					@app.get('/leasing/v1/lessor/leases')
 | 
				
			||||||
 | 
					async def leasing_lessor_lease(request: Request):
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    # venv/lib/python3.9/site-packages/nls_dal_service_instance_dls/schema/service_instance/V1_0_21__product_mapping.sql
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        # GRID-Virtual-WS 2.0 CONCURRENT_COUNTED_SINGLE
 | 
				
			||||||
 | 
					        "active_lease_list": [
 | 
				
			||||||
 | 
					            "BE276D7B-2CDB-11EC-9838-061A22468B59"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat(),
 | 
				
			||||||
 | 
					        "prompts": None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# venv/lib/python3.9/site-packages/nls_core_lease/lease_single.py
 | 
				
			||||||
 | 
					@app.put('/leasing/v1/lease/{lease_ref}')
 | 
				
			||||||
 | 
					async def leasing_lease_renew(request: Request, lease_ref: str):
 | 
				
			||||||
 | 
					    print(f'> [  renew   ]: lease: {lease_ref}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        "lease_ref": lease_ref,
 | 
				
			||||||
 | 
					        "expires": (cur_time + LEASE_EXPIRE_DELTA).isoformat(),
 | 
				
			||||||
 | 
					        "recommended_lease_renewal": 0.16,
 | 
				
			||||||
 | 
					        "offline_lease": True,
 | 
				
			||||||
 | 
					        "prompts": None,
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.delete('/leasing/v1/lessor/leases')
 | 
				
			||||||
 | 
					async def leasing_lessor_lease_remove(request: Request):
 | 
				
			||||||
 | 
					    cur_time = datetime.utcnow()
 | 
				
			||||||
 | 
					    response = {
 | 
				
			||||||
 | 
					        "released_lease_list": None,
 | 
				
			||||||
 | 
					        "release_failure_list": None,
 | 
				
			||||||
 | 
					        "sync_timestamp": cur_time.isoformat(),
 | 
				
			||||||
 | 
					        "prompts": None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return JSONResponse(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    import uvicorn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ###
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # Running `python app/main.py` assumes that the user created a keypair, e.g. with openssl.
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout app/cert/webserver.key -out app/cert/webserver.crt
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f'> Starting dev-server ...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ssl_keyfile = join(dirname(__file__), 'cert/webserver.key')
 | 
				
			||||||
 | 
					    ssl_certfile = join(dirname(__file__), 'cert/webserver.crt')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uvicorn.run('main:app', host='0.0.0.0', port=443, ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, reload=True)
 | 
				
			||||||
							
								
								
									
										174
									
								
								doc/Reverse Engineering Notes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								doc/Reverse Engineering Notes.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
				
			|||||||
 | 
					# Reverse Engineering Notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Usefully commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Check licensing status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `nvidia-smi -q | grep "License"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Output**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					vGPU Software Licensed Product
 | 
				
			||||||
 | 
					        License Status                    : Licensed (Expiry: 2023-1-14 12:59:52 GMT)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Track licensing progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- NVIDIA Grid Log: `journalctl -u nvidia-gridd -f`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					systemd[1]: Started NVIDIA Grid Daemon.
 | 
				
			||||||
 | 
					nvidia-gridd[2986]: Configuration parameter ( ServerAddress  ) not set
 | 
				
			||||||
 | 
					nvidia-gridd[2986]: vGPU Software package (0)
 | 
				
			||||||
 | 
					nvidia-gridd[2986]: Ignore service provider and node-locked licensing
 | 
				
			||||||
 | 
					nvidia-gridd[2986]: NLS initialized
 | 
				
			||||||
 | 
					nvidia-gridd[2986]: Acquiring license. (Info: license.nvidia.space; NVIDIA RTX Virtual Workstation)
 | 
				
			||||||
 | 
					nvidia-gridd[2986]: License acquired successfully. (Info: license.nvidia.space, NVIDIA RTX Virtual Workstation; Expiry: 2023-1-29 22:3:0 GMT)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# DLS-Container File-System (Docker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Configuration data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Most variables and configs are stored in `/var/lib/docker/volumes/configurations/_data`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Dive / Docker image inspector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `dive dls:appliance`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The source code is stored in `/venv/lib/python3.9/site-packages/nls_*`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Image-Reference:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Tags:   (unavailable)
 | 
				
			||||||
 | 
					Id:     d1c7976a5d2b3681ff6c5a30f8187e4015187a83f3f285ba4a37a45458bd6b98
 | 
				
			||||||
 | 
					Digest: sha256:311223c5af7a298ec1104f5dc8c3019bfb0e1f77256dc3d995244ffb295a97
 | 
				
			||||||
 | 
					1f
 | 
				
			||||||
 | 
					Command:
 | 
				
			||||||
 | 
					#(nop) ADD file:c1900d3e3a29c29a743a8da86c437006ec5d2aa873fb24e48033b6bf492bb37b in /
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Private Key (Site-Key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `/etc/dls/config/decryptor/decryptor`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```shell
 | 
				
			||||||
 | 
					 docker exec -it <container-id> /etc/dls/config/decryptor/decryptor > /tmp/private-key.pem
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					-----BEGIN RSA PRIVATE KEY-----
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					-----END RSA PRIVATE KEY-----
 | 
				
			||||||
 | 
					``` 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Site Key Uri - `/etc/dls/config/site_key_uri.bin`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					base64-content...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## DB Password - `/etc/dls/config/dls_db_password.bin`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					base64-content...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Decrypt database password**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					cd /var/lib/docker/volumes/configurations/_data
 | 
				
			||||||
 | 
					cat dls_db_password.bin | base64 -d > dls_db_password.bin.raw
 | 
				
			||||||
 | 
					openssl rsautl -decrypt -inkey /tmp/private-key.pem -in dls_db_password.bin.raw
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- It's enough to manipulate database licenses. There must not be changed any line of code to bypass licensing
 | 
				
			||||||
 | 
					  validations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Logging / Stack Trace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- https://docs.nvidia.com/license-system/latest/nvidia-license-system-user-guide/index.html#troubleshooting-dls-instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Failed licensing log**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "activity": 100,
 | 
				
			||||||
 | 
					    "context": {
 | 
				
			||||||
 | 
					        "SERVICE_INSTANCE_ID": "b43d6e46-d6d0-4943-8b8d-c66a5f6e0d38",
 | 
				
			||||||
 | 
					        "SERVICE_INSTANCE_NAME": "DEFAULT_2022-12-14_12:48:30",
 | 
				
			||||||
 | 
					        "description": "borrow failed: NotFoundError(no pool features found for: NVIDIA RTX Virtual Workstation)",
 | 
				
			||||||
 | 
					        "event_type": null,
 | 
				
			||||||
 | 
					        "function_name": "_evt",
 | 
				
			||||||
 | 
					        "lineno": 54,
 | 
				
			||||||
 | 
					        "module_name": "nls_dal_lease_dls.event",
 | 
				
			||||||
 | 
					        "operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24",
 | 
				
			||||||
 | 
					        "origin_ref": "3f7f5a50-a26b-425b-8d5e-157f63e72b1c",
 | 
				
			||||||
 | 
					        "service_name": "nls_services_lease"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "detail": {
 | 
				
			||||||
 | 
					        "oc": {
 | 
				
			||||||
 | 
					            "license_allotment_xid": "10c4317f-7c4c-11ed-a524-0e4252a7e5f1",
 | 
				
			||||||
 | 
					            "origin_ref": "3f7f5a50-a26b-425b-8d5e-157f63e72b1c",
 | 
				
			||||||
 | 
					            "service_instance_xid": "b43d6e46-d6d0-4943-8b8d-c66a5f6e0d38"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "id": "0cc9e092-3b92-4652-8d9e-7622ef85dc79",
 | 
				
			||||||
 | 
					    "metadata": {},
 | 
				
			||||||
 | 
					    "ts": "2022-12-15T10:25:36.827661Z"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "activity": 400,
 | 
				
			||||||
 | 
					    "context": {
 | 
				
			||||||
 | 
					        "SERVICE_INSTANCE_ID": "b43d6e46-d6d0-4943-8b8d-c66a5f6e0d38",
 | 
				
			||||||
 | 
					        "SERVICE_INSTANCE_NAME": "DEFAULT_2022-12-14_12:48:30",
 | 
				
			||||||
 | 
					        "description": "lease_multi_create failed: no pool features found for: NVIDIA RTX Virtual Workstation",
 | 
				
			||||||
 | 
					        "event_by": "system",
 | 
				
			||||||
 | 
					        "function_name": "lease_multi_create",
 | 
				
			||||||
 | 
					        "level": "warning",
 | 
				
			||||||
 | 
					        "lineno": 157,
 | 
				
			||||||
 | 
					        "module_name": "nls_services_lease.controllers.lease_multi_controller",
 | 
				
			||||||
 | 
					        "operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24",
 | 
				
			||||||
 | 
					        "service_name": "nls_services_lease"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "detail": {
 | 
				
			||||||
 | 
					        "_msg": "lease_multi_create failed: no pool features found for: NVIDIA RTX Virtual Workstation",
 | 
				
			||||||
 | 
					        "exec_info": ["NotFoundError", "NotFoundError(no pool features found for: NVIDIA RTX Virtual Workstation)", "  File \"/venv/lib/python3.9/site-packages/nls_services_lease/controllers/lease_multi_controller.py\", line 127, in lease_multi_create\n    data = _leaseMulti.lease_multi_create(event_args)\n  File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 208, in lease_multi_create\n    raise e\n  File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 184, in lease_multi_create\n    self._try_proposals(oc, mlr, results, detail)\n  File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 219, in _try_proposals\n    lease = self._leases.create(creator)\n  File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 230, in create\n    features = self._get_features(creator)\n  File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 148, in _get_features\n    self._explain_not_available(cur, creator)\n  File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 299, in _explain_not_available\n    raise NotFoundError(f'no pool features found for: {lcc.product_name}')\n"],
 | 
				
			||||||
 | 
					        "operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "id": "282801b9-d612-40a5-9145-b56d8e420dac",
 | 
				
			||||||
 | 
					    "metadata": {},
 | 
				
			||||||
 | 
					    "ts": "2022-12-15T10:25:36.831673Z"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Stack Trace**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					"NotFoundError", "NotFoundError(no pool features found for: NVIDIA RTX Virtual Workstation)", "  File \"/venv/lib/python3.9/site-packages/nls_services_lease/controllers/lease_multi_controller.py\", line 127, in lease_multi_create
 | 
				
			||||||
 | 
					    data = _leaseMulti.lease_multi_create(event_args)
 | 
				
			||||||
 | 
					  File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 208, in lease_multi_create
 | 
				
			||||||
 | 
					    raise e
 | 
				
			||||||
 | 
					  File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 184, in lease_multi_create
 | 
				
			||||||
 | 
					    self._try_proposals(oc, mlr, results, detail)
 | 
				
			||||||
 | 
					  File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 219, in _try_proposals
 | 
				
			||||||
 | 
					    lease = self._leases.create(creator)
 | 
				
			||||||
 | 
					  File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 230, in create
 | 
				
			||||||
 | 
					    features = self._get_features(creator)
 | 
				
			||||||
 | 
					  File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 148, in _get_features
 | 
				
			||||||
 | 
					    self._explain_not_available(cur, creator)
 | 
				
			||||||
 | 
					  File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 299, in _explain_not_available
 | 
				
			||||||
 | 
					    raise NotFoundError(f'no pool features found for: {lcc.product_name}')
 | 
				
			||||||
 | 
					"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Nginx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- NGINX uses `/opt/certs/cert.pem` and `/opt/certs/key.pem`  
 | 
				
			||||||
							
								
								
									
										5
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					fastapi==0.88.0
 | 
				
			||||||
 | 
					uvicorn[standard]==0.20.0
 | 
				
			||||||
 | 
					python-jose==3.3.0
 | 
				
			||||||
 | 
					pycryptodome==3.16.0
 | 
				
			||||||
 | 
					python-dateutil==2.8.2
 | 
				
			||||||
		Reference in New Issue
	
	Block a user