Compare commits

...

527 Commits
legacy ... main

Author SHA1 Message Date
Oscar Krause
584eee41ef Merge branch 'dev' into 'main'
fixed logging

See merge request oscar.krause/fastapi-dls!47
2025-03-12 13:40:45 +01:00
Oscar Krause
25658cb1fb code styling 2025-03-12 11:41:58 +01:00
Oscar Krause
43fdf1170c Reverse Engineering Notes.md bearbeiten 2025-03-12 08:44:37 +01:00
Oscar Krause
a953e62bcb Reverse Engineering Notes.md bearbeiten 2025-03-11 22:51:45 +01:00
Oscar Krause
9c0cd21e71 Reverse Engineering Notes.md bearbeiten 2025-03-11 22:32:13 +01:00
Oscar Krause
3f5fcbebb3 fixed logging 2025-03-11 22:04:35 +01:00
Oscar Krause
3fdd439035 Reverse Engineering Notes.md bearbeiten 2025-03-11 13:40:21 +01:00
Oscar Krause
d30dbced39 Reverse Engineering Notes.md bearbeiten 2025-03-10 23:47:55 +01:00
Oscar Krause
5b61d0a40e Reverse Engineering Notes.md bearbeiten 2025-03-10 21:21:40 +01:00
Oscar Krause
83616c858b Merge branch 'dev' into 'main'
dev

See merge request oscar.krause/fastapi-dls!44
2025-03-09 21:53:50 +01:00
Oscar Krause
ca25349a68 added notes about 18.x branch 2025-03-09 21:37:29 +01:00
Oscar Krause
262312b512 requirements.txt updated 2025-02-25 11:21:59 +01:00
Oscar Krause
aec6535391 Merge branch 'dev' into 'main'
updated driver releases

See merge request oscar.krause/fastapi-dls!43
2025-01-21 08:15:52 +01:00
Oscar Krause
0f4d0eea34 updated driver releases 2025-01-21 07:58:13 +01:00
Oscar Krause
35022d434b Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!42
2025-01-21 07:55:10 +01:00
Oscar Krause
7db43cf61e dependencies updated 2025-01-21 07:02:04 +01:00
Oscar Krause
d7598a37c6 added another further reading link 2025-01-21 07:00:13 +01:00
Oscar Krause
c57d76c74c Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!41
2024-12-16 15:29:32 +01:00
Oscar Krause
dcc3654131 disabled matrix tests for python (covered by apt jobs) 2024-12-16 07:34:55 +01:00
Oscar Krause
32f1be9599 updated DLS 2024-12-14 12:02:48 +01:00
Oscar Krause
e5fc607638 added explicit "algorithms" argument
applied patch from https://github.com/mrzenc/fastapi-dls-nixos/blob/main/add-algorithms-argument.patch from https://github.com/mrzenc/fastapi-dls-nixos
2024-12-13 07:18:14 +01:00
Oscar Krause
f72c64dcb3 renamed "python3-jose" dependency to alias of "python3-josepy" to match apt packages on debian/ubuntu 2024-12-12 12:54:39 +01:00
Oscar Krause
3659aec4b2 refactored gitlab-ci 2024-12-12 12:34:19 +01:00
Oscar Krause
4a501da27b code styling 2024-12-02 10:03:35 +01:00
Oscar Krause
025b88926b fixes 2024-12-02 10:02:54 +01:00
Oscar Krause
f5943cd636 removed return statement on tests 2024-12-02 09:42:56 +01:00
Oscar Krause
03b9b4a598 moved from deprecated "datetime.utcnow()" to "datetime.now(UTC)" 2024-12-02 09:42:41 +01:00
Oscar Krause
ea8a66d449 removed "PatchMalformedJsonMiddleware" because its not working on driver site
ref. oscar.krause/fastapi-dls#1
2024-12-02 08:37:01 +01:00
Oscar Krause
991a35ef1a implemented "fix_ip_address_list_length" 2024-11-29 13:04:10 +01:00
Oscar Krause
e20a9f4b32 added "NixOS" section from mrzenc
ref. https://github.com/mrzenc/fastapi-dls-nixos
2024-11-25 07:21:57 +01:00
Oscar Krause
1b6f142cb5 marked regex with 'r' 2024-11-22 15:07:54 +01:00
Oscar Krause
1daa365df9 code styling 2024-11-22 15:00:16 +01:00
Oscar Krause
afb38d628b typos 2024-11-22 14:19:51 +01:00
Oscar Krause
53c88a79ac improved logging and implemented method to reduce response mac_address_length 2024-11-22 14:16:10 +01:00
Oscar Krause
a43ddf79c3 updated supported os 2024-11-21 11:13:30 +01:00
Oscar Krause
699dbf6fac Merge branch '1-parsing-issue-in-mal-formatted-mac_address_list' into 'main'
Resolve "Parsing issue in mal formatted "mac_address_list""

Closes #1

See merge request oscar.krause/fastapi-dls!40
2024-11-21 09:18:22 +01:00
Oscar Krause
317699ff58 code styling 2024-11-21 08:51:39 +01:00
Oscar Krause
55446f7d9c fixes 2024-11-21 08:51:39 +01:00
Oscar Krause
88c78efcd9 fixes 2024-11-21 08:51:39 +01:00
Oscar Krause
fb3ac4291f code styling 2024-11-21 08:51:39 +01:00
Oscar Krause
15f14cac11 implemented "SUPPORT_MALFORMED_JSON" variable 2024-11-21 08:51:39 +01:00
Oscar Krause
018d7c34fc fixes 2024-11-21 08:51:39 +01:00
Oscar Krause
1aee423120 fixes 2024-11-21 08:51:39 +01:00
Oscar Krause
a6b2f2a942 fixed json payload 2024-11-21 08:51:39 +01:00
Oscar Krause
e33024db86 fixed variable names
ref. oscar.krause/fastapi-dls#1
2024-11-21 08:51:39 +01:00
Oscar Krause
4ad15f0849 fix malformed json on auth
ref. oscar.krause/fastapi-dls#1
2024-11-21 08:51:39 +01:00
Oscar Krause
7bad0359af updated ci pipeline to match current eol supported systems 2024-11-21 08:44:14 +01:00
Oscar Krause
59a7c9f15a Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!38
2024-11-13 16:11:40 +01:00
Oscar Krause
bc6d692f0a added "delete_expired" method for leases 2024-11-13 15:03:37 +01:00
Oscar Krause
63c37c6334 fixed timezone in json response 2024-11-13 15:03:12 +01:00
Oscar Krause
fa2c06972e sql query improvements 2024-11-13 15:01:33 +01:00
Oscar Krause
e4e6387b2a ci improvements 2024-11-13 14:58:55 +01:00
Oscar Krause
f2be9dca8d Merge branch 'dev' into 'main'
requirements.txt updated

See merge request oscar.krause/fastapi-dls!36
2024-11-13 14:09:54 +01:00
Oscar Krause
52dd425583 fixes 2024-11-13 13:41:07 +01:00
Oscar Krause
286399d79a fixed test matrix 2024-11-13 10:48:11 +01:00
Oscar Krause
4ab1a2ed22 added requirements for ubuntu 24.10 2024-11-13 10:28:08 +01:00
Oscar Krause
459c0e21af debugging 2024-11-13 10:27:52 +01:00
Oscar Krause
98ef64211b typings 2024-11-13 09:09:00 +01:00
Oscar Krause
0b4bb65546 added python3-pip to test 2024-11-13 08:55:00 +01:00
Oscar Krause
47624f5019 Dockerfile - updated db dependencies 2024-11-13 08:37:07 +01:00
Oscar Krause
2b9d7821c0 improved gitlab test matrix 2024-11-13 08:33:28 +01:00
Oscar Krause
45f5108717 requirements.txt updated 2024-11-13 08:25:40 +01:00
Oscar Krause
a7fe8b867e Merge branch 'dev' into 'main'
added way to include driver version in api

See merge request oscar.krause/fastapi-dls!35
2024-10-24 13:28:08 +02:00
Oscar Krause
78214df9cc updated to python 3.12 2024-10-24 10:44:31 +02:00
Oscar Krause
4245d5a582 requirements.txt updated 2024-10-24 08:09:30 +02:00
Oscar Krause
9b5a387169 updated support matrix 2024-10-24 08:09:24 +02:00
Oscar Krause
9377d5ce28 requirements.txt updated 2024-10-08 14:33:44 +02:00
Oscar Krause
7489307db8 README.md updated 2024-08-09 13:15:16 +02:00
Oscar Krause
d41314e81d requirements.txt updated 2024-08-09 13:14:53 +02:00
Oscar Krause
a1123d5451 updated support matrix (removed EOL) 2024-07-24 05:35:22 +02:00
Oscar Krause
93cf719454 updated support matrix 2024-07-24 05:35:09 +02:00
Oscar Krause
0dc8f6c582 refactorings 2024-07-11 05:49:13 +02:00
Oscar Krause
4b0219b85a updated to new vgpu page
ref. https://docs.nvidia.com/vgpu/index.html
2024-07-11 05:49:00 +02:00
Oscar Krause
8edbb25c16 README updated 2024-06-27 08:49:31 +02:00
Oscar Krause
49a24f0b68 README updated 2024-06-27 08:47:35 +02:00
Oscar Krause
8af3c8e2b3 README updated 2024-06-27 08:47:04 +02:00
Oscar Krause
3c321a202c README updated 2024-06-27 08:45:25 +02:00
Oscar Krause
1b7d8bc0dc README reorganized 2024-06-27 08:37:21 +02:00
Oscar Krause
23ccea538f README reorganized 2024-06-27 08:32:15 +02:00
Oscar Krause
c79455b84d added way to include driver version in api
use `create_driver_matrix_json.py` to generate file in `static/driver_matrix.json`. Logging currently is disabled to not confuse users when file is missing. This is optional!
2024-06-21 19:01:33 +02:00
Oscar Krause
35fc5ea6b0 set log format 2024-06-21 18:59:23 +02:00
Oscar Krause
6a54c05fbb Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!34
2024-06-18 13:56:30 +02:00
Oscar Krause
c9ac915055 code styling & comments 2024-06-13 20:34:45 +02:00
Oscar Krause
8c5850beda migrated from deprecated "startup" to "lifespan" hook (fastapi) 2024-06-13 20:34:27 +02:00
Oscar Krause
0d9e814d0d show if debug is enabled on app startup 2024-06-13 20:18:33 +02:00
Oscar Krause
5438317fb7 rearranged imports 2024-06-13 20:18:18 +02:00
Oscar Krause
21f19be8ab code styling & improved logging 2024-06-13 20:16:51 +02:00
Oscar Krause
eff6aae25d code styling 2024-06-13 19:53:57 +02:00
Oscar Krause
6473655e57 import fixes 2024-06-13 19:24:46 +02:00
Oscar Krause
c45aa1a2a8 fixed [[__TOC__]] to [TOC] to support "markdown" package 2024-06-12 21:27:56 +02:00
Oscar Krause
1d0631417d added link to gpu support matrix 2024-06-12 19:44:37 +02:00
Oscar Krause
847d3589c5 typos 2024-06-07 08:42:41 +02:00
Oscar Krause
ca53a4e084 updated supported vGPU releases 2024-06-07 08:42:37 +02:00
Oscar Krause
006d3a1833 Merge branch 'dev' into 'main'
Added Ubuntu 24.04 support & updated requirements

See merge request oscar.krause/fastapi-dls!33
2024-05-10 10:52:00 +02:00
Oscar Krause
ad3b622c23 requirements.txt updated 2024-05-10 10:09:28 +02:00
Oscar Krause
e51d6bd391 added Ubuntu 24.04 as supported 2024-05-10 09:16:20 +02:00
Oscar Krause
78c1978dd5 added test matrix for python3.12 2024-05-10 09:15:46 +02:00
Oscar Krause
4ebb4d790e added ubuntu-24.04 "requirements-ubuntu-24.04.txt" 2024-05-10 08:22:47 +02:00
Oscar Krause
11f1456538 test image matrix 2024-05-10 07:56:13 +02:00
Oscar Krause
be6797efc7 requirements.txt updated 2024-04-23 07:43:39 +02:00
Oscar Krause
42fe066e1a Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!32
2024-04-18 07:38:31 +02:00
Oscar Krause
9eb91cbe1a link to proxmox-installer.sh and credits 2024-04-18 07:07:57 +02:00
Oscar Krause
395884f643 credits & further reading 2024-04-10 07:09:01 +02:00
Oscar Krause
ef542ec821 Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!31
2024-04-09 10:28:57 +02:00
Oscar Krause
254e4ee08c requirements.txt updated 2024-04-09 08:13:33 +02:00
Oscar Krause
07273c3ebd update vGPU Version Matrix 2024-04-09 08:01:20 +02:00
Oscar Krause
e04723d128 removed biggerthanshit link 2024-04-09 08:01:01 +02:00
Oscar Krause
8f498f4960 added 17.1 as supported 2024-04-08 15:10:01 +02:00
Oscar Krause
dd69f60fd0 added link to releases & release notes 2024-03-06 20:58:51 +01:00
Oscar Krause
a5d599a52c typos 2024-03-04 21:31:56 +01:00
Oscar Krause
66d203e72a requirements.txt updated 2024-03-04 21:13:12 +01:00
Oscar Krause
7800bf73a8 added "16.4" and "17.0" as supported 2024-02-27 15:44:34 +01:00
Oscar Krause
5b39598487 Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!30
2024-02-27 08:20:43 +01:00
Oscar Krause
ed59260a10 added "16.3" support 2024-02-26 20:53:47 +01:00
Oscar Krause
7c70d121be requirements.txt updated 2024-02-26 20:53:33 +01:00
Oscar Krause
213e768708 removed todo 2024-01-18 17:02:09 +01:00
Oscar Krause
0696900d67 fixes 2024-01-18 16:58:33 +01:00
Oscar Krause
4fb90a22e3 make tests interruptible 2024-01-18 13:10:12 +01:00
Oscar Krause
6aa197dcae only run test matrix when "app" or "test" changes 2024-01-18 13:09:30 +01:00
Oscar Krause
46f6c9fe99 fixed CI/CD path from "/builds" to "/tmp/builds" 2024-01-18 13:06:45 +01:00
Oscar Krause
2baaeb561b run different jobs on "$CI_DEFAULT_BRANCH" 2024-01-18 12:59:06 +01:00
Oscar Krause
867cd7018a removed pylint 2024-01-18 12:58:43 +01:00
Oscar Krause
9c686913dd disabled pylint 2024-01-18 12:46:51 +01:00
Oscar Krause
d3c4dc3fb7 disabled code_quality debug 2024-01-18 08:34:43 +01:00
Oscar Krause
af8b1c2387 Update .codeclimate.yml 2024-01-17 23:13:20 +01:00
Oscar Krause
d37d96dc34 fixed test_coverage (fail on matrix) 2024-01-17 23:05:57 +01:00
Oscar Krause
21d052523f added code_quality debug 2024-01-17 22:43:47 +01:00
Oscar Krause
22110df791 added code_quality “SOURCE_CODE” variable 2024-01-17 22:37:33 +01:00
Oscar Krause
c7f354d50c removed "cython" from "test" 2024-01-17 11:33:22 +01:00
Oscar Krause
3bdfc94527 removed tests for "23.04"
> gcc -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tmp/pip-install-sazb8fvo/httptools_694f06fa2e354ed9ba9f5c167df7fce4/vendor/llhttp/include -I/tmp/pip-install-sazb8fvo/httptools_694f06fa2e354ed9ba9f5c167df7fce4/vendor/llhttp/src -I/usr/local/include/python3.11 -c httptools/parser/parser.c -o build/temp.linux-x86_64-cpython-311/httptools/parser/parser.o -O2
      httptools/parser/parser.c:212:12: fatal error: longintrepr.h: No such file or directory
2024-01-17 09:33:58 +01:00
Oscar Krause
9473f10653 added tests for Ubuntu "Mantic Minotaur" 2024-01-17 08:08:37 +01:00
Oscar Krause
e9ad1d7791 requirements.txt updated 2024-01-12 14:53:17 +01:00
Oscar Krause
f97ee9c8fc updated debian bookworm 12 dependencies 2024-01-12 14:25:03 +01:00
Oscar Krause
236948e483 updated test to debian bookworm 2023-11-03 14:03:48 +01:00
Oscar Krause
948934ad0e fixed testing dependency 2023-11-03 12:53:50 +01:00
Oscar Krause
3ef14e5522 added gcc as dependency 2023-11-03 11:41:44 +01:00
Oscar Krause
ee50ede2ea fixes 2023-11-03 10:49:06 +01:00
Oscar Krause
b11579de98 fixed debian package versions 2023-11-03 09:28:23 +01:00
Oscar Krause
dc33c29158 fixed versions & added 16.2 as supported 2023-11-03 08:23:07 +01:00
Oscar Krause
6f9107087b added os specific requirements.txt 2023-10-25 07:36:17 +02:00
Oscar Krause
01fd954252 implemented python test matrix for different python dependencies on different os releases 2023-10-25 07:31:29 +02:00
Oscar Krause
995dbdac80 README.md updated 2023-10-25 07:30:57 +02:00
Oscar Krause
65de4d0534 Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!29
2023-10-16 10:27:49 +02:00
Oscar Krause
51b28dcdc3 updated ubuntu from 22.10 (EOL) to 23.04 2023-10-16 09:50:24 +02:00
Oscar Krause
9512e29ed9 requirements.txt updated 2023-09-26 07:09:06 +02:00
Oscar Krause
713e33eed1 added 16.1 as supported nvidia driver release 2023-09-26 07:08:58 +02:00
Oscar Krause
4b16b02a7d added macOS as supported host (using python-venv) 2023-09-26 07:08:41 +02:00
Oscar Krause
3e9d7c0061 added Docker supported system architectures 2023-09-26 07:08:12 +02:00
Oscar Krause
7480cb4cf7 added linkt to driver compatibility section 2023-07-13 06:46:27 +02:00
Oscar Krause
58ffa752f3 Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!28
2023-07-10 19:11:28 +02:00
Oscar Krause
2d7909546d requirements.txt updated 2023-07-10 18:47:46 +02:00
Oscar Krause
fec099ae81 added support for 16.0 drivers to readme 2023-07-10 13:32:32 +02:00
Oscar Krause
fd4fa84dc5 fixed docker image name (gitlab registry) 2023-07-04 19:39:06 +02:00
Oscar Krause
5ff3295658 fixed deploy docker 2023-07-04 18:58:13 +02:00
Oscar Krause
ca38ebe3fd Merge branch 'dev' into 'main'
Multiarch to DockerHub

See merge request oscar.krause/fastapi-dls!27
2023-07-04 18:47:45 +02:00
Oscar Krause
df5cb3c9c3 Merge branch 'main' into 'dev'
# Conflicts:
#   .gitlab-ci.yml
2023-07-04 16:19:49 +00:00
Oscar Krause
eca64fb1d5 push multiarch image to docker-hub 2023-07-04 17:47:10 +02:00
Oscar Krause
7ae1201c8f fixed new docker registry image path 2023-07-04 13:43:15 +02:00
Oscar Krause
a4e98dae46 fixed docker image path 2023-07-04 13:42:21 +02:00
Oscar Krause
d4267f3ee6 toggle api endpoints 2023-07-04 12:42:31 +02:00
Oscar Krause
c02ca762ea typos 2023-07-04 12:42:19 +02:00
Oscar Krause
10caf2310c added information about ipv6 may be must disabled 2023-07-04 12:39:13 +02:00
Oscar Krause
7380e4328e removed mysql from included docker drivers 2023-07-04 12:38:54 +02:00
Oscar Krause
c1eaa33d9e added docker command to logging section
thanks to @libreshare (https://gitea.publichub.eu/oscar.krause/fastapi-dls/issues/2)
2023-07-04 12:22:22 +02:00
Oscar Krause
45545953ed improvements
thanks to @AbsolutelyFree (https://gitea.publichub.eu/oscar.krause/fastapi-dls/issues/1)
2023-07-04 12:19:07 +02:00
Oscar Krause
4c8c2ed3d6 fixed "deploy:pacman" 2023-07-04 11:55:26 +02:00
Oscar Krause
6483af4ba9 Merge branch 'dev' into 'main'
Dev

See merge request oscar.krause/fastapi-dls!26
2023-07-04 11:39:37 +02:00
Oscar Krause
e6595c05d5 fixed mariadb-client installation
ref. https://github.com/PyMySQL/mysqlclient/discussions/624
2023-07-04 11:06:00 +02:00
Oscar Krause
fb1dbea1ee added missing "pkg-config" for "mysqlclient==2.2.0"
ref. https://stackoverflow.com/questions/76533384/docker-alpine-build-fails-on-mysqlclient-installation-with-error-exception-can
2023-07-04 10:50:35 +02:00
Oscar Krause
f576ded038 fixed versions 2023-07-04 10:33:30 +02:00
Oscar Krause
54eaf55ee8 refactored docker-compose.yml so very simple example, and moved proxy to "examples" directory 2023-07-04 10:24:11 +02:00
Oscar Krause
3119d2c7ea added 15.3 to supported drivers list 2023-07-04 10:18:07 +02:00
Oscar Krause
e40f4ce41f updated compatibility list 2023-07-04 10:17:45 +02:00
Oscar Krause
576f22333e docker-compose.yml - added note for TZ 2023-07-04 10:17:34 +02:00
Oscar Krause
0f53436700 requirements.txt updated 2023-07-04 10:17:12 +02:00
Oscar Krause
c79636b1c2 fixed .gitlab-ci.yml deprecated build-ref varialbes
ref. https://gitlab.com/gitlab-org/gitlab/-/issues/352957
2023-06-12 11:03:03 +02:00
Oscar Krause
8de9a89e56 Merge branch 'main' into 'dev'
# Conflicts:
#   requirements.txt
2023-06-12 08:56:01 +00:00
Oscar Krause
801d1786ef requirements.txt updated 2023-06-12 10:52:13 +02:00
Oscar Krause
7e5f8b6c8a implemented endpoint to remove expired leases 2023-06-12 10:48:00 +02:00
Oscar Krause
98da86fc2e removed debian bookworm testing notes 2023-06-12 09:31:43 +02:00
Oscar Krause
14cf6a953f typos 2023-05-09 06:58:51 +02:00
Oscar Krause
6a5d3cb2f7 requirements.txt updated 2023-05-09 06:57:17 +02:00
Oscar Krause
774a1c21a1 improved docker build with "ARG" instead of using "version.env" which is not present on local builds (because it's created by ci-pipeline) 2023-05-09 06:57:03 +02:00
Oscar Krause
d1a77df0e1 updated sudo / su commands (list sudo fist instead of su) 2023-04-17 10:58:25 +02:00
Oscar Krause
c9c73f6cf2 updated docker image requirements.txt 2023-04-17 10:35:23 +02:00
Oscar Krause
b216dcb3dd fixed nvidia-smi path on windows 2023-04-17 10:35:08 +02:00
Oscar Krause
d2e4042932 added 15.2 to supported versions 2023-04-01 23:20:06 +02:00
Oscar Krause
04a1ee0948 added Roadmap 2023-03-24 14:28:44 +01:00
Oscar Krause
c1b5f83f44 Merge branch 'multiarch' into 'main'
multiarch support

See merge request oscar.krause/fastapi-dls!25
2023-03-24 10:29:23 +01:00
Oscar Krause
9d1422cbdf secret detection 2023-03-24 10:00:25 +01:00
Oscar Krause
7b7f14bd82 Aktualisieren .gitlab-ci.yml 2023-03-24 09:31:27 +01:00
Oscar Krause
f72c0f7db3 Aktualisieren .gitlab-ci.yml 2023-03-24 09:11:26 +01:00
Oscar Krause
76d8753f28 Aktualisieren .gitlab-ci.yml 2023-03-24 09:07:49 +01:00
Oscar Krause
593db0e789 Aktualisieren .gitlab-ci.yml 2023-03-24 08:43:29 +01:00
Oscar Krause
3d9e3cb88f set specific arm64 version to v8 2023-03-24 07:48:35 +01:00
Oscar Krause
995b944135 removed "linux/arm/v7" 2023-03-23 11:46:17 +01:00
Oscar Krause
e200c84345 improvements 2023-03-23 11:10:53 +01:00
Oscar Krause
04ff36c94d Aktualisieren .gitlab-ci.yml 2023-03-23 08:36:34 +01:00
Oscar Krause
89704bc2a1 Aktualisieren .gitlab-ci.yml 2023-03-23 08:35:27 +01:00
Oscar Krause
6395214fa0 Aktualisieren .gitlab-ci.yml 2023-03-23 08:24:40 +01:00
Oscar Krause
c8e000eb3e Aktualisieren .gitlab-ci.yml 2023-03-23 08:22:51 +01:00
Oscar Krause
c8e5676c01 Aktualisieren .gitlab-ci.yml 2023-03-23 08:17:31 +01:00
Oscar Krause
6f11bc414c Aktualisieren .gitlab-ci.yml 2023-03-23 08:11:57 +01:00
Oscar Krause
1fc5ac8378 added setup_vgpu_license.sh script 2023-03-22 07:10:18 +01:00
Oscar Krause
87334fbfad added unraid section 2023-03-20 20:21:17 +01:00
Oscar Krause
0fac033657 Merge branch 'dev' into 'main'
dev

See merge request oscar.krause/fastapi-dls!24
2023-03-20 15:01:22 +01:00
Oscar Krause
7cd4e6fde0 fixes 2023-03-20 14:51:54 +01:00
Oscar Krause
a22b56edbe fixes 2023-03-20 14:33:50 +01:00
Oscar Krause
e42dc6aa86 code styling 2023-03-20 10:06:21 +01:00
Oscar Krause
86f703a36c ci improvements 2023-03-20 08:35:06 +01:00
Oscar Krause
71795cc7a2 di improvements 2023-03-20 08:07:24 +01:00
Oscar Krause
4ef041bb54 styling 2023-02-28 13:08:34 +01:00
Oscar Krause
88c8fb98da added some notes about included database drivers in docker image 2023-02-28 07:52:14 +01:00
Oscar Krause
a7b4a4b631 requirements.txt updated 2023-02-14 16:00:04 +01:00
Oscar Krause
7ccb254cbf dependency scanning 2023-02-14 15:48:49 +01:00
Oscar Krause
1d5d3b31fb dependency scanning 2023-02-14 15:32:32 +01:00
Oscar Krause
7af2e02627 improvements 2023-02-14 15:14:19 +01:00
Oscar Krause
938fc6bd60 added SAST 2023-02-14 14:50:21 +01:00
Oscar Krause
1b9ebb48b1 added secret detection 2023-02-14 13:55:49 +01:00
Oscar Krause
4972f00822 fixes 2023-02-14 13:37:25 +01:00
Oscar Krause
210a36c07f added code-quality and test-coverage 2023-02-14 12:59:31 +01:00
Oscar Krause
e1bbd42b50 Merge branch 'dev' into 'main'
1.3.5

See merge request oscar.krause/fastapi-dls!23
2023-02-13 17:30:11 +01:00
Oscar Krause
c1d541f7c6 bump version to 1.3.5 2023-02-13 08:09:37 +01:00
Oscar Krause
4b58fe6e20 added openSUSE Leap 15.4 support 2023-02-13 08:09:21 +01:00
Oscar Krause
b36b49df11 fixed missing mkdir for config file on manual installation method 2023-02-01 07:55:12 +01:00
Oscar Krause
a42b1c8cfb added note to be logged in as root using manual install method (git) 2023-01-30 12:34:46 +01:00
Oscar Krause
59152f95e6 fixed - The `declarative_base()` function is now available as sqlalchemy.orm.declarative_base() 2023-01-30 10:23:09 +01:00
Oscar Krause
62d347510d fixed - sqlalchemy.exc.ArgumentError: Textual SQL expression '\nCREATE TABLE origin (\n\to...' should be explicitly declared as text('\nCREATE TABLE origin (\n\to...') 2023-01-30 10:22:18 +01:00
Oscar Krause
f540c4b25b requirements.txt updated 2023-01-30 09:19:03 +01:00
Oscar Krause
70212e0edd improved docker-compose examples 2023-01-30 09:18:57 +01:00
Oscar Krause
616e8fba5e README - improvements 2023-01-30 08:37:34 +01:00
Oscar Krause
b905ab9dd9 Merge branch 'dev' into 'main'
1.3.4

See merge request oscar.krause/fastapi-dls!22
2023-01-26 07:56:34 +01:00
Oscar Krause
9edc93653e bump version to 1.3.4 2023-01-26 07:38:48 +01:00
Oscar Krause
f30e9237a5 requirements.txt updated 2023-01-26 07:18:01 +01:00
Oscar Krause
f12dc28c42 fixed overriding config file on update / reinstall 2023-01-26 07:17:16 +01:00
Oscar Krause
02276d5440 disabled openapi endpoints 2023-01-23 07:33:54 +01:00
Oscar Krause
9ebff8d6ca typos 2023-01-23 07:29:13 +01:00
Oscar Krause
48eb6d6c64 typos 2023-01-23 07:23:42 +01:00
Oscar Krause
f7ef8d76b6 fixed Origin.delete() 2023-01-23 07:12:02 +01:00
Oscar Krause
bed24b56ce styling 2023-01-19 08:26:35 +01:00
Oscar Krause
95427d430e added startup script 2023-01-19 07:26:22 +01:00
Oscar Krause
c3ea0aa48c added variable for client-token-expire-delta 2023-01-19 07:26:07 +01:00
Oscar Krause
91be7b226c added some comments for default values 2023-01-19 07:25:44 +01:00
Oscar Krause
7045692958 added official links 2023-01-19 07:25:24 +01:00
Oscar Krause
38177fa259 styling 2023-01-18 14:29:48 +01:00
Oscar Krause
9411759f6d added system requirements and preparements 2023-01-18 14:23:34 +01:00
Oscar Krause
48c37987b2 fixed logging and added current timezone info 2023-01-18 14:23:25 +01:00
Oscar Krause
e3745d7fa8 Merge branch 'dev' into 'main'
1.3.3

See merge request oscar.krause/fastapi-dls!21
2023-01-18 08:13:42 +01:00
Oscar Krause
5bb8f17679 improvements 2023-01-18 08:07:55 +01:00
Oscar Krause
de17b0f1b5 fixes 2023-01-18 08:03:02 +01:00
Oscar Krause
0ab5969d3a fixes 2023-01-18 06:56:16 +01:00
Oscar Krause
059a51fe74 refactored commands 2023-01-17 17:25:48 +01:00
Oscar Krause
bf858b38f4 fixes 2023-01-17 17:09:13 +01:00
Oscar Krause
f60f08d543 run powershell as administrator 2023-01-17 16:57:15 +01:00
Oscar Krause
b2e6fab294 fixes 2023-01-17 16:49:15 +01:00
Oscar Krause
b09bb091a5 bump version to 1.3.3 2023-01-17 16:29:32 +01:00
Oscar Krause
651af4cc82 fixed client-token url and added wget als alternative to curl 2023-01-17 16:29:21 +01:00
Oscar Krause
70f7d3f483 mark Let's Encrypt section as optional 2023-01-17 15:36:38 +01:00
Oscar Krause
1e4070a1ba added remove "/usr/share/fastapi-dls" to "postrm" 2023-01-17 14:57:54 +01:00
Oscar Krause
d69d833923 migrated "[[ ]]" if statements to "[ ]" 2023-01-17 14:57:39 +01:00
Oscar Krause
7ef071f92b removed fastapi-dls.service from conffiles 2023-01-17 14:57:09 +01:00
Oscar Krause
3c19fc9d5b implemented "lease_renewal" attribute as calculated value within what period of time the license must be renewed 2023-01-17 11:49:56 +01:00
Oscar Krause
164b5ebc44 Merge branch 'dev' into 'main'
1.3.2

See merge request oscar.krause/fastapi-dls!20
2023-01-17 11:36:23 +01:00
Oscar Krause
742fa07ed4 bump version to 1.3.2 2023-01-17 11:18:25 +01:00
Oscar Krause
a758d93970 main.py - fixed empty lease origin response 2023-01-17 11:18:07 +01:00
Oscar Krause
70250f1fca Merge branch 'dev' into 'main'
1.3.1

See merge request oscar.krause/fastapi-dls!19
2023-01-16 13:08:57 +01:00
Oscar Krause
a65687a082 bump version to 1.3.1 2023-01-16 10:34:20 +01:00
Oscar Krause
3e445c80aa fixes 2023-01-16 10:33:52 +01:00
Oscar Krause
20cc984799 FAQ.md 2023-01-16 10:30:55 +01:00
Oscar Krause
3495cc3af5 typos 2023-01-16 10:30:40 +01:00
Oscar Krause
ed13577e82 Dockerfile - updated to python 3.11 2023-01-16 10:30:21 +01:00
Oscar Krause
ca8a9df54c requirements.txt updated 2023-01-16 10:24:08 +01:00
Oscar Krause
5425eec545 .gitlab-ci.yml simplified 2023-01-16 10:23:58 +01:00
Oscar Krause
2f3c7d5433 Merge branch 'main' into dev 2023-01-16 07:00:20 +01:00
Oscar Krause
b551b0e7f9 README.md - added sunsupoorted ubuntu version 2023-01-15 19:47:50 +01:00
Oscar Krause
50dea9ac4e fixes 2023-01-05 14:08:08 +01:00
Oscar Krause
549a48a10b Merge branch 'dev' into 'main'
1.3

See merge request oscar.krause/fastapi-dls!18
2023-01-05 07:27:10 +01:00
Oscar Krause
1f3bc8b4af .gitlab-ci.yml 2023-01-05 07:22:25 +01:00
Oscar Krause
5fc8d4091b Merge branch 'dev' into 'main'
1.3

See merge request oscar.krause/fastapi-dls!17
2023-01-05 07:21:28 +01:00
Oscar Krause
851ec1a5c6 requirements.txt updated 2023-01-05 06:56:56 +01:00
Oscar Krause
9180222169 README.md 2023-01-04 21:46:02 +01:00
Oscar Krause
e71d4c4f4e fixed missing servie file for DEBIAN 2023-01-04 18:27:57 +01:00
Oscar Krause
aecad82914 main.py - added confirmation to deleteOrigins() 2023-01-04 18:12:59 +01:00
Oscar Krause
02fccb3605 README.md 2023-01-04 18:05:07 +01:00
Oscar Krause
24dba89dbe removed todos, currently all done or there is a branch for it 2023-01-04 17:58:23 +01:00
Oscar Krause
f5557a5ccd README.md 2023-01-04 17:46:19 +01:00
Oscar Krause
e8736c94ec docker-compose.yml - disabled internal ssl support 2023-01-04 17:46:02 +01:00
Oscar Krause
4325560ec4 README.md - added some collapses for logs 2023-01-04 17:18:13 +01:00
Oscar Krause
05979490ce README.md - moved "Endpoints" below "Setup" 2023-01-04 17:17:58 +01:00
Oscar Krause
52ffedffc7 code styling 2023-01-04 11:14:26 +01:00
Oscar Krause
5f5569a0c7 improved debian installation 2023-01-04 11:02:54 +01:00
Oscar Krause
32b05808c4 fixed "return" instead of "raise" 2023-01-04 10:14:00 +01:00
Oscar Krause
6c9ea63dc1 added variable for TOKEN_EXPIRE_DELTA 2023-01-04 10:08:17 +01:00
Oscar Krause
b839e6c2b3 code styling
- replaced 'json.loads' with 'json_loads'
- shortened 'JSONResponse' to 'JSONr'
- shortened 'HTMLResponse' to 'HTMLr'
- replaced HTTPException with JsonResponses
- added some error handing for invalid tokens
2023-01-04 10:04:52 +01:00
Oscar Krause
8bd37c0ead added some notes to required variables to change 2023-01-04 07:40:37 +01:00
Oscar Krause
27f47b93b8 docker-compose.yml - added experimental health endpoint 2023-01-03 20:45:16 +01:00
Oscar Krause
5bb8437b1d README.md - added timestamp to linux token filename 2023-01-03 18:59:34 +01:00
Oscar Krause
7e3f2d0345 docker-compose.yml - fixes 2023-01-03 18:44:30 +01:00
Oscar Krause
4198021212 README.md - fixed windows issue with /leasing/v1/lessor/shutdown 2023-01-03 18:10:02 +01:00
Oscar Krause
7e6e523799 improved test (checking uuid are 36 chars long) 2023-01-03 18:05:46 +01:00
Oscar Krause
7b2428ea38 removed some debugging 2023-01-03 18:05:46 +01:00
Oscar Krause
ac811d5df7 added 'LEASE_EXPIRE_HOURS' variable for better debugging 2023-01-03 18:05:46 +01:00
Oscar Krause
5575fee382 fixed config test 2023-01-03 18:05:46 +01:00
Oscar Krause
f1369d5e25 added some docs 2023-01-03 17:38:45 +01:00
Oscar Krause
d6cc6dcbee fixes 2023-01-03 17:38:32 +01:00
Oscar Krause
01fe142850 .gitlab-ci.yml - fixed release job 2023-01-03 15:22:49 +01:00
Oscar Krause
18e9ab2ebf fixes 2023-01-03 14:52:31 +01:00
Oscar Krause
b64c531898 bump version to 1.3 2023-01-03 14:50:52 +01:00
Oscar Krause
ef1730f4fe orm.py - added some docs 2023-01-03 14:20:13 +01:00
Oscar Krause
146ae8b824 updated docs 2023-01-03 14:09:35 +01:00
Oscar Krause
5a5ad0e654 removed 'scope_ref' from code checks because we only support one 'ALLOTMENT_REF', so we need no checks 2023-01-03 14:09:19 +01:00
Oscar Krause
0e3e7cbd3a main.py - corrected leasing behaviour (migrated from 'LEASE_REF' to 'ALLOTMENT_REF') 2023-01-03 13:05:05 +01:00
Oscar Krause
bd5625af42 main.py - removed example responses 2023-01-03 13:02:37 +01:00
Oscar Krause
8f9d95056f code styling - migrated direct dict access to '.get()' 2023-01-03 09:20:18 +01:00
Oscar Krause
2b8c468270 main.py - fixed missing 'LEASE_RENEWAL_PERIOD' on '/auth/v1/origin' 2023-01-03 07:25:09 +01:00
Oscar Krause
50e0dc8d1f implemented '/leasing/v1/lessor/shutdown' for windows guests 2023-01-02 19:42:23 +01:00
Oscar Krause
8b934dfeef fixed '/-/config' endpoint serialisation 2023-01-02 19:23:23 +01:00
Oscar Krause
4fb6243330 removed deprecated endpoints
- '/client-token' moved to '/-/client-token'
- '/status' moved to '/-/health' and '/-/config'

see README.md for more information
2023-01-02 19:18:32 +01:00
Oscar Krause
2e950ca6f4 implemented '/-/config' endpoint to list runtime environment variables 2023-01-02 19:14:25 +01:00
Oscar Krause
34662e6612 implemented 'LEASE_RENEWAL_PERIOD' variable 2023-01-02 18:57:41 +01:00
Oscar Krause
a3e089a3d5 added some references 2023-01-02 18:10:11 +01:00
Oscar Krause
ab996bb030 code styling 2023-01-02 18:04:14 +01:00
Oscar Krause
0853dd64cb README.md - added known issue for error on releasing leases on windows shutdown 2023-01-02 14:12:15 +01:00
Oscar Krause
838956bdb7 README.md - added '-L' parameter to curl commands to follow redirects (from deprecated endpoints) 2023-01-02 11:40:19 +01:00
Oscar Krause
8c515b7f2e README.md - removed links from endpoints 2023-01-02 11:39:37 +01:00
Oscar Krause
de5f07273b README.md - added compatibility to official dls 2023-01-02 11:38:48 +01:00
Oscar Krause
c894537ff9 Merge branch 'dev' into 'main'
1.2

See merge request oscar.krause/fastapi-dls!16
2022-12-30 07:51:26 +01:00
Oscar Krause
98d7492534 main.py - fixed cors parsing 2022-12-30 07:42:57 +01:00
Oscar Krause
2368cc2578 bump version to 1.2 2022-12-30 07:37:36 +01:00
Oscar Krause
5e40d7944a PKGBUILD - updated service running uvicorn natively instead of calling main.py
- fixed issue with not loading env variables inside to fastapi
- fixed to not using "python main.py" which meant for development
2022-12-30 07:36:44 +01:00
Oscar Krause
5fc9fc8e0a added documentation to debian service 2022-12-30 07:14:25 +01:00
Oscar Krause
b0e10004f1 README.md - added windows license key installation from powershell 2022-12-30 07:11:02 +01:00
Oscar Krause
478ca0ab63 added some comments 2022-12-30 07:02:50 +01:00
Oscar Krause
3d83e533da fixed client-token filename (missing .tok extension) 2022-12-30 03:50:48 +01:00
Oscar Krause
1f56d31351 code styling 2022-12-29 20:42:40 +01:00
Oscar Krause
400c983025 added redirect for "/-/" route 2022-12-29 20:41:55 +01:00
Oscar Krause
fa3a06a360 code styling 2022-12-29 20:40:42 +01:00
Oscar Krause
c0ab3a589f migrated '/client-token' to '/-/client-token' 2022-12-29 20:33:50 +01:00
Oscar Krause
a8504f3017 hardcoded default CORS to https, since drivers only support secure connections 2022-12-29 19:14:49 +01:00
Oscar Krause
9a5cf9ff81 code styling 2022-12-29 19:07:30 +01:00
Oscar Krause
17978c2e2e main.py - added endpoint to release single lease 2022-12-29 19:03:09 +01:00
Oscar Krause
569ca8b3ea orm.py - fixed renewing timestamps from params 2022-12-29 19:00:14 +01:00
Oscar Krause
e0843ca1d4 code styling 2022-12-29 18:59:26 +01:00
Oscar Krause
3fad49b18a main.py - added api descriptions 2022-12-29 18:48:30 +01:00
Oscar Krause
82876bf6b1 .gitlab-ci.yml - fixed release 2022-12-29 13:14:16 +01:00
Oscar Krause
dc6b6bff69 Merge branch 'dev' into 'main'
fixes

See merge request oscar.krause/fastapi-dls!15
2022-12-29 13:03:22 +01:00
Oscar Krause
e91436b236 README.md - fixed redoc links 2022-12-29 12:58:19 +01:00
Oscar Krause
6a0c35a7a8 .gitlab-ci.yml - fixed deploy:pacman pipeline 2022-12-29 12:58:01 +01:00
Oscar Krause
0b7bedde66 Merge branch 'dev' into 'main'
1.1

See merge request oscar.krause/fastapi-dls!14
2022-12-29 12:54:37 +01:00
Oscar Krause
00f7c50e4e .gitlab-ci.yml - added release job 2022-12-29 12:47:51 +01:00
Oscar Krause
13ec45e762 orm.py - added init call after dropping table by migration 2022-12-29 12:34:25 +01:00
Oscar Krause
0983426f30 .gitlab-ci.yml improvements 2022-12-29 12:30:23 +01:00
Oscar Krause
0c3a38b84e .gitlab-ci.yml - fixed MR pipeline 2022-12-29 12:26:05 +01:00
Oscar Krause
51183f6845 updated hashes 2022-12-29 12:21:41 +01:00
Oscar Krause
5f87e65034 bump version to 1.1 2022-12-29 12:19:56 +01:00
Oscar Krause
26d6d1feeb updated variables descriptions 2022-12-29 12:19:49 +01:00
Oscar Krause
ca6942becc added some comments 2022-12-29 12:15:05 +01:00
Oscar Krause
ff02c77afe use version variable in PKGBUILD 2022-12-29 12:14:53 +01:00
Oscar Krause
85e2ef6930 use version variable in DEBIAN/control 2022-12-29 12:12:03 +01:00
Oscar Krause
47312f65d9 .gitlab-ci.yml improved 2022-12-29 10:44:39 +01:00
Oscar Krause
a59b720f3f fixes 2022-12-29 10:40:34 +01:00
Oscar Krause
1b2da802cb added tests for new endpoints 2022-12-29 10:37:47 +01:00
Oscar Krause
8b9c7d688b added some docs to custom endpoints 2022-12-29 10:35:15 +01:00
Oscar Krause
a09fc5f2ad added some new endpoints and links in readme 2022-12-29 10:31:25 +01:00
Oscar Krause
ed1b55f5f1 created a simple management ui 2022-12-29 10:12:31 +01:00
Oscar Krause
2b7fed3381 created endpoints to delete origins and to delete a lease 2022-12-29 09:57:37 +01:00
Oscar Krause
922dc9f5a7 refactored database structure and created migration script 2022-12-29 09:40:50 +01:00
Oscar Krause
1a50e28202 main.py - removed unused import 2022-12-29 09:15:51 +01:00
Oscar Krause
a7cb6a7756 PKGBUILD - include version file 2022-12-29 09:15:33 +01:00
Oscar Krause
001b70b89c README.md - added credits 2022-12-29 09:15:12 +01:00
Oscar Krause
e6790588ef Revert "CODEOWNERS"
This reverts commit d57b494779.
2022-12-29 09:12:56 +01:00
Oscar Krause
d57b494779 CODEOWNERS 2022-12-29 09:12:30 +01:00
Oscar Krause
07de2401d7 REAMDE.md - added shout out to @samicrusader 2022-12-29 09:07:20 +01:00
Oscar Krause
d86948aee2 added some comments 2022-12-29 09:01:36 +01:00
Oscar Krause
6b2e6bf392 added optional query parameter to '/-/origins' and '/-/leases' for linked leases/origin 2022-12-29 09:00:52 +01:00
Oscar Krause
913da290f1 PKGBUILD - fixed missing util.py 2022-12-29 08:48:34 +01:00
Oscar Krause
5c1d291fac .gitlab-ci.yml improvements 2022-12-29 08:00:34 +01:00
Oscar Krause
76f732adb6 .gitlab-ci.yml - fixed test:debian 2022-12-29 07:54:10 +01:00
Oscar Krause
d73221afb7 bump version to 1.0 2022-12-29 07:41:25 +01:00
Oscar Krause
a6ac58d12c fixes 2022-12-29 07:41:25 +01:00
Oscar Krause
aa76ba5650 .gitlab-ci.yml improvements 2022-12-29 07:32:12 +01:00
Oscar Krause
7abfb96841 README.md - added archlinux section 2022-12-29 07:17:51 +01:00
Oscar Krause
6978ba4873 orm.py - timestamps are not updated in database 2022-12-29 07:09:39 +01:00
Oscar Krause
21e61796ff fixes 2022-12-28 22:02:12 +01:00
Oscar Krause
3c4fb35498 pacman - test version x.y.z instead of x.y 2022-12-28 22:01:20 +01:00
Oscar Krause
b5ed098093 fixed debian install scripts permissions 2022-12-28 22:00:37 +01:00
Oscar Krause
478dc04787 testing "deploy:pacman" job 2022-12-28 21:55:42 +01:00
Oscar Krause
eddf9217e5 refactorings 2022-12-28 21:52:19 +01:00
Oscar Krause
903ef73280 Merge branch 'archlinux-makepkg' into 'dev'
Archlinux makepkg

See merge request oscar.krause/fastapi-dls!13
2022-12-28 21:42:01 +01:00
Oscar Krause
a02d1ab9df .gitlab-ci.yml - handle artifact 2022-12-28 21:40:52 +01:00
Oscar Krause
34283555a1 refactorings 2022-12-28 21:40:26 +01:00
Oscar Krause
abb56be3bb added git 2022-12-28 21:15:32 +01:00
Oscar Krause
571e654af1 fixes 2022-12-28 21:14:14 +01:00
Oscar Krause
f04d4905df applied changes from samicrusader <hi@samicrusader.me> 2022-12-28 21:13:20 +01:00
Oscar Krause
7f99c260ce added PKGBUILD 2022-12-28 17:05:59 +01:00
Oscar Krause
15d52f7586 added PKGBUILD 2022-12-28 17:01:57 +01:00
Oscar Krause
62af76b95a added PKGBUILD 2022-12-28 17:00:35 +01:00
Oscar Krause
32a512b89b fixes 2022-12-28 15:59:38 +01:00
Oscar Krause
321cd17b02 updated PKGBUILD 2022-12-28 15:57:55 +01:00
Oscar Krause
bb43fc3f49 .gitlab-ci.yml 2022-12-28 15:27:16 +01:00
Oscar Krause
12f661707f added PKGBUILD 2022-12-28 15:24:04 +01:00
Oscar Krause
837721fd7b finished all remaining tests 2022-12-28 14:54:30 +01:00
Oscar Krause
d4ca6ba1aa fixed imports 2022-12-28 14:39:04 +01:00
Oscar Krause
9ab0eb4796 .gitlab-ci.yml - added ubuntu to test:debian stage 2022-12-28 14:36:22 +01:00
Oscar Krause
d91b81e50f improved tests 2022-12-28 14:30:54 +01:00
Oscar Krause
2663901988 util.py - implemented generate key method 2022-12-28 14:30:42 +01:00
Oscar Krause
8633190e97 removed todo for migrating to flask 2022-12-28 13:45:42 +01:00
Oscar Krause
a7fb43e1dc .gitlab-ci.yml improvements 2022-12-28 12:08:13 +01:00
Oscar Krause
5af1ba106d .gitlab-ci.yml improvements 2022-12-28 12:05:56 +01:00
Oscar Krause
fb858adc0c README.md 2022-12-28 12:01:57 +01:00
Oscar Krause
dacfd2084f code styling 2022-12-28 11:54:01 +01:00
Oscar Krause
92fe6154e6 code styling 2022-12-28 11:53:56 +01:00
Oscar Krause
3d5203dae0 Merge branch 'dev' into 'main'
1.0.0

See merge request oscar.krause/fastapi-dls!12
2022-12-28 11:44:52 +01:00
Oscar Krause
c83130f138 README.md - added known issue 2022-12-28 11:33:26 +01:00
Oscar Krause
a951433ca0 fixes 2022-12-28 11:33:06 +01:00
Oscar Krause
dada9cc4cd fixes 2022-12-28 11:05:41 +01:00
Oscar Krause
670e05f693 .gitlab-ci.yml 2022-12-28 10:00:34 +01:00
Oscar Krause
e88b1afcf7 fixes 2022-12-28 09:57:55 +01:00
Oscar Krause
0e24d26089 README.md 2022-12-28 09:47:31 +01:00
Oscar Krause
3d073dbd7d bump version to 1.0.0 2022-12-28 09:24:41 +01:00
Oscar Krause
89bf744054 removed some todos 2022-12-28 09:24:02 +01:00
Oscar Krause
e1f2e942a6 code styling 2022-12-28 09:23:17 +01:00
Oscar Krause
2afa01273a Merge branch 'debian' into 'dev'
Debian

See merge request oscar.krause/fastapi-dls!11
2022-12-28 09:16:32 +01:00
Oscar Krause
943786099b Merge branch 'sqlalchemy' into 'dev'
Sqlalchemy

See merge request oscar.krause/fastapi-dls!10
2022-12-28 09:15:03 +01:00
Oscar Krause
5db66c893d Merge branch 'dev' into sqlalchemy
# Conflicts:
#	README.md
2022-12-28 09:14:41 +01:00
Oscar Krause
3dc9c8bcb1 README.md 2022-12-28 09:10:57 +01:00
Oscar Krause
b22613c337 postinst improvements 2022-12-28 09:04:35 +01:00
Oscar Krause
2340931a60 fixes 2022-12-28 08:57:35 +01:00
Oscar Krause
437b62376f fixed missing debian dependency 2022-12-28 08:56:11 +01:00
Oscar Krause
e9dc5a765a fixed service
Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
2022-12-28 08:52:13 +01:00
Oscar Krause
4e5559bb85 fixed service
Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
2022-12-28 08:51:55 +01:00
Oscar Krause
b745367baa postrm fixed 2022-12-28 08:48:36 +01:00
Oscar Krause
914fc17795 Merge branch 'dev' into debian
# Conflicts:
#	README.md
2022-12-28 08:39:10 +01:00
Oscar Krause
050d105659 README.md - added Let's Encrypt section 2022-12-28 08:37:47 +01:00
Oscar Krause
da21ef3cdc fixed some permissions 2022-12-28 08:35:59 +01:00
Oscar Krause
6844604a0b fixed deb package paths 2022-12-28 08:35:42 +01:00
Oscar Krause
45af6c11c0 fixed missing systemctl daemon-reload 2022-12-28 08:21:04 +01:00
Oscar Krause
cf21bec3b0 postrm fixed removing app dir 2022-12-28 08:05:35 +01:00
Oscar Krause
6b3f536681 fixes
- fixed app dir
- fixed missing readme and version file
- keep config on update/remove
2022-12-28 07:40:44 +01:00
Oscar Krause
cca24f0ad5 fixed instance keypair path 2022-12-28 07:31:23 +01:00
Oscar Krause
ddb1299f5c Merge branch 'dev' into debian 2022-12-28 07:29:54 +01:00
Oscar Krause
a95126f51d typos 2022-12-28 07:29:42 +01:00
Oscar Krause
180cdcb43d added some variables 2022-12-28 07:29:38 +01:00
Oscar Krause
db412c6a43 postrm - remove service 2022-12-28 07:16:34 +01:00
Oscar Krause
a08261f7cd postinst - fixed paths and permissions 2022-12-28 07:14:24 +01:00
Oscar Krause
9744a8f0e8 code styling 2022-12-28 07:04:10 +01:00
Oscar Krause
63670f52e8 postinst fixes 2022-12-28 07:03:41 +01:00
Oscar Krause
65937b153e typos 2022-12-28 06:58:50 +01:00
Oscar Krause
84f7e99c78 README.md - adde toc 2022-12-28 06:58:26 +01:00
Oscar Krause
2af4b456b6 fixes 2022-12-28 06:56:31 +01:00
Oscar Krause
0b46212f28 Merge branch 'dev' into debian 2022-12-28 06:54:50 +01:00
Oscar Krause
3b75e8dbeb fixes 2022-12-28 06:54:25 +01:00
Oscar Krause
8c1c51897f README.md - added install instructions 2022-12-28 06:53:31 +01:00
Oscar Krause
52faba5a1d Merge branch 'dev' into debian 2022-12-28 06:50:19 +01:00
Oscar Krause
46620c5e2a typos 2022-12-28 06:50:04 +01:00
Oscar Krause
c820dac4ec README.md - improvements & fixed manual install steps 2022-12-28 06:49:18 +01:00
Oscar Krause
548e1c9492 postinst - fixed service file 2022-12-28 06:47:06 +01:00
Oscar Krause
0f345f52ab postinst - fixed "cat" instead of "echo" 2022-12-28 06:46:42 +01:00
Oscar Krause
18d6da8ebf fixes 2022-12-27 22:18:02 +01:00
Oscar Krause
9a0db3c18f .gitlab-ci.yml - using generic package registry temporary 2022-12-27 21:59:52 +01:00
Oscar Krause
15c49d396f README.md - added required cipher suite for windows guests 2022-12-27 20:35:04 +01:00
Oscar Krause
c38ed25a2f fixes 2022-12-27 20:28:09 +01:00
Oscar Krause
1b34edfda6 fixes 2022-12-27 20:22:00 +01:00
Oscar Krause
12bfd4c82a removed toc 2022-12-27 20:19:50 +01:00
Oscar Krause
2a3e740964 added toc 2022-12-27 20:19:23 +01:00
Oscar Krause
85736c5ce4 typos 2022-12-27 20:10:18 +01:00
Oscar Krause
07f1e64553 fixes 2022-12-27 20:08:37 +01:00
Oscar Krause
560b18b5c4 orm.py - fixed not null column 2022-12-27 19:57:58 +01:00
Oscar Krause
b5c64038cb main.py - migrated merged changes from dataset to sqlalchemy 2022-12-27 19:05:41 +01:00
Oscar Krause
c7aa28382a Merge branch 'dev' into sqlalchemy
# Conflicts:
#	app/main.py
2022-12-27 19:04:41 +01:00
Oscar Krause
6d5ed1a142 main.py - added origin update endpoint 2022-12-27 19:03:03 +01:00
Oscar Krause
11a2c1d129 added "CAP_NET_BIND_SERVICE" to debian service to allow low range ports for non root user "www-data" 2022-12-27 18:51:20 +01:00
Oscar Krause
cefee22202 README.md - fixed srevice type 2022-12-27 18:38:26 +01:00
Oscar Krause
e5f557eb96 README.md - added todos 2022-12-27 17:49:52 +01:00
Oscar Krause
f9e3740150 main.py - added env variable for "INSTANCE_REF" 2022-12-27 17:42:58 +01:00
Oscar Krause
7898052207 fixed service 2022-12-27 17:00:33 +01:00
Oscar Krause
3d6da6fab9 README - fixed debian installation via git 2022-12-27 16:59:35 +01:00
Oscar Krause
6ddba90cd8 README fixed 2022-12-27 15:28:52 +01:00
Oscar Krause
6f143f2199 .gitlab-ci.yml - fixed filename 2022-12-27 14:52:17 +01:00
Oscar Krause
c2e04552f7 debian - bump version to 0.6.0 2022-12-27 14:45:03 +01:00
Oscar Krause
6947d928ec .gitlab-ci.yml - fixed artifact upload with access token 2022-12-27 14:04:33 +01:00
Oscar Krause
8f5ff50aaf .gitlab-ci.yml - dynamically create repo for codename if not exist 2022-12-27 13:34:21 +01:00
Oscar Krause
9d900c4f5c .gitlab-ci.yml - create initial debian repo 2022-12-27 13:27:27 +01:00
Oscar Krause
751546995d .gitlab-ci.yml - fixed artifact upload 2022-12-27 12:56:46 +01:00
Oscar Krause
4c643b18dd .gitlab-ci.yml - implemented deploy stage for debian package 2022-12-27 12:49:12 +01:00
Oscar Krause
b89381fdfc Merge branch 'dev' into debian 2022-12-27 12:44:33 +01:00
Oscar Krause
4df5f18b67 .gitlab-ci.yml - improved testing 2022-12-27 12:40:33 +01:00
Oscar Krause
701453b18a .gitlab-ci.yml - fixes 2022-12-27 12:35:07 +01:00
Oscar Krause
507ce93718 .gitlab-ci.yml - test starting service 2022-12-27 12:32:40 +01:00
Oscar Krause
52fb18dea0 main.py - fixed imports for "Crypto" and "Cryptodome" (on debian) 2022-12-27 12:21:52 +01:00
Oscar Krause
7c8a113fbd .gitlab-ci.yml - added "DEBIAN_FRONTEND=noninteractive" for debian test 2022-12-27 11:05:11 +01:00
Oscar Krause
a91e1f7018 README.md - added supported package version 14.4 2022-12-27 11:03:53 +01:00
Oscar Krause
646cca42f4 .gitlab-ci.yml - removed some debugging 2022-12-27 10:38:49 +01:00
Oscar Krause
60ec2821e2 postinst - add default value 2022-12-27 10:38:26 +01:00
Oscar Krause
ab30ad2117 .gitlab-ci.yml - debugging 2022-12-27 10:23:51 +01:00
Oscar Krause
e2cea71365 .gitlab-ci.yml - added some debugging 2022-12-27 10:22:03 +01:00
Oscar Krause
5d48f6b7d5 .gitlab-ci.yml - fixed artifact path 2022-12-27 10:19:35 +01:00
Oscar Krause
1e84e141df fixes 2022-12-27 10:16:04 +01:00
Oscar Krause
98e98ccd84 chroot into "build" dir 2022-12-27 10:10:00 +01:00
Oscar Krause
f1eddaa99a fixed missing directory 2022-12-27 10:05:52 +01:00
Oscar Krause
df0816832e fixed conffiles 2022-12-27 10:04:26 +01:00
Oscar Krause
599eaba14a README.md - added supported and tested driver versions 2022-12-27 09:19:05 +01:00
Oscar Krause
4e17e6da82 main.py fixed pycryptodome import 2022-12-23 14:09:13 +01:00
Oscar Krause
843d918e59 added dependencies 2022-12-23 14:08:56 +01:00
Oscar Krause
952a74cabe Merge branch 'sqlalchemy' into debian
# Conflicts:
#	app/main.py
2022-12-23 13:50:50 +01:00
Oscar Krause
81608fe497 merged dev into debian 2022-12-23 13:48:48 +01:00
Oscar Krause
b00a2a032a Merge branch 'dev' into debian
# Conflicts:
#	.gitlab-ci.yml
2022-12-23 13:48:24 +01:00
Oscar Krause
6b7c70e59a tests improved 2022-12-23 13:42:02 +01:00
Oscar Krause
332b9b23cd code styling 2022-12-23 13:31:43 +01:00
Oscar Krause
3d5d728d67 code styling 2022-12-23 13:22:06 +01:00
Oscar Krause
838e30458d code styling 2022-12-23 13:21:52 +01:00
Oscar Krause
f539db5933 implemented db_init 2022-12-23 13:17:19 +01:00
Oscar Krause
6049048bbf fixed test 2022-12-23 11:24:40 +01:00
Oscar Krause
43d5736f37 code styling & removed comments 2022-12-23 08:22:21 +01:00
Oscar Krause
e7102c4de6 fixed updates 2022-12-23 08:16:58 +01:00
Oscar Krause
d1db441df4 removed Auth 2022-12-23 08:16:34 +01:00
Oscar Krause
d5b51bd83c Merge branch 'dev' into sqlalchemy
# Conflicts:
#	app/main.py
2022-12-23 08:08:35 +01:00
Oscar Krause
3f71c88d48 added some test 2022-12-23 07:48:47 +01:00
Oscar Krause
a58549a162 .gitlab-ci.yml - fixed test cert path 2022-12-23 07:43:02 +01:00
Oscar Krause
2c1c9b63b4 .gitignore 2022-12-23 07:41:23 +01:00
Oscar Krause
3367977652 .gitlab-ci.yml - fixed cd into test 2022-12-23 07:41:18 +01:00
Oscar Krause
67ed6108a2 .gitlab-ci.yml - changed test image to bullseye 2022-12-23 07:40:27 +01:00
Oscar Krause
d5d156e70e .gitlab-ci.yml - create test certificates 2022-12-23 07:38:53 +01:00
Oscar Krause
906af9430a .gitlab-ci.yml - fixed installing dependencies 2022-12-23 07:36:33 +01:00
Oscar Krause
3f5e3b16c5 added api tests 2022-12-23 07:35:37 +01:00
Oscar Krause
394180652e migrated from dataset to sqlalchemy 2022-12-22 12:57:06 +01:00
Oscar Krause
f0fdfafaed added basic debian package setup and pipeline 2022-12-22 10:41:07 +01:00
36 changed files with 2831 additions and 297 deletions

1
.DEBIAN/conffiles Normal file
View File

@ -0,0 +1 @@
/etc/fastapi-dls/env

9
.DEBIAN/control Normal file
View File

@ -0,0 +1,9 @@
Package: fastapi-dls
Version: 0.0
Architecture: all
Maintainer: Oscar Krause oscar.krause@collinwebdesigns.de
Depends: python3, python3-fastapi, python3-uvicorn, python3-dotenv, python3-dateutil, python3-josepy, python3-sqlalchemy, python3-pycryptodome, python3-markdown, uvicorn, openssl
Recommends: curl
Installed-Size: 10240
Homepage: https://git.collinwebdesigns.de/oscar.krause/fastapi-dls
Description: Minimal Delegated License Service (DLS).

27
.DEBIAN/env.default Normal file
View File

@ -0,0 +1,27 @@
# Toggle debug mode
#DEBUG=false
# Where the client can find the DLS server
DLS_URL=127.0.0.1
DLS_PORT=443
# CORS configuration
## comma separated list without spaces
#CORS_ORIGINS="https://$DLS_URL:$DLS_PORT"
# Lease expiration in days
LEASE_EXPIRE_DAYS=90
LEASE_RENEWAL_PERIOD=0.2
# Database location
## https://docs.sqlalchemy.org/en/14/core/engines.html
DATABASE=sqlite:////etc/fastapi-dls/db.sqlite
# UUIDs for identifying the instance
#SITE_KEY_XID="00000000-0000-0000-0000-000000000000"
#INSTANCE_REF="10000000-0000-0000-0000-000000000001"
#ALLOTMENT_REF="20000000-0000-0000-0000-000000000001"
# Site-wide signing keys
INSTANCE_KEY_RSA=/etc/fastapi-dls/instance.private.pem
INSTANCE_KEY_PUB=/etc/fastapi-dls/instance.public.pem

View File

@ -0,0 +1,25 @@
[Unit]
Description=Service for fastapi-dls
Documentation=https://git.collinwebdesigns.de/oscar.krause/fastapi-dls
After=network.target
[Service]
User=www-data
Group=www-data
AmbientCapabilities=CAP_NET_BIND_SERVICE
WorkingDirectory=/usr/share/fastapi-dls/app
EnvironmentFile=/etc/fastapi-dls/env
ExecStart=uvicorn main:app \
--env-file /etc/fastapi-dls/env \
--host $DLS_URL --port $DLS_PORT \
--app-dir /usr/share/fastapi-dls/app \
--ssl-keyfile /etc/fastapi-dls/webserver.key \
--ssl-certfile /etc/fastapi-dls/webserver.crt \
--proxy-headers
Restart=always
KillSignal=SIGQUIT
Type=simple
NotifyAccess=all
[Install]
WantedBy=multi-user.target

60
.DEBIAN/postinst Normal file
View File

@ -0,0 +1,60 @@
#!/bin/bash
WORKING_DIR=/usr/share/fastapi-dls
CONFIG_DIR=/etc/fastapi-dls
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
else
echo "> Create dls-instance keypair skipped! (exists)"
fi
while true; do
[ -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
[Yy]*)
echo "> Generating keypair ..."
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $CONFIG_DIR/webserver.key -out $CONFIG_DIR/webserver.crt
break
;;
[Nn]*) echo "> Generating keypair skipped! (exists)"; break ;;
*) echo "Please answer [y] or [n]." ;;
esac
done
if [ -f $CONFIG_DIR/webserver.key ]; then
echo "> Starting service ..."
systemctl start fastapi-dls.service
if [ -x "$(command -v curl)" ]; then
echo "> Testing API ..."
source $CONFIG_DIR/env
curl --insecure -X GET https://$DLS_URL:$DLS_PORT/-/health
else
echo "> Testing API failed, curl not available. Please test manually!"
fi
fi
chown -R www-data:www-data $CONFIG_DIR
chown -R www-data:www-data $WORKING_DIR
cat <<EOF
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# fastapi-dls is now installed. #
# #
# Service should be up and running. #
# Webservice is listen to https://localhost #
# #
# Configuration is stored in /etc/fastapi-dls/env. #
# #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
EOF

9
.DEBIAN/postrm Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# is removed automatically
#if [ "$1" = purge ] && [ -d /usr/share/fastapi-dls ]; then
# echo "> Removing app."
# rm -r /usr/share/fastapi-dls
#fi
echo -e "> Done."

3
.DEBIAN/prerm Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
echo -e "> Starting uninstallation of 'fastapi-dls'!"

View File

@ -0,0 +1,11 @@
# https://packages.debian.org/hu/
fastapi==0.92.0
uvicorn[standard]==0.17.6
python-jose[pycryptodome]==3.3.0
pycryptodome==3.11.0
python-dateutil==2.8.2
sqlalchemy==1.4.46
markdown==3.4.1
python-dotenv==0.21.0
jinja2==3.1.2
httpx==0.23.3

View File

@ -0,0 +1,10 @@
# https://packages.ubuntu.com
fastapi==0.101.0
uvicorn[standard]==0.27.1
python-jose[pycryptodome]==3.3.0
pycryptodome==3.20.0
python-dateutil==2.8.2
sqlalchemy==1.4.50
markdown==3.5.2
python-dotenv==1.0.1
jinja2==3.1.2

View File

@ -0,0 +1,10 @@
# https://packages.ubuntu.com
fastapi==0.110.3
uvicorn[standard]==0.30.3
python-jose[pycryptodome]==3.3.0
pycryptodome==3.20.0
python-dateutil==2.9.0
sqlalchemy==2.0.32
markdown==3.6
python-dotenv==1.0.1
jinja2==3.1.3

54
.PKGBUILD/PKGBUILD Normal file
View File

@ -0,0 +1,54 @@
# Maintainer: Oscar Krause <oscar.krause@collinwebdesigns.de>
# Contributor: samicrusader <hi@samicrusader.me>
pkgname=fastapi-dls
pkgver=1.1
pkgrel=1
pkgdesc='NVIDIA DLS server implementation with FastAPI'
arch=('any')
url='https://git.collinwebdesigns.de/oscar.krause/fastapi-dls'
license=('MIT')
depends=('python' 'python-jose' 'python-starlette' 'python-httpx' 'python-fastapi' 'python-dotenv' 'python-dateutil' 'python-sqlalchemy' 'python-pycryptodome' 'uvicorn' 'python-markdown' 'openssl')
provider=("$pkgname")
install="$pkgname.install"
backup=('etc/default/fastapi-dls')
source=("git+file://${CI_PROJECT_DIR}"
"$pkgname.default"
"$pkgname.service"
"$pkgname.tmpfiles")
sha256sums=('SKIP'
'fbd015449a30c0ae82733289a56eb98151dcfab66c91b37fe8e202e39f7a5edb'
'2719338541104c537453a65261c012dda58e1dbee99154cf4f33b526ee6ca22e'
'3dc60140c08122a8ec0e7fa7f0937eb8c1288058890ba09478420fc30ce9e30c')
pkgver() {
echo -e "VERSION=$VERSION\nCOMMIT=$CI_COMMIT_SHA" > $srcdir/$pkgname/version.env
source $srcdir/$pkgname/version.env
echo $VERSION
}
check() {
cd "$srcdir/$pkgname/test"
mkdir "$srcdir/$pkgname/app/cert"
openssl genrsa -out "$srcdir/$pkgname/app/cert/instance.private.pem" 2048
openssl rsa -in "$srcdir/$pkgname/app/cert/instance.private.pem" -outform PEM -pubout -out "$srcdir/$pkgname/app/cert/instance.public.pem"
python "$srcdir/$pkgname/test/main.py"
rm -rf "$srcdir/$pkgname/app/cert"
}
package() {
install -d "$pkgdir/usr/share/doc/$pkgname"
install -d "$pkgdir/var/lib/$pkgname/cert"
cp -r "$srcdir/$pkgname/doc"/* "$pkgdir/usr/share/doc/$pkgname/"
install -Dm644 "$srcdir/$pkgname/README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
install -Dm644 "$srcdir/$pkgname/version.env" "$pkgdir/usr/share/doc/$pkgname/version.env"
sed -i "s/README.md/\/usr\/share\/doc\/$pkgname\/README.md/g" "$srcdir/$pkgname/app/main.py"
sed -i "s/join(dirname(__file__), 'cert\//join('\/var\/lib\/$pkgname', 'cert\//g" "$srcdir/$pkgname/app/main.py"
install -Dm755 "$srcdir/$pkgname/app/main.py" "$pkgdir/opt/$pkgname/main.py"
install -Dm755 "$srcdir/$pkgname/app/orm.py" "$pkgdir/opt/$pkgname/orm.py"
install -Dm755 "$srcdir/$pkgname/app/util.py" "$pkgdir/opt/$pkgname/util.py"
install -Dm644 "$srcdir/$pkgname.default" "$pkgdir/etc/default/$pkgname"
install -Dm644 "$srcdir/$pkgname.service" "$pkgdir/usr/lib/systemd/system/$pkgname.service"
install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf"
}

View File

@ -0,0 +1,28 @@
# Toggle FastAPI debug mode
DEBUG=false
# Where the client can find the DLS server
## DLS_URL should be a hostname
LISTEN_IP="0.0.0.0"
DLS_URL="localhost.localdomain"
DLS_PORT=8443
CORS_ORIGINS="https://$DLS_URL:$DLS_PORT"
# Lease expiration in days
LEASE_EXPIRE_DAYS=90
# Database location
## https://docs.sqlalchemy.org/en/14/core/engines.html
DATABASE="sqlite:////var/lib/fastapi-dls/db.sqlite"
# UUIDs for identifying the instance
SITE_KEY_XID="<<sitekey>>"
INSTANCE_REF="<<instanceref>>"
# Site-wide signing keys
INSTANCE_KEY_RSA="/var/lib/fastapi-dls/instance.private.pem"
INSTANCE_KEY_PUB="/var/lib/fastapi-dls/instance.public.pem"
# TLS certificate
INSTANCE_SSL_CERT="/var/lib/fastapi-dls/cert/webserver.crt"
INSTANCE_SSL_KEY="/var/lib/fastapi-dls/cert/webserver.key"

View File

@ -0,0 +1,14 @@
post_install() {
sed -i "s/<<sitekey>>/$(uuidgen)/" /etc/default/fastapi-dls
sed -i "s/<<instanceref>>/$(uuidgen)/" /etc/default/fastapi-dls
echo 'The environment variables for this server can be edited at: /etc/default/fastapi-dls'
echo 'The server can be started with: systemctl start fastapi-dls.service'
echo
echo 'A valid HTTPS certificate needs to be installed to /var/lib/fastapi-dls/cert/webserver.{crt,key}'
echo 'A self-signed certificate can be generated with: openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /var/lib/fastapi-dls/cert/webserver.key -out /var/lib/fastapi-dls/cert/webserver.crt'
echo
echo 'The signing keys for your instance need to be generated as well. Generate them with these commands:'
echo 'openssl genrsa -out /var/lib/fastapi-dls/instance.private.pem 2048'
echo 'openssl rsa -in /var/lib/fastapi-dls/instance.private.pem -outform PEM -pubout -out /var/lib/fastapi-dls/instance.public.pem'
}

View File

@ -0,0 +1,16 @@
[Unit]
Description=FastAPI-DLS
Documentation=https://git.collinwebdesigns.de/oscar.krause/fastapi-dls
After=network.target
[Service]
Type=simple
AmbientCapabilities=CAP_NET_BIND_SERVICE
EnvironmentFile=/etc/default/fastapi-dls
ExecStart=/usr/bin/uvicorn main:app --proxy-headers --env-file=/etc/default/fastapi-dls --host=${LISTEN_IP} --port=${DLS_PORT} --app-dir=/opt/fastapi-dls --ssl-keyfile=${INSTANCE_SSL_KEY} --ssl-certfile=${INSTANCE_SSL_CERT}
Restart=on-abort
User=http
Group=http
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,2 @@
d /var/lib/fastapi-dls 0755 http http
d /var/lib/fastapi-dls/cert 0755 http http

48
.UNRAID/FastAPI-DLS.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0"?>
<Container version="2">
<Name>FastAPI-DLS</Name>
<Repository>collinwebdesigns/fastapi-dls:latest</Repository>
<Registry>https://hub.docker.com/r/collinwebdesigns/fastapi-dls</Registry>
<Network>br0</Network>
<MyIP></MyIP>
<Shell>sh</Shell>
<Privileged>false</Privileged>
<Support/>
<Project/>
<Overview>Source:&#xD;
https://git.collinwebdesigns.de/oscar.krause/fastapi-dls#docker&#xD;
&#xD;
Make sure you create these certificates before starting the container for the first time:&#xD;
```&#xD;
# Check https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/tree/main/#docker for more information:&#xD;
WORKING_DIR=/mnt/user/appdata/fastapi-dls/cert&#xD;
mkdir -p $WORKING_DIR&#xD;
cd $WORKING_DIR&#xD;
# create instance private and public key for singing JWT's&#xD;
openssl genrsa -out $WORKING_DIR/instance.private.pem 2048 &#xD;
openssl rsa -in $WORKING_DIR/instance.private.pem -outform PEM -pubout -out $WORKING_DIR/instance.public.pem&#xD;
# create ssl certificate for integrated webserver (uvicorn) - because clients rely on ssl&#xD;
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $WORKING_DIR/webserver.key -out $WORKING_DIR/webserver.crt&#xD;
```&#xD;
</Overview>
<Category/>
<WebUI>https://[IP]:[PORT:443]</WebUI>
<TemplateURL/>
<Icon>https://git.collinwebdesigns.de/uploads/-/system/project/avatar/106/png-transparent-nvidia-grid-logo-business-nvidia-electronics-text-trademark.png?width=64</Icon>
<ExtraParams>--restart always</ExtraParams>
<PostArgs/>
<CPUset/>
<DateInstalled>1679161568</DateInstalled>
<DonateText/>
<DonateLink/>
<Requires/>
<Config Name="HTTPS Port" Target="" Default="443" Mode="tcp" Description="Same as DLS Port below." Type="Port" Display="always-hide" Required="true" Mask="false">443</Config>
<Config Name="App Cert" Target="/app/cert" Default="/mnt/user/appdata/fastapi-dls/cert" Mode="rw" Description="[REQUIRED] Read the description above to make this folder. &#13;&#10;&#13;&#10;You do not need to change the path." Type="Path" Display="always-hide" Required="true" Mask="false">/mnt/user/appdata/fastapi-dls/cert</Config>
<Config Name="DLS Port" Target="DSL_PORT" Default="443" Mode="" Description="Choose port you want to use. Make sure to change the HTTPS port above to match it." Type="Variable" Display="always-hide" Required="true" Mask="false">443</Config>
<Config Name="App database" Target="/app/database" Default="/mnt/user/appdata/fastapi-dls/data" Mode="rw" Description="[REQUIRED] Read the description above to make this folder. &#13;&#10;&#13;&#10;You do not need to change the path." Type="Path" Display="always-hide" Required="true" Mask="false">/mnt/user/appdata/fastapi-dls/data</Config>
<Config Name="DSL IP" Target="DLS_URL" Default="localhost" Mode="" Description="Put your container's IP (or your host's IP if it's shared)." Type="Variable" Display="always-hide" Required="true" Mask="false"></Config>
<Config Name="Time Zone" Target="TZ" Default="" Mode="" Description="Format example: America/New_York. MUST MATCH YOUR CURRENT TIMEZONE AND THE GUEST VMS TIMEZONE! Otherwise you'll get into issues, read the guide above." Type="Variable" Display="always-hide" Required="true" Mask="false"></Config>
<Config Name="Database" Target="DATABASE" Default="sqlite:////app/database/db.sqlite" Mode="" Description="Set to sqlite:////app/database/db.sqlite" Type="Variable" Display="advanced-hide" Required="true" Mask="false">sqlite:////app/database/db.sqlite</Config>
<Config Name="Debug" Target="DEBUG" Default="true" Mode="" Description="true to enable debugging, false to disable them." Type="Variable" Display="advanced-hide" Required="false" Mask="false">true</Config>
<Config Name="Lease" Target="LEASE_EXPIRE_DAYS" Default="90" Mode="" Description="90 days is the maximum value." Type="Variable" Display="advanced" Required="false" Mask="false">90</Config>
</Container>

View File

@ -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

View File

@ -1,7 +1,9 @@
version: "2"
plugins:
bandit:
enabled: true
sonar-python:
enabled: true
pylint:
enabled: true
config:
tests_patterns:
- test/**

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ venv/
.idea/
app/*.sqlite*
app/cert/*.*
.pytest_cache

View File

@ -1,43 +1,403 @@
include:
- template: Jobs/Code-Quality.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Container-Scanning.gitlab-ci.yml
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
cache:
key: one-key-to-rule-them-all
build:
variables:
DOCKER_BUILDX_PLATFORM: "linux/amd64,linux/arm64"
build:docker:
image: docker:dind
interruptible: true
stage: build
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
changes:
- app/**/*
- Dockerfile
- requirements.txt
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
tags: [ docker ]
before_script:
- echo "COMMIT=${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
- 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}
- IMAGE=$CI_REGISTRY/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHA
- docker buildx build --progress=plain --platform $DOCKER_BUILDX_PLATFORM --build-arg VERSION=$CI_COMMIT_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:
dotenv: container_scanning.env
build:apt:
image: debian:bookworm-slim
interruptible: true
stage: build
rules:
- if: $CI_COMMIT_TAG
variables:
VERSION: $CI_COMMIT_REF_NAME
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
changes:
- app/**/*
- .DEBIAN/**/*
- .gitlab-ci.yml
variables:
VERSION: "0.0.1"
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
variables:
VERSION: "0.0.1"
before_script:
- echo -e "VERSION=$VERSION\nCOMMIT=$CI_COMMIT_SHA" > version.env
# install build dependencies
- apt-get update -qq && apt-get install -qq -y build-essential
# create build directory for .deb sources
- mkdir build
# copy install instructions
- cp -r .DEBIAN build/DEBIAN
- chmod -R 0775 build/DEBIAN
# copy app into "/usr/share/fastapi-dls" as "/usr/share/fastapi-dls/app" & copy README.md and version.env
- mkdir -p build/usr/share/fastapi-dls
- cp -r app build/usr/share/fastapi-dls
- cp README.md version.env build/usr/share/fastapi-dls
# create conf file
- mkdir -p build/etc/fastapi-dls
- cp .DEBIAN/env.default build/etc/fastapi-dls/env
# create service file
- mkdir -p build/etc/systemd/system
- cp .DEBIAN/fastapi-dls.service build/etc/systemd/system/fastapi-dls.service
# cd into "build/"
- cd build/
script:
# set version based on value in "$CI_COMMIT_REF_NAME"
- sed -i -E 's/(Version\:\s)0.0/\1'"$VERSION"'/g' DEBIAN/control
# build
- dpkg -b . build.deb
- dpkg -I build.deb
artifacts:
expire_in: 1 week
paths:
- build/build.deb
build:pacman:
image: archlinux:base-devel
interruptible: true
stage: build
rules:
- if: $CI_COMMIT_TAG
variables:
VERSION: $CI_COMMIT_REF_NAME
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
changes:
- app/**/*
- .PKGBUILD/**/*
- .gitlab-ci.yml
variables:
VERSION: "0.0.1"
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
variables:
VERSION: "0.0.1"
before_script:
#- echo -e "VERSION=$VERSION\nCOMMIT=$CI_COMMIT_SHA" > version.env
# install build dependencies
- pacman -Syu --noconfirm git
# create a build-user because "makepkg" don't like root user
- useradd --no-create-home --shell=/bin/false build && usermod -L build
- 'echo "build ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers'
- 'echo "root ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers'
- chown -R build:build .
# move .PKGBUILD contents to root directory
- mv .PKGBUILD/* .
script:
- pwd
# download dependencies
- source PKGBUILD && pacman -Syu --noconfirm --needed --asdeps "${makedepends[@]}" "${depends[@]}"
# build
- sudo --preserve-env -u build makepkg -s
artifacts:
expire_in: 1 week
paths:
- "*.pkg.tar.zst"
test:
image: python:3.12-slim-bookworm
stage: test
script:
- echo "Nothing to do ..."
deploy:
stage: deploy
interruptible: true
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
changes:
- app/**/*
- test/**/*
variables:
DATABASE: sqlite:///../app/db.sqlite
parallel:
matrix:
- REQUIREMENTS:
- 'requirements.txt'
# - '.DEBIAN/requirements-bookworm-12.txt'
# - '.DEBIAN/requirements-ubuntu-24.04.txt'
# - '.DEBIAN/requirements-ubuntu-24.10.txt'
before_script:
- echo "COMMIT=${CI_COMMIT_SHA}" >> version.env
- source version.env
- echo "Building docker image for commit ${COMMIT} with version ${VERSION}"
- apt-get update && apt-get install -y python3-dev python3-pip python3-venv gcc
- python3 -m venv venv
- source venv/bin/activate
- pip install --upgrade pip
- pip install -r $REQUIREMENTS
- pip install pytest httpx
- mkdir -p app/cert
- openssl genrsa -out app/cert/instance.private.pem 2048
- openssl rsa -in app/cert/instance.private.pem -outform PEM -pubout -out app/cert/instance.public.pem
- cd test
script:
- echo "GitLab-Registry"
- python -m pytest main.py --junitxml=report.xml
artifacts:
reports:
dotenv: version.env
junit: ['**/report.xml']
.test:apt:
stage: test
rules:
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
changes:
- app/**/*
- .DEBIAN/**/*
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
needs:
- job: build:apt
artifacts: true
variables:
DEBIAN_FRONTEND: noninteractive
before_script:
- apt-get update -qq && apt-get install -qq -y jq curl
script:
# test installation
- apt-get install -q -y ./build/build.deb --fix-missing
- openssl req -x509 -newkey rsa:2048 -nodes -out /etc/fastapi-dls/webserver.crt -keyout /etc/fastapi-dls/webserver.key -days 7 -subj "/C=DE/O=GitLab-CI/OU=Test/CN=localhost"
# copy example config from GitLab-CI-Variables
#- cat ${EXAMPLE_CONFIG} > /etc/fastapi-dls/env
# start service in background
- cd /usr/share/fastapi-dls/app
- uvicorn main:app
--host 127.0.0.1 --port 443
--app-dir /usr/share/fastapi-dls/app
--ssl-keyfile /etc/fastapi-dls/webserver.key
--ssl-certfile /etc/fastapi-dls/webserver.crt
--proxy-headers &
- FASTAPI_DLS_PID=$!
- echo "Started service with pid $FASTAPI_DLS_PID"
- cat /etc/fastapi-dls/env
# testing service
- if [ "`curl --insecure -s https://127.0.0.1/-/health | jq .status`" != "up" ]; then echo "Success"; else "Error"; fi
# cleanup
- kill $FASTAPI_DLS_PID
- apt-get purge -qq -y fastapi-dls
- apt-get autoremove -qq -y && apt-get clean -qq
test:apt:
extends: .test:apt
image: $IMAGE
parallel:
matrix:
- IMAGE:
- debian:bookworm-slim # EOL: June 06, 2026
- ubuntu:24.04 # EOL: April 2036
- ubuntu:24.10
test:pacman:archlinux:
image: archlinux:base
rules:
- if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
changes:
- app/**/*
- .PKGBUILD/**/*
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
needs:
- job: build:pacman
artifacts: true
script:
- pacman -Sy
- pacman -U --noconfirm *.pkg.tar.zst
code_quality:
variables:
SOURCE_CODE: app
rules:
- if: $CODE_QUALITY_DISABLED
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
secret_detection:
rules:
- 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:
- if: $SAST_DISABLED
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
test_coverage:
# extends: test
image: python:3.12-slim-bookworm
allow_failure: true
stage: test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
variables:
DATABASE: sqlite:///../app/db.sqlite
before_script:
- apt-get update && apt-get install -y python3-dev gcc
- pip install -r requirements.txt
- pip install pytest httpx
- mkdir -p app/cert
- openssl genrsa -out app/cert/instance.private.pem 2048
- openssl rsa -in app/cert/instance.private.pem -outform PEM -pubout -out app/cert/instance.public.pem
- cd test
script:
- pip install pytest pytest-cov
- coverage run -m pytest main.py
- coverage report
- coverage xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: '**/coverage.xml'
container_scanning:
dependencies: [ build:docker ]
rules:
- if: $CONTAINER_SCANNING_DISABLED
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
gemnasium-python-dependency_scanning:
rules:
- if: $DEPENDENCY_SCANNING_DISABLED
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
.deploy:
rules:
- if: $CI_COMMIT_TAG
deploy:docker:
extends: .deploy
image: docker:dind
stage: deploy
tags: [ docker ]
before_script:
- echo "Building docker image for commit $CI_COMMIT_SHA with version $CI_COMMIT_REF_NAME"
- docker buildx inspect
- docker buildx create --use
script:
- 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}:${VERSION}
- docker build . --tag ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_BUILD_REF_NAME}:latest
- docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_BUILD_REF_NAME}:${VERSION}
- docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_BUILD_REF_NAME}:latest
- echo "Docker-Hub"
- IMAGE=$CI_REGISTRY/$CI_PROJECT_PATH
- docker buildx build --progress=plain --platform $DOCKER_BUILDX_PLATFORM --build-arg VERSION=$CI_COMMIT_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:$CI_COMMIT_REF_NAME --push .
- docker buildx build --progress=plain --platform $DOCKER_BUILDX_PLATFORM --build-arg VERSION=$CI_COMMIT_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:latest --push .
- echo "========== Docker-Hub =========="
- docker login -u $PUBLIC_REGISTRY_USER -p $PUBLIC_REGISTRY_TOKEN
- docker build . --tag $PUBLIC_REGISTRY_USER/${CI_PROJECT_NAME}:${VERSION}
- docker build . --tag $PUBLIC_REGISTRY_USER/${CI_PROJECT_NAME}:latest
- docker push $PUBLIC_REGISTRY_USER/${CI_PROJECT_NAME}:${VERSION}
- docker push $PUBLIC_REGISTRY_USER/${CI_PROJECT_NAME}:latest
- IMAGE=$PUBLIC_REGISTRY_USER/$CI_PROJECT_NAME
- docker buildx build --progress=plain --platform $DOCKER_BUILDX_PLATFORM --build-arg VERSION=$CI_COMMIT_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:$CI_COMMIT_REF_NAME --push .
- docker buildx build --progress=plain --platform $DOCKER_BUILDX_PLATFORM --build-arg VERSION=$CI_COMMIT_REF_NAME --build-arg COMMIT=$CI_COMMIT_SHA --tag $IMAGE:latest --push .
deploy:apt:
# doc: https://git.collinwebdesigns.de/help/user/packages/debian_repository/index.md#install-a-package
extends: .deploy
image: debian:bookworm-slim
stage: deploy
needs:
- job: build:apt
artifacts: true
before_script:
- apt-get update -qq && apt-get install -qq -y curl lsb-release
# create distribution initial
- CODENAME=`lsb_release -cs`
# create repo if not exists
- 'if [ "`curl -s -o /dev/null -w "%{http_code}" --header "JOB-TOKEN: $CI_JOB_TOKEN" -s ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/debian_distributions/${CODENAME}/key.asc`" != "200" ]; then curl --request POST --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/debian_distributions?codename=${CODENAME}"; fi'
script:
# Naming format: <name>_<version>-<release>_<arch>.deb
# Version is the version number of the app being packaged
# Release number is the version number of the *packaging* itself.
# The release number might increment if the package maintainer
# updated the packaging, while the version number of the application
# being packaged did not change.
- BUILD_NAME=build/build.deb # inherited by build-stage
- PACKAGE_NAME=`dpkg -I ${BUILD_NAME} | grep "Package:" | awk '{ print $2 }'`
- PACKAGE_VERSION=`dpkg -I ${BUILD_NAME} | grep "Version:" | awk '{ print $2 }'`
- PACKAGE_ARCH=amd64
#- EXPORT_NAME="${PACKAGE_NAME}_${PACKAGE_VERSION}-0_${PACKAGE_ARCH}.deb"
- EXPORT_NAME="${PACKAGE_NAME}_${PACKAGE_VERSION}_${PACKAGE_ARCH}.deb"
- mv ${BUILD_NAME} ${EXPORT_NAME}
- 'echo "PACKAGE_NAME: ${PACKAGE_NAME}"'
- 'echo "PACKAGE_VERSION: ${PACKAGE_VERSION}"'
- 'echo "PACKAGE_ARCH: ${PACKAGE_ARCH}"'
- 'echo "EXPORT_NAME: ${EXPORT_NAME}"'
# https://docs.gitlab.com/14.3/ee/user/packages/debian_repository/index.html
- URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/debian/${EXPORT_NAME}"
- 'echo "URL: ${URL}"'
#- 'curl --request PUT --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${EXPORT_NAME} ${URL}'
# using generic-package-registry until debian-registry is GA
# https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-generic-package-by-using-cicd
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${EXPORT_NAME} "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}/${EXPORT_NAME}"'
deploy:pacman:
extends: .deploy
image: archlinux:base-devel
stage: deploy
needs:
- job: build:pacman
artifacts: true
script:
- source .PKGBUILD/PKGBUILD
# fastapi-dls-1.0-1-any.pkg.tar.zst
- BUILD_NAME=${pkgname}-${CI_COMMIT_REF_NAME}-${pkgrel}-any.pkg.tar.zst
- PACKAGE_NAME=${pkgname}
- PACKAGE_VERSION=${CI_COMMIT_REF_NAME}
- PACKAGE_ARCH=any
- EXPORT_NAME=${BUILD_NAME}
- 'echo "PACKAGE_NAME: ${PACKAGE_NAME}"'
- 'echo "PACKAGE_VERSION: ${PACKAGE_VERSION}"'
- 'echo "PACKAGE_ARCH: ${PACKAGE_ARCH}"'
- 'echo "EXPORT_NAME: ${EXPORT_NAME}"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${EXPORT_NAME} "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}/${EXPORT_NAME}"'
release:
image: registry.gitlab.com/gitlab-org/release-cli:latest
stage: .post
needs: [ test ]
rules:
- if: $CI_COMMIT_TAG
script:
- echo "Running release-job for $CI_COMMIT_TAG"
release:
name: $CI_PROJECT_TITLE $CI_COMMIT_TAG
description: Release of $CI_PROJECT_TITLE version $CI_COMMIT_TAG
tag_name: $CI_COMMIT_TAG
ref: $CI_COMMIT_SHA
assets:
links:
- name: 'Package Registry'
url: 'https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/packages'
- name: 'Container Registry'
url: 'https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/container_registry/40'

2
CODEOWNERS Executable file
View File

@ -0,0 +1,2 @@
* @oscar.krause
.PKGBUILD/ @samicrusader

View File

@ -1,18 +1,21 @@
FROM python:3.10-alpine
FROM python:3.12-alpine
ARG VERSION
ARG COMMIT=""
RUN echo -e "VERSION=$VERSION\nCOMMIT=$COMMIT" > /version.env
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 curl postgresql postgresql-dev mariadb-connector-c-dev sqlite-dev \
&& apk add --no-cache --virtual build-deps gcc g++ python3-dev musl-dev pkgconfig \
&& apk add --no-cache curl postgresql postgresql-dev mariadb-dev sqlite-dev \
&& pip install --no-cache-dir --upgrade uvicorn \
&& pip install --no-cache-dir psycopg2==2.9.5 mysqlclient==2.1.1 pysqlite3==0.5.0 \
&& pip install --no-cache-dir psycopg2==2.9.10 mysqlclient==2.2.7 pysqlite3==0.5.4 \
&& pip install --no-cache-dir -r /tmp/requirements.txt \
&& 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/status || exit 1
HEALTHCHECK --start-period=30s --interval=10s --timeout=5s --retries=3 CMD curl --insecure --fail https://localhost/-/health || 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"]

17
FAQ.md Normal file
View File

@ -0,0 +1,17 @@
# FAQ
## `Failed to acquire license from <ip> (Info: <license> - Error: The allowed time to process response has expired)`
- Did your timezone settings are correct on fastapi-dls **and your guest**?
- Did you download the client-token more than an hour ago?
Please download a new client-token. The guest have to register within an hour after client-token was created.
## `jose.exceptions.JWTError: Signature verification failed.`
- Did you recreated `instance.public.pem` / `instance.private.pem`?
Then you have to download a **new** client-token on each of your guests.

612
README.md
View File

@ -2,47 +2,60 @@
Minimal Delegated License Service (DLS).
> Compatibility tested with official NLS 2.0.1, 2.1.0, 3.1.0, 3.3.1, 3.4.0. For Driver compatibility
see [compatibility matrix](#vgpu-software-compatibility-matrix).
Drivers are only supported until **17.x releases**.
This service can be used without internet connection.
Only the clients need a connection to this service on configured port.
## Endpoints
**Official Links**
### `GET /`
* 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`)
HTML rendered README.md.
*All other repositories are forks! (which is no bad - just for information and bug reports)*
### `GET /status`
[Releases & Release Notes](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/releases)
Status endpoint, used for *healthcheck*. Shows also current version and commit hash.
**Further Reading**
### `GET /docs`
* [NVIDIA vGPU Guide](https://gitlab.com/polloloco/vgpu-proxmox) - This document serves as a guide to install NVIDIA vGPU host drivers on the latest Proxmox VE version
* [vgpu_unlock](https://github.com/DualCoder/vgpu_unlock) - Unlock vGPU functionality for consumer-grade Nvidia GPUs.
* [vGPU_Unlock Wiki](https://docs.google.com/document/d/1pzrWJ9h-zANCtyqRgS7Vzla0Y8Ea2-5z2HEi4X75d2Q) - Guide for `vgpu_unlock`
* [Proxmox 8 vGPU in VMs and LXC Containers](https://medium.com/@dionisievldulrincz/proxmox-8-vgpu-in-vms-and-lxc-containers-4146400207a3) - Install *Merged Drivers* for using in Proxmox VMs and LXCs
* [Proxmox All-In-One Installer Script](https://wvthoog.nl/proxmox-vgpu-v3/) - Also known as `proxmox-installer.sh`
OpenAPI specifications rendered from `GET /openapi.json`.
---
### `GET /-/origins`
[TOC]
List registered origins.
# Setup (Service)
### `GET /-/leases`
**System requirements**
List current leases.
- 256mb ram
- 4gb hdd
- *maybe IPv6 must be disabled*
### `GET /client-token`
Tested with Ubuntu 22.10 (EOL!) (from Proxmox templates), actually its consuming 100mb ram and 750mb hdd.
Generate client token, (see [installation](#installation)).
**Prepare your system**
### Others
- Make sure your timezone is set correct on you fastapi-dls server and your client
There are some more internal api endpoints for handling authentication and lease process.
# Setup
This guide does not show how to install vGPU host drivers! Look at the official documentation packed with the driver
releases.
## Docker
Docker-Images are available here:
Docker-Images are available here for Intel (x86), AMD (amd64) and ARM (arm64):
- [Docker-Hub](https://hub.docker.com/repository/docker/collinwebdesigns/fastapi-dls): `collinwebdesigns/fastapi-dls:latest`
- GitLab-Registry: `registry.git.collinwebdesigns.de/oscar.krause/fastapi-dls/main:latest`
- [GitLab-Registry](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/container_registry): `registry.git.collinwebdesigns.de/oscar.krause/fastapi-dls:latest`
The images include database drivers for `postgres`, `mariadb` and `sqlite`.
**Run this on the Docker-Host**
@ -59,6 +72,8 @@ openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $WORKING_DIR/webse
**Start container**
To test if everything is set up properly you can start container as following:
```shell
docker volume create dls-db
docker run -e DLS_URL=`hostname -i` -e DLS_PORT=443 -p 443:443 -v $WORKING_DIR:/app/cert -v dls-db:/app/database collinwebdesigns/fastapi-dls:latest
@ -66,14 +81,20 @@ docker run -e DLS_URL=`hostname -i` -e DLS_PORT=443 -p 443:443 -v $WORKING_DIR:/
**Docker-Compose / Deploy stack**
See [`examples`](examples) directory for more advanced examples (with reverse proxy usage).
> Adjust *REQUIRED* variables as needed
```yaml
version: '3.9'
x-dls-variables: &dls-variables
DLS_URL: localhost # REQUIRED
TZ: Europe/Berlin # REQUIRED, set your timezone correctly on fastapi-dls AND YOUR CLIENTS !!!
DLS_URL: localhost # REQUIRED, change to your ip or hostname
DLS_PORT: 443
LEASE_EXPIRE_DAYS: 90
LEASE_EXPIRE_DAYS: 90 # 90 days is maximum
DATABASE: sqlite:////app/database/db.sqlite
DEBUG: false
services:
dls:
@ -86,14 +107,22 @@ services:
volumes:
- /opt/docker/fastapi-dls/cert:/app/cert
- dls-db:/app/database
logging: # optional, for those who do not need logs
driver: "json-file"
options:
max-file: 5
max-size: 10m
volumes:
dls-db:
```
## Debian
## Debian / Ubuntu / macOS (manual method using `git clone` and python virtual environment)
Tested on `Debian 11 (bullseye)`, Ubuntu may also work.
Tested on `Debian 11 (bullseye)`, `Debian 12 (bookworm)` and `macOS Ventura (13.6)`, Ubuntu may also work.
**Please note that setup on macOS differs from Debian based systems.**
**Make sure you are logged in as root.**
**Install requirements**
@ -112,42 +141,47 @@ python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
deactivate
chown -R www-data:www-data $WORKING_DIR
```
**Create keypair and webserver certificate**
```shell
WORKING_DIR=/opt/fastapi-dls/app/cert
mkdir $WORKING_DIR
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
chown -R www-data:www-data $WORKING_DIR
```
**Test Service**
This is only to test whether the service starts successfully.
```shell
cd /opt/fastapi-dls/app
/opt/fastapi-dls/venv/bin/uvicorn main:app \
--host 127.0.0.1 --port 443 \
--app-dir /opt/fastapi-dls/app \
--ssl-keyfile /opt/fastapi-dls/app/cert/webserver.key \
--ssl-certfile /opt/fastapi-dls/app/cert/webserver.crt \
--proxy-headers
sudo -u www-data /opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app
# or
su - www-data -c "/opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app"
```
**Create config file**
> Adjust `DLS_URL` as needed (accessing from LAN won't work with 127.0.0.1)
```shell
cat <<EOF > /etc/fastapi-dls.env
mkdir /etc/fastapi-dls
cat <<EOF >/etc/fastapi-dls/env
DLS_URL=127.0.0.1
DLS_PORT=443
LEASE_EXPIRE_DAYS=90
DATABASE=sqlite:////opt/fastapi-dls/app/db.sqlite
EOF
EOF
```
**Create service**
@ -161,57 +195,400 @@ After=network.target
[Service]
User=www-data
Group=www-data
AmbientCapabilities=CAP_NET_BIND_SERVICE
WorkingDirectory=/opt/fastapi-dls/app
ExecStart=/opt/fastapi-dls/venv/bin/uvicorn \
--host $DLS_URL --port $DLS_PORT \
--app-dir /opt/fastapi-dls/app \
--ssl-keyfile /opt/fastapi-dls/app/cert/webserver.key \
--ssl-certfile /opt/fastapi-dls/app/cert/webserver.crt \
EnvironmentFile=/etc/fastapi-dls/env
ExecStart=/opt/fastapi-dls/venv/bin/uvicorn main:app \\
--env-file /etc/fastapi-dls/env \\
--host \$DLS_URL --port \$DLS_PORT \\
--app-dir /opt/fastapi-dls/app \\
--ssl-keyfile /opt/fastapi-dls/app/cert/webserver.key \\
--ssl-certfile /opt/fastapi-dls/app/cert/webserver.crt \\
--proxy-headers
EnvironmentFile=/etc/fastapi-dls.env
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
Type=simple
NotifyAccess=all
[Install]
WantedBy=multi-user.target
EOF
```
Now you have to run `systemctl daemon-reload`. After that you can start service
with `systemctl start fastapi-dls.service`.
with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`.
## openSUSE Leap (manual method using `git clone` and python virtual environment)
Tested on `openSUSE Leap 15.4`, openSUSE Tumbleweed may also work.
**Install requirements**
```shell
zypper in -y python310 python3-virtualenv python3-pip
```
**Install FastAPI-DLS**
```shell
BASE_DIR=/opt/fastapi-dls
SERVICE_USER=dls
mkdir -p ${BASE_DIR}
cd ${BASE_DIR}
git clone https://git.collinwebdesigns.de/oscar.krause/fastapi-dls .
python3.10 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
deactivate
useradd -r ${SERVICE_USER} -M -d /opt/fastapi-dls
chown -R ${SERVICE_USER} ${BASE_DIR}
```
**Create keypair and webserver certificate**
```shell
CERT_DIR=${BASE_DIR}/app/cert
SERVICE_USER=dls
mkdir ${CERT_DIR}
cd ${CERT_DIR}
# create instance private and public key for singing JWT's
openssl genrsa -out ${CERT_DIR}/instance.private.pem 2048
openssl rsa -in ${CERT_DIR}/instance.private.pem -outform PEM -pubout -out ${CERT_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 ${CERT_DIR}/webserver.key -out ${CERT_DIR}/webserver.crt
chown -R ${SERVICE_USER} ${CERT_DIR}
```
**Test Service**
This is only to test whether the service starts successfully.
```shell
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"
```
**Create config file**
> Adjust `DLS_URL` as needed (accessing from LAN won't work with 127.0.0.1)
```shell
BASE_DIR=/opt/fastapi-dls
cat <<EOF >/etc/fastapi-dls/env
DLS_URL=127.0.0.1
DLS_PORT=443
LEASE_EXPIRE_DAYS=90
DATABASE=sqlite:///${BASE_DIR}/app/db.sqlite
EOF
```
**Create service**
```shell
BASE_DIR=/opt/fastapi-dls
SERVICE_USER=dls
cat <<EOF >/etc/systemd/system/fastapi-dls.service
[Unit]
Description=Service for fastapi-dls vGPU licensing service
After=network.target
[Service]
User=${SERVICE_USER}
AmbientCapabilities=CAP_NET_BIND_SERVICE
WorkingDirectory=${BASE_DIR}/app
EnvironmentFile=/etc/fastapi-dls/env
ExecStart=${BASE_DIR}/venv/bin/uvicorn main:app \\
--env-file /etc/fastapi-dls/env \\
--host \$DLS_URL --port \$DLS_PORT \\
--app-dir ${BASE_DIR}/app \\
--ssl-keyfile ${BASE_DIR}/app/cert/webserver.key \\
--ssl-certfile ${BASE_DIR}/app/cert/webserver.crt \\
--proxy-headers
Restart=always
KillSignal=SIGQUIT
Type=simple
NotifyAccess=all
[Install]
WantedBy=multi-user.target
EOF
```
Now you have to run `systemctl daemon-reload`. After that you can start service
with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`.
## Debian / Ubuntu (using `dpkg` / `apt`)
Packages are available here:
- [GitLab-Registry](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/packages)
Successful tested with:
- **Debian 12 (Bookworm)** (EOL: June 06, 2026)
- *Ubuntu 22.10 (Kinetic Kudu)* (EOL: July 20, 2023)
- *Ubuntu 23.04 (Lunar Lobster)* (EOL: January 2024)
- *Ubuntu 23.10 (Mantic Minotaur)* (EOL: July 2024)
- **Ubuntu 24.04 (Noble Numbat)** (EOL: April 2036)
- *Ubuntu 24.10 (Oracular Oriole)* (EOL: tba.)
Not working with:
- Debian 11 (Bullseye) and lower (missing `python-jose` dependency)
- Ubuntu 22.04 (Jammy Jellyfish) (not supported as for 15.01.2023 due to [fastapi - uvicorn version missmatch](https://bugs.launchpad.net/ubuntu/+source/fastapi/+bug/1970557))
**Run this on your server instance**
First go to [GitLab-Registry](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/packages) and select your
version. Then you have to copy the download link of the `fastapi-dls_X.Y.Z_amd64.deb` asset.
```shell
apt-get update
FILENAME=/opt/fastapi-dls.deb
wget -O $FILENAME <download-url>
dpkg -i $FILENAME
apt-get install -f --fix-missing
```
Start with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`.
Now you have to edit `/etc/fastapi-dls/env` as needed.
## ArchLinux (using `pacman`)
**Shout out to `samicrusader` who created build file for ArchLinux!**
Packages are available here:
- [GitLab-Registry](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/packages)
```shell
pacman -Sy
FILENAME=/opt/fastapi-dls.pkg.tar.zst
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`.
Now you have to edit `/etc/default/fastapi-dls` as needed.
## 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.
## NixOS
Tanks to [@mrzenc](https://github.com/mrzenc) for [fastapi-dls-nixos](https://github.com/mrzenc/fastapi-dls-nixos).
## Let's Encrypt Certificate (optional)
If you're using installation via docker, you can use `traefik`. Please refer to their documentation.
Note that port 80 must be accessible, and you have to install `socat` if you're using `standalone` mode.
```shell
acme.sh --issue -d example.com \
--cert-file /etc/fastapi-dls/webserver.donotuse.crt \
--key-file /etc/fastapi-dls/webserver.key \
--fullchain-file /etc/fastapi-dls/webserver.crt \
--reloadcmd "systemctl restart fastapi-dls.service"
```
After first success you have to replace `--issue` with `--renew`.
# Configuration
| Variable | Default | Usage |
|---------------------|-----------------------|---------------------------------------------------------------------------------------|
| `DEBUG` | `false` | Toggles `fastapi` debug mode |
| `DLS_URL` | `localhost` | Used in client-token to tell guest driver where dls instance is reachable |
| `DLS_PORT` | `443` | Used in client-token to tell guest driver where dls instance is reachable |
| `LEASE_EXPIRE_DAYS` | `90` | Lease time in days |
| `DATABASE` | `sqlite:///db.sqlite` | See [official dataset docs](https://dataset.readthedocs.io/en/latest/quickstart.html) |
| `CORS_ORIGINS` | `https://{DLS_URL}` | Sets `Access-Control-Allow-Origin` header (comma separated string) |
| Variable | Default | Usage |
|--------------------------|----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
| `DEBUG` | `false` | Toggles `fastapi` debug mode |
| `DLS_URL` | `localhost` | Used in client-token to tell guest driver where dls instance is reachable |
| `DLS_PORT` | `443` | Used in client-token to tell guest driver where dls instance is reachable |
| `TOKEN_EXPIRE_DAYS` | `1` | Client auth-token validity (used for authenticate client against api, **not `.tok` file!**) |
| `LEASE_EXPIRE_DAYS` | `90` | Lease time in days |
| `LEASE_RENEWAL_PERIOD` | `0.15` | The percentage of the lease period that must elapse before a licensed client can renew a license \*1 |
| `DATABASE` | `sqlite:///db.sqlite` | See [official SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/core/engines.html) |
| `CORS_ORIGINS` | `https://{DLS_URL}` | Sets `Access-Control-Allow-Origin` header (comma separated string) \*2 |
| `SITE_KEY_XID` | `00000000-0000-0000-0000-000000000000` | Site identification uuid |
| `INSTANCE_REF` | `10000000-0000-0000-0000-000000000001` | Instance identification uuid |
| `ALLOTMENT_REF` | `20000000-0000-0000-0000-000000000001` | Allotment identification uuid |
| `INSTANCE_KEY_RSA` | `<app-dir>/cert/instance.private.pem` | Site-wide private RSA key for singing JWTs \*3 |
| `INSTANCE_KEY_PUB` | `<app-dir>/cert/instance.public.pem` | Site-wide public key \*3 |
# Installation
\*1 For example, if the lease period is one day and the renewal period is 20%, the client attempts to renew its license
every 4.8 hours. If network connectivity is lost, the loss of connectivity is detected during license renewal and the
client has 19.2 hours in which to re-establish connectivity before its license expires.
\*2 Always use `https`, since guest-drivers only support secure connections!
\*3 If you recreate your instance keys you need to **recreate client-token for each guest**!
# Setup (Client)
**The token file has to be copied! It's not enough to C&P file contents, because there can be special characters.**
This guide does not show how to install vGPU guest drivers! Look at the official documentation packed with the driver
releases.
## 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 -X GET https://<dls-hostname-or-ip>/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token.tok
service nvidia-gridd restart
```
Check licensing status:
```shell
nvidia-smi -q | grep "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).
## Windows
Download file and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`.
Now restart `NvContainerLocalSystem` service.
**Power-Shell** (run as administrator!)
# Troubleshoot
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
& 'nvidia-smi' -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).
## 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
# API Endpoints
<details>
<summary>show</summary>
**`GET /`**
Redirect to `/-/readme`.
**`GET /-/health`**
Status endpoint, used for *healthcheck*.
**`GET /-/config`**
Shows current runtime environment variables and their values.
**`GET /-/readme`**
HTML rendered README.md.
**`GET /-/manage`**
Shows a very basic UI to delete origins or leases.
**`GET /-/origins?leases=false`**
List registered origins.
| Query Parameter | Default | Usage |
|-----------------|---------|--------------------------------------|
| `leases` | `false` | Include referenced leases per origin |
**`DELETE /-/origins`**
Deletes all origins and their leases.
**`GET /-/leases?origin=false`**
List current leases.
| Query Parameter | Default | Usage |
|-----------------|---------|-------------------------------------|
| `origin` | `false` | Include referenced origin per lease |
**`DELETE /-/lease/{lease_ref}`**
Deletes an lease.
**`GET /-/client-token`**
Generate client token, (see [installation](#installation)).
**Others**
There are many other internal api endpoints for handling authentication and lease process.
</details>
# Troubleshoot / Debug
**Please make sure that fastapi-dls and your guests are on the same timezone!**
Maybe you have to disable IPv6 on the machine you are running FastAPI-DLS.
## Docker
Logs are available with `docker logs <container>`. To get the correct container-id use `docker container ls` or `docker ps`.
## Linux
@ -225,10 +602,44 @@ Logs are available in `C:\Users\Public\Documents\Nvidia\LoggingLog.NVDisplay.Con
## Linux
Currently, there are no known issues.
### Invalid HTTP request
This error message: `uvicorn.error:Invalid HTTP request received.` can be ignored.
- Ref. https://github.com/encode/uvicorn/issues/441
<details>
<summary>Log example</summary>
```
WARNING:uvicorn.error:Invalid HTTP request received.
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/uvicorn/protocols/http/h11_impl.py", line 129, in handle_events
event = self.conn.next_event()
File "/usr/lib/python3/dist-packages/h11/_connection.py", line 485, in next_event
exc._reraise_as_remote_protocol_error()
File "/usr/lib/python3/dist-packages/h11/_util.py", line 77, in _reraise_as_remote_protocol_error
raise self
File "/usr/lib/python3/dist-packages/h11/_connection.py", line 467, in next_event
event = self._extract_next_receive_event()
File "/usr/lib/python3/dist-packages/h11/_connection.py", line 409, in _extract_next_receive_event
event = self._reader(self._receive_buffer)
File "/usr/lib/python3/dist-packages/h11/_readers.py", line 84, in maybe_read_from_IDLE_client
raise LocalProtocolError("no request line received")
h11._util.RemoteProtocolError: no request line received
```
</details>
## Windows
### Required cipher on Windows Guests (e.g. managed by domain controller with GPO)
It is required to enable `SHA1` (`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P521`)
in [windows cipher suite](https://learn.microsoft.com/en-us/windows-server/security/tls/manage-tls).
### Multiple Display Container LS Instances
On Windows on some machines there are running two or more instances of `NVIDIA Display Container LS`. This causes a
problem on licensing flow. As you can see in the logs below, there are two lines with `NLS initialized`, each prefixed
with `<1>` and `<2>`. So it is possible, that *daemon 1* fetches a valid license through dls-service, and *daemon 2*
@ -236,7 +647,7 @@ only
gets a valid local license.
<details>
<summary>Log</summary>
<summary>Log example</summary>
**Display-Container-LS**
@ -286,3 +697,90 @@ Dec 20 17:53:34 ubuntu-grid-server nvidia-gridd[10354]: License acquired success
```
</details>
### Error on releasing leases on shutdown (can be ignored and/or fixed with reverse proxy)
The driver wants to release current leases on shutting down windows. This endpoint needs to be a http endpoint.
The error message can safely be ignored (since we have no license limitation :P) and looks like this:
<details>
<summary>Log example</summary>
```
<1>:NLS initialized
<1>:License acquired successfully. (Info: 192.168.178.110, NVIDIA RTX Virtual Workstation; Expiry: 2023-3-30 23:0:22 GMT)
<0>:Failed to return license to 192.168.178.110 (Error: Generic network communication failure)
<0>:End Logging
```
#### log with nginx as reverse proxy (see [docker-compose-http-and-https.yml](examples/docker-compose-http-and-https.yml))
```
<1>:NLS initialized
<2>:NLS initialized
<1>:Valid GRID license not found. GPU features and performance will be fully degraded. To enable full functionality please configure licensing details.
<1>:License acquired successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2023-1-4 16:48:20 GMT)
<2>:Valid GRID license not found. GPU features and performance will be fully degraded. To enable full functionality please configure licensing details.
<2>:License acquired successfully from local trusted store. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2023-1-4 16:48:20 GMT)
<2>:End Logging
<1>:End Logging
<0>:License returned successfully. (Info: 192.168.178.33)
<0>:End Logging
```
</details>
# vGPU Software Compatibility Matrix
**18.x Drivers are not supported on FastAPI-DLS Versions < 1.6.0**
<details>
<summary>Show Table</summary>
Successfully tested with this package versions.
| vGPU Suftware | Driver Branch | Linux vGPU Manager | Linux Driver | Windows Driver | Release Date | EOL Date |
|:-------------:|:-------------:|--------------------|--------------|----------------|--------------:|--------------:|
| `17.5` | R550 | `550.144.02` | `550.144.03` | `553.62` | January 2025 | June 2025 |
| `17.4` | R550 | `550.127.06` | `550.127.05` | `553.24` | October 2024 | |
| `17.3` | R550 | `550.90.05` | `550.90.07` | `552.74` | July 2024 | |
| `17.2` | R550 | `550.90.05` | `550.90.07` | `552.55` | June 2024 | |
| `17.1` | R550 | `550.54.16` | `550.54.15` | `551.78` | March 2024 | |
| `17.0` | R550 | `550.54.10` | `550.54.14` | `551.61` | February 2024 | |
| `16.9` | R535 | `535.230.02` | `535.216.01` | `539.19` | October 2024 | July 2026 |
| `16.8` | R535 | `535.216.01` | `535.216.01` | `538.95` | October 2024 | |
| `16.7` | R535 | `535.183.04` | `535.183.06` | `538.78` | July 2024 | |
| `16.6` | R535 | `535.183.04` | `535.183.01` | `538.67` | June 2024 | |
| `16.5` | R535 | `535.161.05` | `535.161.08` | `538.46` | February 2024 | |
| `16.4` | R535 | `535.161.05` | `535.161.07` | `538.33` | February 2024 | |
| `16.3` | R535 | `535.154.02` | `535.154.05` | `538.15` | January 2024 | |
| `16.2` | R535 | `535.129.03` | `535.129.03` | `537.70` | October 2023 | |
| `16.1` | R535 | `535.104.06` | `535.104.05` | `537.13` | August 2023 | |
| `16.0` | R535 | `535.54.06` | `535.54.03` | `536.22` | July 2023 | |
| `15.4` | R525 | `525.147.01` | `525.147.05` | `529.19` | June 2023 | December 2023 |
| `14.4` | R510 | `510.108.03` | `510.108.03` | `514.08` | December 2022 | February 2023 |
</details>
- https://docs.nvidia.com/grid/index.html
- https://docs.nvidia.com/grid/gpus-supported-by-vgpu.html
*To get the latest drivers, visit Nvidia or search in Discord-Channel `GPU Unlocking` (Server-ID: `829786927829745685`)
on channel `licensing`
# Credits
Thanks to vGPU community and all who uses this project and report bugs.
Special thanks to:
- @samicrusader who created build file for **ArchLinux**
- @cyrus who wrote the section for **openSUSE**
- @midi who wrote the section for **unRAID**
- @polloloco who wrote the *[NVIDIA vGPU Guide](https://gitlab.com/polloloco/vgpu-proxmox)*
- @DualCoder who creates the `vgpu_unlock` functionality [vgpu_unlock](https://github.com/DualCoder/vgpu_unlock)
- Krutav Shah who wrote the [vGPU_Unlock Wiki](https://docs.google.com/document/d/1pzrWJ9h-zANCtyqRgS7Vzla0Y8Ea2-5z2HEi4X75d2Q/)
- Wim van 't Hoog for the [Proxmox All-In-One Installer Script](https://wvthoog.nl/proxmox-vgpu-v3/)
- @mrzenc who wrote [fastapi-dls-nixos](https://github.com/mrzenc/fastapi-dls-nixos)
And thanks to all people who contributed to all these libraries!

27
ROADMAP.md Normal file
View File

@ -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.

View File

@ -1,122 +1,245 @@
import logging
from base64 import b64encode as b64enc
from hashlib import sha256
from uuid import uuid4
from os.path import join, dirname
from os import getenv
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from fastapi.requests import Request
from fastapi.encoders import jsonable_encoder
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.middleware.cors import CORSMiddleware
from starlette.responses import StreamingResponse, JSONResponse, HTMLResponse
import dataset
from Crypto.PublicKey import RSA
from Crypto.PublicKey.RSA import RsaKey
from contextlib import asynccontextmanager
from datetime import datetime, timedelta, UTC
from hashlib import sha256
from json import loads as json_loads
from os import getenv as env
from os.path import join, dirname
from uuid import uuid4
logger = logging.getLogger()
from dateutil.relativedelta import relativedelta
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.requests import Request
from jose import jws, jwk, jwt, JWTError
from jose.constants import ALGORITHMS
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import StreamingResponse, JSONResponse as JSONr, HTMLResponse as HTMLr, Response, RedirectResponse
from orm import Origin, Lease, init as db_init, migrate
from util import load_key, load_file
# Load variables
load_dotenv('../version.env')
VERSION, COMMIT, DEBUG = getenv('VERSION', 'unknown'), getenv('COMMIT', 'unknown'), bool(getenv('DEBUG', False))
# Get current timezone
TZ = datetime.now().astimezone().tzinfo
# Load basic variables
VERSION, COMMIT, DEBUG = env('VERSION', 'unknown'), env('COMMIT', 'unknown'), bool(env('DEBUG', False))
def load_file(filename) -> bytes:
with open(filename, 'rb') as file:
content = file.read()
return content
# Database connection
db = create_engine(str(env('DATABASE', 'sqlite:///db.sqlite')))
db_init(db), migrate(db)
def load_key(filename) -> RsaKey:
return RSA.import_key(extern_key=load_file(filename), passphrase=None)
# todo: initialize certificate (or should be done by user, and passed through "volumes"?)
__details = dict(
title='FastAPI-DLS',
description='Minimal Delegated License Service (DLS).',
version=VERSION,
)
app, db = FastAPI(**__details), dataset.connect(str(getenv('DATABASE', 'sqlite:///db.sqlite')))
TOKEN_EXPIRE_DELTA = relativedelta(hours=1) # days=1
LEASE_EXPIRE_DELTA = relativedelta(days=int(getenv('LEASE_EXPIRE_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'))
CORS_ORIGINS = getenv('CORS_ORIGINS').split(',') if (getenv('CORS_ORIGINS')) else f'https://{DLS_URL}' # todo: prevent static https
# Load DLS variables (all prefixed with "INSTANCE_*" is used as "SERVICE_INSTANCE_*" or "SI_*" in official dls service)
DLS_URL = str(env('DLS_URL', 'localhost'))
DLS_PORT = int(env('DLS_PORT', '443'))
SITE_KEY_XID = str(env('SITE_KEY_XID', '00000000-0000-0000-0000-000000000000'))
INSTANCE_REF = str(env('INSTANCE_REF', '10000000-0000-0000-0000-000000000001'))
ALLOTMENT_REF = str(env('ALLOTMENT_REF', '20000000-0000-0000-0000-000000000001'))
INSTANCE_KEY_RSA = load_key(str(env('INSTANCE_KEY_RSA', join(dirname(__file__), 'cert/instance.private.pem'))))
INSTANCE_KEY_PUB = load_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__), 'cert/instance.public.pem'))))
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)))
CLIENT_TOKEN_EXPIRE_DELTA = relativedelta(years=12)
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)
jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
# Logging
LOG_LEVEL = logging.DEBUG if DEBUG else logging.INFO
logging.basicConfig(format='[{levelname:^7}] [{module:^15}] {message}', style='{')
logger = logging.getLogger(__name__)
logger.setLevel(LOG_LEVEL)
logging.getLogger('util').setLevel(LOG_LEVEL)
logging.getLogger('NV').setLevel(LOG_LEVEL)
# FastAPI
@asynccontextmanager
async def lifespan(_: FastAPI):
# on startup
logger.info(f'''
Using timezone: {str(TZ)}. Make sure this is correct and match your clients!
Your clients renew their license every {str(Lease.calculate_renewal(LEASE_RENEWAL_PERIOD, LEASE_RENEWAL_DELTA))}.
If the renewal fails, the license is {str(LEASE_RENEWAL_DELTA)} valid.
Your client-token file (.tok) is valid for {str(CLIENT_TOKEN_EXPIRE_DELTA)}.
''')
logger.info(f'Debug is {"enabled" if DEBUG else "disabled"}.')
yield
# on shutdown
logger.info(f'Shutting down ...')
config = dict(openapi_url=None, docs_url=None, redoc_url=None) # dict(openapi_url='/-/openapi.json', docs_url='/-/docs', redoc_url='/-/redoc')
app = FastAPI(title='FastAPI-DLS', description='Minimal Delegated License Service (DLS).', version=VERSION, lifespan=lifespan, **config)
app.debug = DEBUG
app.add_middleware(
CORSMiddleware,
allow_origins=CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
allow_methods=['*'],
allow_headers=['*'],
)
logger.setLevel(logging.DEBUG if DEBUG else logging.INFO)
def get_token(request: Request) -> dict:
authorization_header = request.headers['authorization']
# Helper
def __get_token(request: Request) -> dict:
authorization_header = request.headers.get('authorization')
token = authorization_header.split(' ')[1]
return jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
@app.get('/')
# Endpoints
@app.get('/', summary='Index')
async def index():
return RedirectResponse('/-/readme')
@app.get('/-/', summary='* Index')
async def _index():
return RedirectResponse('/-/readme')
@app.get('/-/health', summary='* Health')
async def _health():
return JSONr({'status': 'up'})
@app.get('/-/config', summary='* Config', description='returns environment variables.')
async def _config():
return JSONr({
'VERSION': str(VERSION),
'COMMIT': str(COMMIT),
'DEBUG': str(DEBUG),
'DLS_URL': str(DLS_URL),
'DLS_PORT': str(DLS_PORT),
'SITE_KEY_XID': str(SITE_KEY_XID),
'INSTANCE_REF': str(INSTANCE_REF),
'ALLOTMENT_REF': [str(ALLOTMENT_REF)],
'TOKEN_EXPIRE_DELTA': str(TOKEN_EXPIRE_DELTA),
'LEASE_EXPIRE_DELTA': str(LEASE_EXPIRE_DELTA),
'LEASE_RENEWAL_PERIOD': str(LEASE_RENEWAL_PERIOD),
'CORS_ORIGINS': str(CORS_ORIGINS),
'TZ': str(TZ),
})
@app.get('/-/readme', summary='* Readme')
async def _readme():
from markdown import markdown
content = load_file('../README.md').decode('utf-8')
return HTMLResponse(markdown(text=content, extensions=['tables', 'fenced_code', 'md_in_html', 'nl2br', 'toc']))
content = load_file(join(dirname(__file__), '../README.md')).decode('utf-8')
return HTMLr(markdown(text=content, extensions=['tables', 'fenced_code', 'md_in_html', 'nl2br', 'toc']))
@app.get('/status')
async def status(request: Request):
return JSONResponse({'status': 'up', 'version': VERSION, 'commit': COMMIT, 'debug': DEBUG})
@app.get('/-/manage', summary='* Management UI')
async def _manage(request: Request):
response = '''
<!DOCTYPE html>
<html>
<head>
<title>FastAPI-DLS Management</title>
</head>
<body>
<button onclick="deleteOrigins()">delete ALL origins and their leases</button>
<button onclick="deleteLease()">delete specific lease</button>
<script>
function deleteOrigins() {
const response = confirm('Are you sure you want to delete all origins and their leases?');
if (response) {
var xhr = new XMLHttpRequest();
xhr.open("DELETE", '/-/origins', true);
xhr.send();
}
}
function deleteLease(lease_ref) {
if(lease_ref === undefined)
lease_ref = window.prompt("Please enter 'lease_ref' which should be deleted");
if(lease_ref === null || lease_ref === "")
return
var xhr = new XMLHttpRequest();
xhr.open("DELETE", `/-/lease/${lease_ref}`, true);
xhr.send();
}
</script>
</body>
</html>
'''
return HTMLr(response)
@app.get('/-/origins')
async def _origins(request: Request):
response = list(map(lambda x: jsonable_encoder(x), db['origin'].all()))
return JSONResponse(response)
@app.get('/-/origins', summary='* Origins')
async def _origins(request: Request, leases: bool = False):
session = sessionmaker(bind=db)()
response = []
for origin in session.query(Origin).all():
x = origin.serialize()
if leases:
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)
@app.get('/-/leases')
async def _leases(request: Request):
response = list(map(lambda x: jsonable_encoder(x), db['lease'].all()))
return JSONResponse(response)
@app.delete('/-/origins', summary='* Origins')
async def _origins_delete(request: Request):
Origin.delete(db)
return Response(status_code=201)
@app.get('/-/leases', summary='* Leases')
async def _leases(request: Request, origin: bool = False):
session = sessionmaker(bind=db)()
response = []
for lease in session.query(Lease).all():
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:
x['origin'] = lease_origin.serialize()
response.append(x)
session.close()
return JSONr(response)
@app.delete('/-/leases/expired', summary='* Leases')
async def _lease_delete_expired(request: Request):
Lease.delete_expired(db)
return Response(status_code=201)
@app.delete('/-/lease/{lease_ref}', summary='* Lease')
async def _lease_delete(request: Request, lease_ref: str):
if Lease.delete(db, lease_ref) == 1:
return Response(status_code=201)
return JSONr(status_code=404, content={'status': 404, 'detail': 'lease not found'})
# 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"
}
@app.get('/-/client-token', summary='* Client-Token', description='creates a new messenger token for this service instance')
async def _client_token():
cur_time = datetime.now(UTC)
exp_time = cur_time + CLIENT_TOKEN_EXPIRE_DELTA
payload = {
"jti": str(uuid4()),
@ -126,10 +249,10 @@ async def client_token():
"nbf": timegm(cur_time.timetuple()),
"exp": timegm(exp_time.timetuple()),
"update_mode": "ABSOLUTE",
"scope_ref_list": [str(uuid4())],
"scope_ref_list": [ALLOTMENT_REF],
"fulfillment_class_ref_list": [],
"service_instance_configuration": {
"nls_service_instance_ref": "00000000-0000-0000-0000-000000000000",
"nls_service_instance_ref": INSTANCE_REF,
"svc_port_set_list": [
{
"idx": 0,
@ -139,40 +262,45 @@ async def client_token():
],
"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,
"service_instance_public_key_configuration": {
"service_instance_public_key_me": {
"mod": hex(INSTANCE_KEY_PUB.public_key().n)[2:],
"exp": int(INSTANCE_KEY_PUB.public_key().e),
},
"service_instance_public_key_pem": INSTANCE_KEY_PUB.export_key().decode('utf-8'),
"key_retention_mode": "LATEST_ONLY"
},
}
content = jws.sign(payload, key=jwt_encode_key, headers=None, algorithm=ALGORITHMS.RS256)
response = StreamingResponse(iter([content]), media_type="text/plain")
filename = f'client_configuration_token_{datetime.now().strftime("%d-%m-%y-%H-%M-%S")}'
filename = f'client_configuration_token_{datetime.now().strftime("%d-%m-%y-%H-%M-%S")}.tok'
response.headers["Content-Disposition"] = f'attachment; filename={filename}'
return response
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py
# {"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}
@app.post('/auth/v1/origin')
@app.post('/auth/v1/origin', description='find or create an origin')
async def auth_v1_origin(request: Request):
j = json.loads((await request.body()).decode('utf-8'))
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
origin_ref = j['candidate_origin_ref']
logging.info(f'> [ origin ]: {origin_ref}: {j}')
origin_ref = j.get('candidate_origin_ref')
logger.info(f'> [ origin ]: {origin_ref}: {j}')
data = dict(
data = Origin(
origin_ref=origin_ref,
hostname=j['environment']['hostname'],
guest_driver_version=j['environment']['guest_driver_version'],
os_platform=j['environment']['os_platform'], os_version=j['environment']['os_version'],
hostname=j.get('environment').get('hostname'),
guest_driver_version=j.get('environment').get('guest_driver_version'),
os_platform=j.get('environment').get('os_platform'), os_version=j.get('environment').get('os_version'),
)
db['origin'].upsert(data, ['origin_ref'])
Origin.create_or_update(db, data)
cur_time = datetime.utcnow()
response = {
"origin_ref": origin_ref,
"environment": j['environment'],
"environment": j.get('environment'),
"svc_port_set_list": None,
"node_url_list": None,
"node_query_order": None,
@ -180,28 +308,52 @@ async def auth_v1_origin(request: Request):
"sync_timestamp": cur_time.isoformat()
}
return JSONResponse(response)
return JSONr(response)
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py
@app.post('/auth/v1/origin/update', description='update an origin evidence')
async def auth_v1_origin_update(request: Request):
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
origin_ref = j.get('origin_ref')
logger.info(f'> [ update ]: {origin_ref}: {j}')
data = Origin(
origin_ref=origin_ref,
hostname=j.get('environment').get('hostname'),
guest_driver_version=j.get('environment').get('guest_driver_version'),
os_platform=j.get('environment').get('os_platform'), os_version=j.get('environment').get('os_version'),
)
Origin.create_or_update(db, data)
response = {
"environment": j.get('environment'),
"prompts": None,
"sync_timestamp": cur_time.isoformat()
}
return JSONr(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
# {"code_challenge":"...","origin_ref":"00112233-4455-6677-8899-aabbccddeeff"}
@app.post('/auth/v1/code')
@app.post('/auth/v1/code', description='get an authorization code')
async def auth_v1_code(request: Request):
j = json.loads((await request.body()).decode('utf-8'))
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
origin_ref = j['origin_ref']
logging.info(f'> [ code ]: {origin_ref}: {j}')
origin_ref = j.get('origin_ref')
logger.info(f'> [ code ]: {origin_ref}: {j}')
cur_time = datetime.utcnow()
delta = relativedelta(minutes=15)
expires = cur_time + delta
payload = {
'iat': timegm(cur_time.timetuple()),
'exp': timegm(expires.timetuple()),
'challenge': j['code_challenge'],
'origin_ref': j['origin_ref'],
'challenge': j.get('code_challenge'),
'origin_ref': j.get('origin_ref'),
'key_ref': SITE_KEY_XID,
'kid': SITE_KEY_XID
}
@ -214,25 +366,28 @@ async def auth_v1_code(request: Request):
"prompts": None
}
return JSONResponse(response)
return JSONr(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
# {"auth_code":"...","code_verifier":"..."}
@app.post('/auth/v1/token')
@app.post('/auth/v1/token', description='exchange auth code and verifier for token')
async def auth_v1_token(request: Request):
j = json.loads((await request.body()).decode('utf-8'))
payload = jwt.decode(token=j['auth_code'], key=jwt_decode_key)
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
origin_ref = payload['origin_ref']
logging.info(f'> [ auth ]: {origin_ref}: {j}')
try:
payload = jwt.decode(token=j.get('auth_code'), key=jwt_decode_key, algorithms=ALGORITHMS.RS256)
except JWTError as e:
return JSONr(status_code=400, content={'status': 400, 'title': 'invalid token', 'detail': str(e)})
origin_ref = payload.get('origin_ref')
logger.info(f'> [ auth ]: {origin_ref}: {j}')
# validate the code challenge
if payload['challenge'] != b64enc(sha256(j['code_verifier'].encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'):
raise HTTPException(status_code=401, detail='expected challenge did not match verifier')
challenge = b64enc(sha256(j.get('code_verifier').encode('utf-8')).digest()).rstrip(b'=').decode('utf-8')
if payload.get('challenge') != challenge:
return JSONr(status_code=401, content={'status': 401, 'detail': 'expected challenge did not match verifier'})
cur_time = datetime.utcnow()
access_expires_on = cur_time + TOKEN_EXPIRE_DELTA
new_payload = {
@ -254,38 +409,45 @@ async def auth_v1_token(request: Request):
"sync_timestamp": cur_time.isoformat(),
}
return JSONResponse(response)
return JSONr(response)
# {'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']}
@app.post('/leasing/v1/lessor')
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
@app.post('/leasing/v1/lessor', description='request multiple leases (borrow) for current origin')
async def leasing_v1_lessor(request: Request):
j, token = json.loads((await request.body()).decode('utf-8')), get_token(request)
j, token, cur_time = json_loads((await request.body()).decode('utf-8')), __get_token(request), datetime.now(UTC)
origin_ref = token['origin_ref']
scope_ref_list = j['scope_ref_list']
logging.info(f'> [ create ]: {origin_ref}: create leases for scope_ref_list {scope_ref_list}')
try:
token = __get_token(request)
except JWTError:
return JSONr(status_code=401, content={'status': 401, 'detail': 'token is not valid'})
origin_ref = token.get('origin_ref')
scope_ref_list = j.get('scope_ref_list')
logger.info(f'> [ create ]: {origin_ref}: create leases for scope_ref_list {scope_ref_list}')
cur_time = datetime.utcnow()
lease_result_list = []
for scope_ref in scope_ref_list:
# if scope_ref not in [ALLOTMENT_REF]:
# return JSONr(status_code=500, detail=f'no service instances found for scopes: ["{scope_ref}"]')
lease_ref = str(uuid4())
expires = cur_time + LEASE_EXPIRE_DELTA
lease_result_list.append({
"ordinal": 0,
# https://docs.nvidia.com/license-system/latest/nvidia-license-system-user-guide/index.html
"lease": {
"ref": scope_ref,
"ref": lease_ref,
"created": cur_time.isoformat(),
"expires": expires.isoformat(),
# The percentage of the lease period that must elapse before a licensed client can renew a license
"recommended_lease_renewal": 0.15,
"recommended_lease_renewal": LEASE_RENEWAL_PERIOD,
"offline_lease": "true",
"license_type": "CONCURRENT_COUNTED_SINGLE"
}
})
data = dict(origin_ref=origin_ref, lease_ref=scope_ref, lease_created=cur_time, lease_expires=expires)
db['lease'].insert_ignore(data, ['origin_ref', 'lease_ref']) # todo: handle update
data = Lease(origin_ref=origin_ref, lease_ref=lease_ref, lease_created=cur_time, lease_expires=expires)
Lease.create_or_update(db, data)
response = {
"lease_result_list": lease_result_list,
@ -294,76 +456,124 @@ async def leasing_v1_lessor(request: Request):
"prompts": None
}
return JSONResponse(response)
return JSONr(response)
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
# venv/lib/python3.9/site-packages/nls_dal_service_instance_dls/schema/service_instance/V1_0_21__product_mapping.sql
@app.get('/leasing/v1/lessor/leases')
@app.get('/leasing/v1/lessor/leases', description='get active leases for current origin')
async def leasing_v1_lessor_lease(request: Request):
token = get_token(request)
token, cur_time = __get_token(request), datetime.now(UTC)
origin_ref = token['origin_ref']
origin_ref = token.get('origin_ref')
active_lease_list = list(map(lambda x: x['lease_ref'], db['lease'].find(origin_ref=origin_ref)))
logging.info(f'> [ leases ]: {origin_ref}: found {len(active_lease_list)} active leases')
active_lease_list = list(map(lambda x: x.lease_ref, Lease.find_by_origin_ref(db, origin_ref)))
logger.info(f'> [ leases ]: {origin_ref}: found {len(active_lease_list)} active leases')
cur_time = datetime.utcnow()
response = {
"active_lease_list": active_lease_list,
"sync_timestamp": cur_time.isoformat(),
"prompts": None
}
return JSONResponse(response)
return JSONr(response)
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_single_controller.py
# venv/lib/python3.9/site-packages/nls_core_lease/lease_single.py
@app.put('/leasing/v1/lease/{lease_ref}')
@app.put('/leasing/v1/lease/{lease_ref}', description='renew a lease')
async def leasing_v1_lease_renew(request: Request, lease_ref: str):
token = get_token(request)
token, cur_time = __get_token(request), datetime.now(UTC)
origin_ref = token['origin_ref']
logging.info(f'> [ renew ]: {origin_ref}: renew {lease_ref}')
origin_ref = token.get('origin_ref')
logger.info(f'> [ renew ]: {origin_ref}: renew {lease_ref}')
if db['lease'].count(origin_ref=origin_ref, lease_ref=lease_ref) == 0:
raise HTTPException(status_code=404, detail='requested lease not available')
entity = Lease.find_by_origin_ref_and_lease_ref(db, origin_ref, lease_ref)
if entity is None:
return JSONr(status_code=404, content={'status': 404, 'detail': 'requested lease not available'})
cur_time = datetime.utcnow()
expires = cur_time + LEASE_EXPIRE_DELTA
response = {
"lease_ref": lease_ref,
"expires": expires.isoformat(),
"recommended_lease_renewal": 0.16,
"recommended_lease_renewal": LEASE_RENEWAL_PERIOD,
"offline_lease": True,
"prompts": None,
"sync_timestamp": cur_time.isoformat(),
}
data = dict(origin_ref=origin_ref, lease_ref=lease_ref, lease_expires=expires, lease_last_update=cur_time)
db['lease'].update(data, ['origin_ref', 'lease_ref'])
Lease.renew(db, entity, expires, cur_time)
return JSONResponse(response)
return JSONr(response)
@app.delete('/leasing/v1/lessor/leases')
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_single_controller.py
@app.delete('/leasing/v1/lease/{lease_ref}', description='release (return) a lease')
async def leasing_v1_lease_delete(request: Request, lease_ref: str):
token, cur_time = __get_token(request), datetime.now(UTC)
origin_ref = token.get('origin_ref')
logger.info(f'> [ return ]: {origin_ref}: return {lease_ref}')
entity = Lease.find_by_lease_ref(db, lease_ref)
if entity.origin_ref != origin_ref:
return JSONr(status_code=403, content={'status': 403, 'detail': 'access or operation forbidden'})
if entity is None:
return JSONr(status_code=404, content={'status': 404, 'detail': 'requested lease not available'})
if Lease.delete(db, lease_ref) == 0:
return JSONr(status_code=404, content={'status': 404, 'detail': 'lease not found'})
response = {
"lease_ref": lease_ref,
"prompts": None,
"sync_timestamp": cur_time.isoformat(),
}
return JSONr(response)
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
@app.delete('/leasing/v1/lessor/leases', description='release all leases')
async def leasing_v1_lessor_lease_remove(request: Request):
token = get_token(request)
token, cur_time = __get_token(request), datetime.now(UTC)
origin_ref = token['origin_ref']
origin_ref = token.get('origin_ref')
released_lease_list = list(map(lambda x: x['lease_ref'], db['lease'].find(origin_ref=origin_ref)))
deletions = db['lease'].delete(origin_ref=origin_ref)
logging.info(f'> [ remove ]: {origin_ref}: removed {deletions} leases')
released_lease_list = list(map(lambda x: x.lease_ref, Lease.find_by_origin_ref(db, origin_ref)))
deletions = Lease.cleanup(db, origin_ref)
logger.info(f'> [ remove ]: {origin_ref}: removed {deletions} leases')
cur_time = datetime.utcnow()
response = {
"released_lease_list": released_lease_list,
"release_failure_list": None,
"sync_timestamp": cur_time.isoformat(),
"prompts": None
}
return JSONResponse(response)
return JSONr(response)
@app.post('/leasing/v1/lessor/shutdown', description='shutdown all leases')
async def leasing_v1_lessor_shutdown(request: Request):
j, cur_time = json_loads((await request.body()).decode('utf-8')), datetime.now(UTC)
token = j.get('token')
token = jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
origin_ref = token.get('origin_ref')
released_lease_list = list(map(lambda x: x.lease_ref, Lease.find_by_origin_ref(db, origin_ref)))
deletions = Lease.cleanup(db, origin_ref)
logger.info(f'> [ shutdown ]: {origin_ref}: removed {deletions} leases')
response = {
"released_lease_list": released_lease_list,
"release_failure_list": None,
"sync_timestamp": cur_time.isoformat(),
"prompts": None
}
return JSONr(response)
if __name__ == '__main__':
@ -377,7 +587,7 @@ if __name__ == '__main__':
#
###
logging.info(f'> Starting dev-server ...')
logger.info(f'> Starting dev-server ...')
ssl_keyfile = join(dirname(__file__), 'cert/webserver.key')
ssl_certfile = join(dirname(__file__), 'cert/webserver.crt')

243
app/orm.py Normal file
View File

@ -0,0 +1,243 @@
from datetime import datetime, timedelta, timezone, UTC
from dateutil.relativedelta import relativedelta
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect, text
from sqlalchemy.engine import Engine
from sqlalchemy.orm import sessionmaker, declarative_base
from util import NV
Base = declarative_base()
class Origin(Base):
__tablename__ = "origin"
origin_ref = Column(CHAR(length=36), primary_key=True, unique=True, index=True) # uuid4
# service_instance_xid = Column(CHAR(length=36), nullable=False, index=True) # uuid4 # not necessary, we only support one service_instance_xid ('INSTANCE_REF')
hostname = Column(VARCHAR(length=256), nullable=True)
guest_driver_version = Column(VARCHAR(length=10), nullable=True)
os_platform = Column(VARCHAR(length=256), nullable=True)
os_version = Column(VARCHAR(length=256), nullable=True)
def __repr__(self):
return f'Origin(origin_ref={self.origin_ref}, hostname={self.hostname})'
def serialize(self) -> dict:
_ = NV().find(self.guest_driver_version)
return {
'origin_ref': self.origin_ref,
# 'service_instance_xid': self.service_instance_xid,
'hostname': self.hostname,
'guest_driver_version': self.guest_driver_version,
'os_platform': self.os_platform,
'os_version': self.os_version,
'$driver': _ if _ is not None else None,
}
@staticmethod
def create_statement(engine: Engine):
from sqlalchemy.schema import CreateTable
return CreateTable(Origin.__table__).compile(engine)
@staticmethod
def create_or_update(engine: Engine, origin: "Origin"):
session = sessionmaker(bind=engine)()
entity = session.query(Origin).filter(Origin.origin_ref == origin.origin_ref).first()
if entity is None:
session.add(origin)
else:
x = dict(
hostname=origin.hostname,
guest_driver_version=origin.guest_driver_version,
os_platform=origin.os_platform,
os_version=origin.os_version
)
session.execute(update(Origin).where(Origin.origin_ref == origin.origin_ref).values(**x))
session.commit()
session.flush()
session.close()
@staticmethod
def delete(engine: Engine, origin_refs: [str] = None) -> int:
session = sessionmaker(bind=engine)()
if origin_refs is None:
deletions = session.query(Origin).delete()
else:
deletions = session.query(Origin).filter(Origin.origin_ref.in_(origin_refs)).delete()
session.commit()
session.close()
return deletions
@staticmethod
def delete_expired(engine: Engine) -> int:
session = sessionmaker(bind=engine)()
origins = session.query(Origin).join(Lease, Origin.origin_ref == Lease.origin_ref, isouter=True).filter(Lease.lease_ref.is_(None)).all()
origin_refs = [origin.origin_ref for origin in origins]
deletions = session.query(Origin).filter(Origin.origin_ref.in_(origin_refs)).delete()
session.commit()
session.close()
return deletions
class Lease(Base):
__tablename__ = "lease"
lease_ref = Column(CHAR(length=36), primary_key=True, nullable=False, index=True) # uuid4
origin_ref = Column(CHAR(length=36), ForeignKey(Origin.origin_ref, ondelete='CASCADE'), nullable=False, index=True) # uuid4
# scope_ref = Column(CHAR(length=36), nullable=False, index=True) # uuid4 # not necessary, we only support one scope_ref ('ALLOTMENT_REF')
lease_created = Column(DATETIME(), nullable=False)
lease_expires = Column(DATETIME(), nullable=False)
lease_updated = Column(DATETIME(), nullable=False)
def __repr__(self):
return f'Lease(origin_ref={self.origin_ref}, lease_ref={self.lease_ref}, expires={self.lease_expires})'
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,
# 'scope_ref': self.scope_ref,
'lease_created': self.lease_created.replace(tzinfo=timezone.utc).isoformat(),
'lease_expires': self.lease_expires.replace(tzinfo=timezone.utc).isoformat(),
'lease_updated': self.lease_updated.replace(tzinfo=timezone.utc).isoformat(),
'lease_renewal': lease_renewal.replace(tzinfo=timezone.utc).isoformat(),
}
@staticmethod
def create_statement(engine: Engine):
from sqlalchemy.schema import CreateTable
return CreateTable(Lease.__table__).compile(engine)
@staticmethod
def create_or_update(engine: Engine, lease: "Lease"):
session = sessionmaker(bind=engine)()
entity = session.query(Lease).filter(Lease.lease_ref == lease.lease_ref).first()
if entity is None:
if lease.lease_updated is None:
lease.lease_updated = lease.lease_created
session.add(lease)
else:
x = dict(origin_ref=lease.origin_ref, lease_expires=lease.lease_expires, lease_updated=lease.lease_updated)
session.execute(update(Lease).where(Lease.lease_ref == lease.lease_ref).values(**x))
session.commit()
session.flush()
session.close()
@staticmethod
def find_by_origin_ref(engine: Engine, origin_ref: str) -> ["Lease"]:
session = sessionmaker(bind=engine)()
entities = session.query(Lease).filter(Lease.origin_ref == origin_ref).all()
session.close()
return entities
@staticmethod
def find_by_lease_ref(engine: Engine, lease_ref: str) -> "Lease":
session = sessionmaker(bind=engine)()
entity = session.query(Lease).filter(Lease.lease_ref == lease_ref).first()
session.close()
return entity
@staticmethod
def find_by_origin_ref_and_lease_ref(engine: Engine, origin_ref: str, lease_ref: str) -> "Lease":
session = sessionmaker(bind=engine)()
entity = session.query(Lease).filter(and_(Lease.origin_ref == origin_ref, Lease.lease_ref == lease_ref)).first()
session.close()
return entity
@staticmethod
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))
session.commit()
session.close()
@staticmethod
def cleanup(engine: Engine, origin_ref: str) -> int:
session = sessionmaker(bind=engine)()
deletions = session.query(Lease).filter(Lease.origin_ref == origin_ref).delete()
session.commit()
session.close()
return deletions
@staticmethod
def delete(engine: Engine, lease_ref: str) -> int:
session = sessionmaker(bind=engine)()
deletions = session.query(Lease).filter(Lease.lease_ref == lease_ref).delete()
session.commit()
session.close()
return deletions
@staticmethod
def delete_expired(engine: Engine) -> int:
session = sessionmaker(bind=engine)()
deletions = session.query(Lease).filter(Lease.lease_expires <= datetime.now(UTC)).delete()
session.commit()
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
import datetime
LEASE_RENEWAL_PERIOD=0.15 # 15%
delta = datetime.timedelta(days=90)
renew = delta.total_seconds() * LEASE_RENEWAL_PERIOD
renew = datetime.timedelta(seconds=renew)
expires = delta - renew # 76 days, 12:00:00 hours
"""
renew = delta.total_seconds() * renewal_period
renew = timedelta(seconds=renew)
return renew
def init(engine: Engine):
tables = [Origin, Lease]
db = inspect(engine)
session = sessionmaker(bind=engine)()
for table in tables:
if not db.dialect.has_table(engine.connect(), table.__tablename__):
session.execute(text(str(table.create_statement(engine))))
session.commit()
session.close()
def migrate(engine: Engine):
db = inspect(engine)
def upgrade_1_0_to_1_1():
x = db.dialect.get_columns(engine.connect(), Lease.__tablename__)
x = next(_ for _ in x if _['name'] == 'origin_ref')
if x['primary_key'] > 0:
print('Found old database schema with "origin_ref" as primary-key in "lease" table. Dropping table!')
print(' Your leases are recreated on next renewal!')
print(' If an error message appears on the client, you can ignore it.')
Lease.__table__.drop(bind=engine)
init(engine)
# def upgrade_1_2_to_1_3():
# x = db.dialect.get_columns(engine.connect(), Lease.__tablename__)
# x = next((_ for _ in x if _['name'] == 'scope_ref'), None)
# if x is None:
# Lease.scope_ref.compile()
# column_name = Lease.scope_ref.name
# column_type = Lease.scope_ref.type.compile(engine.dialect)
# engine.execute(f'ALTER TABLE "{Lease.__tablename__}" ADD COLUMN "{column_name}" {column_type}')
upgrade_1_0_to_1_1()
# upgrade_1_2_to_1_3()

82
app/util.py Normal file
View File

@ -0,0 +1,82 @@
import logging
logging.basicConfig()
def load_file(filename: str) -> bytes:
log = logging.getLogger(f'{__name__}')
log.debug(f'Loading contents of file "{filename}')
with open(filename, 'rb') as file:
content = file.read()
return content
def load_key(filename: str) -> "RsaKey":
try:
# Crypto | Cryptodome on Debian
from Crypto.PublicKey import RSA
from Crypto.PublicKey.RSA import RsaKey
except ModuleNotFoundError:
from Cryptodome.PublicKey import RSA
from Cryptodome.PublicKey.RSA import RsaKey
log = logging.getLogger(__name__)
log.debug(f'Importing RSA-Key from "{filename}"')
return RSA.import_key(extern_key=load_file(filename), passphrase=None)
def generate_key() -> "RsaKey":
try:
# Crypto | Cryptodome on Debian
from Crypto.PublicKey import RSA
from Crypto.PublicKey.RSA import RsaKey
except ModuleNotFoundError:
from Cryptodome.PublicKey import RSA
from Cryptodome.PublicKey.RSA import RsaKey
log = logging.getLogger(__name__)
log.debug(f'Generating RSA-Key')
return RSA.generate(bits=2048)
class NV:
__DRIVER_MATRIX_FILENAME = 'static/driver_matrix.json'
__DRIVER_MATRIX: None | dict = None # https://docs.nvidia.com/grid/ => "Driver Versions"
def __init__(self):
self.log = logging.getLogger(self.__class__.__name__)
if NV.__DRIVER_MATRIX is None:
from json import load as json_load
try:
file = open(NV.__DRIVER_MATRIX_FILENAME)
NV.__DRIVER_MATRIX = json_load(file)
file.close()
self.log.debug(f'Successfully loaded "{NV.__DRIVER_MATRIX_FILENAME}".')
except Exception as e:
NV.__DRIVER_MATRIX = {} # init empty dict to not try open file everytime, just when restarting app
# self.log.warning(f'Failed to load "{NV.__DRIVER_MATRIX_FILENAME}": {e}')
@staticmethod
def find(version: str) -> dict | None:
if NV.__DRIVER_MATRIX is None:
return None
for idx, (key, branch) in enumerate(NV.__DRIVER_MATRIX.items()):
for release in branch.get('$releases'):
linux_driver = release.get('Linux Driver')
windows_driver = release.get('Windows Driver')
if version == linux_driver or version == windows_driver:
tmp = branch.copy()
tmp.pop('$releases')
is_latest = release.get('vGPU Software') == branch.get('Latest Release in Branch')
return {
'software_branch': branch.get('vGPU Software Branch'),
'branch_version': release.get('vGPU Software'),
'driver_branch': branch.get('Driver Branch'),
'branch_status': branch.get('vGPU Branch Status'),
'release_date': release.get('Release Date'),
'eol': branch.get('EOL Date') if is_latest else None,
'is_latest': is_latest,
}
return None

26
doc/Database.md Normal file
View File

@ -0,0 +1,26 @@
# Database structure
## `request_routing.service_instance`
| xid | org_name |
|----------------------------------------|--------------------------|
| `10000000-0000-0000-0000-000000000000` | `lic-000000000000000000` |
- `xid` is used as `SERVICE_INSTANCE_XID`
## `request_routing.license_allotment_service_instance`
| xid | service_instance_xid | license_allotment_xid |
|----------------------------------------|----------------------------------------|----------------------------------------|
| `90000000-0000-0000-0000-000000000001` | `10000000-0000-0000-0000-000000000000` | `80000000-0000-0000-0000-000000000001` |
- `xid` is only a primary-key and never used as foreign-key or reference
- `license_allotment_xid` must be used to fetch `xid`'s from `request_routing.license_allotment_reference`
## `request_routing.license_allotment_reference`
| xid | license_allotment_xid |
|----------------------------------------|----------------------------------------|
| `20000000-0000-0000-0000-000000000001` | `80000000-0000-0000-0000-000000000001` |
- `xid` is used as `scope_ref_list` on token request

View File

@ -1,5 +1,7 @@
# Reverse Engineering Notes
[[_TOC_]]
# Usefully commands
## Check licensing status
@ -27,13 +29,62 @@ nvidia-gridd[2986]: Acquiring license. (Info: license.nvidia.space; NVIDIA RTX V
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)
# Docker DLS-Container File-System
- More about Docker Images https://git.collinwebdesigns.de/nvidia/nls
## Configuration data
Most variables and configs are stored in `/var/lib/docker/volumes/configurations/_data`.
## Dive / Docker image inspector
Files can be modified with `docker cp <container-id>:/venv/... /opt/localfile/...` and back.
(May you need to fix permissions with `docker exec -u 0 <container-id> chown nonroot:nonroot /venv/...`)
Config-Variables are in `etc/dls/config/service_env.conf`.
## Site Key Uri - `/etc/dls/config/site_key_uri.bin`
```
base64-content...
```
## DB Password - `/etc/dls/config/dls_db_password.bin`
```
# docker cp -a <container-id>:/etc/dls/config/dls_db_password.bin /tmp/dls_db_password.bin
base64-content...
```
**Decrypt database password**
```
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
```
# Docker Postgres-Container
- It's enough to manipulate database licenses. There must not be changed any line of code to bypass licensing
validations.
## Inspect
Valid users are `dls_writer` and `postgres`.
```shell
docker exec -it <dls:pgsql> psql -h localhost -U postgres
```
## External Access
Or you can modify `docker-compose.yaml` to forward Postgres port. To create a superuser for external access, use `docker exec` from above and rund the following:
```sql
CREATE USER admin WITH LOGIN SUPERUSER PASSWORD 'admin';
```
# Dive / Docker image inspector
- `dive dls:appliance`
@ -50,45 +101,6 @@ 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

29
docker-compose.yml Normal file
View File

@ -0,0 +1,29 @@
version: '3.9'
x-dls-variables: &dls-variables
TZ: Europe/Berlin # REQUIRED, set your timezone correctly on fastapi-dls AND YOUR CLIENTS !!!
DLS_URL: localhost # REQUIRED, change to your ip or hostname
DLS_PORT: 443
LEASE_EXPIRE_DAYS: 90 # 90 days is maximum
DATABASE: sqlite:////app/database/db.sqlite
DEBUG: false
services:
dls:
image: collinwebdesigns/fastapi-dls:latest
restart: always
environment:
<<: *dls-variables
ports:
- "443:443"
volumes:
- /opt/docker/fastapi-dls/cert:/app/cert
- dls-db:/app/database
logging: # optional, for those who do not need logs
driver: "json-file"
options:
max-file: 5
max-size: 10m
volumes:
dls-db:

View File

@ -0,0 +1,120 @@
version: '3.9'
x-dls-variables: &dls-variables
DLS_URL: localhost # REQUIRED, change to your ip or hostname
DLS_PORT: 443 # must match nginx listen & exposed port
LEASE_EXPIRE_DAYS: 90
DATABASE: sqlite:////app/database/db.sqlite
DEBUG: false
services:
dls:
image: collinwebdesigns/fastapi-dls:latest
restart: always
environment:
<<: *dls-variables
volumes:
- /etc/timezone:/etc/timezone:ro
- /opt/docker/fastapi-dls/cert:/app/cert # instance.private.pem, instance.public.pem
- db:/app/database
entrypoint: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--app-dir", "/app", "--proxy-headers"]
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:8000/-/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
proxy:
image: nginx
ports:
# thees are ports where nginx (!) is listen to
- "80:80" # for "/leasing/v1/lessor/shutdown" used by windows guests, can't be changed!
- "443:443" # first part must match "DLS_PORT"
volumes:
- /etc/timezone:/etc/timezone:ro
- /opt/docker/fastapi-dls/cert:/opt/cert
healthcheck:
test: ["CMD", "curl", "--insecure", "--fail", "https://localhost/-/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
command: |
bash -c "bash -s <<\"EOF\"
cat > /etc/nginx/nginx.conf <<\"EON\"
daemon off;
user root;
worker_processes auto;
events {
worker_connections 1024;
}
http {
gzip on;
gzip_disable "msie6";
include /etc/nginx/mime.types;
upstream dls-backend {
server dls:8000; # must match dls listen port
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
root /var/www/html;
index index.html;
server_name _;
ssl_certificate "/opt/cert/webserver.crt";
ssl_certificate_key "/opt/cert/webserver.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.3 TLSv1.2;
# ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305";
# ssl_ciphers PROFILE=SYSTEM;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host $$http_host;
proxy_set_header X-Real-IP $$remote_addr;
proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $$scheme;
proxy_pass http://dls-backend$$request_uri;
}
location = /-/health {
access_log off;
add_header 'Content-Type' 'application/json';
return 200 '{\"status\":\"up\",\"service\":\"nginx\"}';
}
}
server {
listen 80;
listen [::]:80;
root /var/www/html;
index index.html;
server_name _;
location /leasing/v1/lessor/shutdown {
proxy_set_header Host $$http_host;
proxy_set_header X-Real-IP $$remote_addr;
proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $$scheme;
proxy_pass http://dls-backend/leasing/v1/lessor/shutdown;
}
location / {
return 301 https://$$host$$request_uri;
}
}
}
EON
nginx
EOF"
volumes:
db:

View File

@ -1,8 +1,8 @@
fastapi==0.88.0
uvicorn[standard]==0.20.0
python-jose==3.3.0
pycryptodome==3.16.0
fastapi==0.115.8
uvicorn[standard]==0.34.0
python-jose==3.4.0
pycryptodome==3.21.0
python-dateutil==2.8.2
dataset==1.5.2
markdown==3.4.1
python-dotenv==0.21.0
sqlalchemy==2.0.38
markdown==3.7
python-dotenv==1.0.1

View File

@ -0,0 +1,137 @@
import logging
logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
URL = 'https://docs.nvidia.com/vgpu/index.html'
BRANCH_STATUS_KEY, SOFTWARE_BRANCH_KEY, = 'vGPU Branch Status', 'vGPU Software Branch'
VGPU_KEY, GRID_KEY, DRIVER_BRANCH_KEY = 'vGPU Software', 'vGPU Software', 'Driver Branch'
LINUX_VGPU_MANAGER_KEY, LINUX_DRIVER_KEY = 'Linux vGPU Manager', 'Linux Driver'
WINDOWS_VGPU_MANAGER_KEY, WINDOWS_DRIVER_KEY = 'Windows vGPU Manager', 'Windows Driver'
ALT_VGPU_MANAGER_KEY = 'vGPU Manager'
RELEASE_DATE_KEY, LATEST_KEY, EOL_KEY = 'Release Date', 'Latest Release in Branch', 'EOL Date'
JSON_RELEASES_KEY = '$releases'
def __driver_versions(html: 'BeautifulSoup'):
def __strip(_: str) -> str:
# removes content after linebreak (e.g. "Hello\n World" to "Hello")
_ = _.strip()
tmp = _.split('\n')
if len(tmp) > 0:
return tmp[0]
return _
# find wrapper for "DriverVersions" and find tables
data = html.find('div', {'id': 'driver-versions'})
items = data.findAll('bsp-accordion', {'class': 'Accordion-items-item'})
for item in items:
software_branch = item.find('div', {'class': 'Accordion-items-item-title'}).text.strip()
software_branch = software_branch.replace(' Releases', '')
matrix_key = software_branch.lower()
# driver version info from table-heads (ths) and table-rows (trs)
table = item.find('table')
ths, trs = table.find_all('th'), table.find_all('tr')
headers, releases = [header.text.strip() for header in ths], []
for trs in trs:
tds = trs.find_all('td')
if len(tds) == 0: # skip empty
continue
# create dict with table-heads as key and cell content as value
x = {headers[i]: __strip(cell.text) for i, cell in enumerate(tds)}
releases.append(x)
# add to matrix
MATRIX.update({matrix_key: {JSON_RELEASES_KEY: releases}})
def __release_branches(html: 'BeautifulSoup'):
# find wrapper for "AllReleaseBranches" and find table
data = html.find('div', {'id': 'all-release-branches'})
table = data.find('table')
# branch releases info from table-heads (ths) and table-rows (trs)
ths, trs = table.find_all('th'), table.find_all('tr')
headers = [header.text.strip() for header in ths]
for trs in trs:
tds = trs.find_all('td')
if len(tds) == 0: # skip empty
continue
# create dict with table-heads as key and cell content as value
x = {headers[i]: cell.text.strip() for i, cell in enumerate(tds)}
# get matrix_key
software_branch = x.get(SOFTWARE_BRANCH_KEY)
matrix_key = software_branch.lower()
# add to matrix
MATRIX.update({matrix_key: MATRIX.get(matrix_key) | x})
def __debug():
# print table head
s = f'{SOFTWARE_BRANCH_KEY:^21} | {BRANCH_STATUS_KEY:^21} | {VGPU_KEY:^13} | {LINUX_VGPU_MANAGER_KEY:^21} | {LINUX_DRIVER_KEY:^21} | {WINDOWS_VGPU_MANAGER_KEY:^21} | {WINDOWS_DRIVER_KEY:^21} | {RELEASE_DATE_KEY:>21} | {EOL_KEY:>21}'
print(s)
# iterate over dict & format some variables to not overload table
for idx, (key, branch) in enumerate(MATRIX.items()):
branch_status = branch.get(BRANCH_STATUS_KEY)
branch_status = branch_status.replace('Branch ', '')
branch_status = branch_status.replace('Long-Term Support', 'LTS')
branch_status = branch_status.replace('Production', 'Prod.')
software_branch = branch.get(SOFTWARE_BRANCH_KEY).replace('NVIDIA ', '')
for release in branch.get(JSON_RELEASES_KEY):
version = release.get(VGPU_KEY, release.get(GRID_KEY, ''))
linux_manager = release.get(LINUX_VGPU_MANAGER_KEY, release.get(ALT_VGPU_MANAGER_KEY, ''))
linux_driver = release.get(LINUX_DRIVER_KEY)
windows_manager = release.get(WINDOWS_VGPU_MANAGER_KEY, release.get(ALT_VGPU_MANAGER_KEY, ''))
windows_driver = release.get(WINDOWS_DRIVER_KEY)
release_date = release.get(RELEASE_DATE_KEY)
is_latest = release.get(VGPU_KEY) == branch.get(LATEST_KEY)
version = f'{version} *' if is_latest else version
eol = branch.get(EOL_KEY) if is_latest else ''
s = f'{software_branch:^21} | {branch_status:^21} | {version:<13} | {linux_manager:<21} | {linux_driver:<21} | {windows_manager:<21} | {windows_driver:<21} | {release_date:>21} | {eol:>21}'
print(s)
def __dump(filename: str):
import json
file = open(filename, 'w')
json.dump(MATRIX, file)
file.close()
if __name__ == '__main__':
MATRIX = {}
try:
import httpx
from bs4 import BeautifulSoup
except Exception as e:
logger.error(f'Failed to import module: {e}')
logger.info('Run "pip install beautifulsoup4 httpx"')
exit(1)
r = httpx.get(URL)
if r.status_code != 200:
logger.error(f'Error loading "{URL}" with status code {r.status_code}.')
exit(2)
# parse html
soup = BeautifulSoup(r.text, features='html.parser')
# build matrix
__driver_versions(soup)
__release_branches(soup)
# debug output
__debug()
# dump data to file
__dump('../app/static/driver_matrix.json')

242
test/main.py Normal file
View File

@ -0,0 +1,242 @@
import sys
from base64 import b64encode as b64enc
from calendar import timegm
from datetime import datetime, UTC
from hashlib import sha256
from os.path import dirname, join
from uuid import uuid4, UUID
from dateutil.relativedelta import relativedelta
from jose import jwt, jwk
from jose.constants import ALGORITHMS
from starlette.testclient import TestClient
# add relative path to use packages as they were in the app/ dir
sys.path.append('../')
sys.path.append('../app')
from app import main
from app.util import load_key
client = TestClient(main.app)
ORIGIN_REF, ALLOTMENT_REF, SECRET = str(uuid4()), '20000000-0000-0000-0000-000000000001', 'HelloWorld'
# INSTANCE_KEY_RSA = generate_key()
# INSTANCE_KEY_PUB = INSTANCE_KEY_RSA.public_key()
INSTANCE_KEY_RSA = load_key(str(join(dirname(__file__), '../app/cert/instance.private.pem')))
INSTANCE_KEY_PUB = load_key(str(join(dirname(__file__), '../app/cert/instance.public.pem')))
jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
def __bearer_token(origin_ref: str) -> str:
token = jwt.encode({"origin_ref": origin_ref}, key=jwt_encode_key, algorithm=ALGORITHMS.RS256)
token = f'Bearer {token}'
return token
def test_index():
response = client.get('/')
assert response.status_code == 200
def test_health():
response = client.get('/-/health')
assert response.status_code == 200
assert response.json().get('status') == 'up'
def test_config():
response = client.get('/-/config')
assert response.status_code == 200
def test_readme():
response = client.get('/-/readme')
assert response.status_code == 200
def test_manage():
response = client.get('/-/manage')
assert response.status_code == 200
def test_client_token():
response = client.get('/-/client-token')
assert response.status_code == 200
def test_origins():
pass
def test_origins_delete():
pass
def test_leases():
pass
def test_lease_delete():
pass
def test_auth_v1_origin():
payload = {
"registration_pending": False,
"environment": {
"guest_driver_version": "guest_driver_version",
"hostname": "myhost",
"ip_address_list": ["192.168.1.123"],
"os_version": "os_version",
"os_platform": "os_platform",
"fingerprint": {"mac_address_list": ["ff:ff:ff:ff:ff:ff"]},
"host_driver_version": "host_driver_version"
},
"update_pending": False,
"candidate_origin_ref": ORIGIN_REF,
}
response = client.post('/auth/v1/origin', json=payload)
assert response.status_code == 200
assert response.json().get('origin_ref') == ORIGIN_REF
def auth_v1_origin_update():
payload = {
"registration_pending": False,
"environment": {
"guest_driver_version": "guest_driver_version",
"hostname": "myhost",
"ip_address_list": ["192.168.1.123"],
"os_version": "os_version",
"os_platform": "os_platform",
"fingerprint": {"mac_address_list": ["ff:ff:ff:ff:ff:ff"]},
"host_driver_version": "host_driver_version"
},
"update_pending": False,
"candidate_origin_ref": ORIGIN_REF,
}
response = client.post('/auth/v1/origin/update', json=payload)
assert response.status_code == 200
assert response.json().get('origin_ref') == ORIGIN_REF
def test_auth_v1_code():
payload = {
"code_challenge": b64enc(sha256(SECRET.encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'),
"origin_ref": ORIGIN_REF,
}
response = client.post('/auth/v1/code', json=payload)
assert response.status_code == 200
payload = jwt.get_unverified_claims(token=response.json().get('auth_code'))
assert payload.get('origin_ref') == ORIGIN_REF
def test_auth_v1_token():
cur_time = datetime.now(UTC)
access_expires_on = cur_time + relativedelta(hours=1)
payload = {
"iat": timegm(cur_time.timetuple()),
"exp": timegm(access_expires_on.timetuple()),
"challenge": b64enc(sha256(SECRET.encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'),
"origin_ref": ORIGIN_REF,
"key_ref": "00000000-0000-0000-0000-000000000000",
"kid": "00000000-0000-0000-0000-000000000000"
}
payload = {
"auth_code": jwt.encode(payload, key=jwt_encode_key, headers={'kid': payload.get('kid')}, algorithm=ALGORITHMS.RS256),
"code_verifier": SECRET,
}
response = client.post('/auth/v1/token', json=payload)
assert response.status_code == 200
token = response.json().get('auth_token')
payload = jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
assert payload.get('origin_ref') == ORIGIN_REF
def test_leasing_v1_lessor():
payload = {
'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': [ALLOTMENT_REF]
}
response = client.post('/leasing/v1/lessor', json=payload, headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200
lease_result_list = response.json().get('lease_result_list')
assert len(lease_result_list) == 1
assert len(lease_result_list[0]['lease']['ref']) == 36
assert str(UUID(lease_result_list[0]['lease']['ref'])) == lease_result_list[0]['lease']['ref']
return lease_result_list[0]['lease']['ref']
def test_leasing_v1_lessor_lease():
response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200
active_lease_list = response.json().get('active_lease_list')
assert len(active_lease_list) == 1
assert len(active_lease_list[0]) == 36
assert str(UUID(active_lease_list[0])) == active_lease_list[0]
def test_leasing_v1_lease_renew():
response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
active_lease_list = response.json().get('active_lease_list')
active_lease_ref = active_lease_list[0]
###
response = client.put(f'/leasing/v1/lease/{active_lease_ref}', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200
lease_ref = response.json().get('lease_ref')
assert len(lease_ref) == 36
assert lease_ref == active_lease_ref
def test_leasing_v1_lease_delete():
response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
active_lease_list = response.json().get('active_lease_list')
active_lease_ref = active_lease_list[0]
###
response = client.delete(f'/leasing/v1/lease/{active_lease_ref}', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200
lease_ref = response.json().get('lease_ref')
assert len(lease_ref) == 36
assert lease_ref == active_lease_ref
def test_leasing_v1_lessor_lease_remove():
lease_ref = test_leasing_v1_lessor()
response = client.delete('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200
released_lease_list = response.json().get('released_lease_list')
assert len(released_lease_list) == 1
assert len(released_lease_list[0]) == 36
assert released_lease_list[0] == lease_ref

View File

@ -1 +0,0 @@
VERSION=0.6