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!
This commit is contained in:
parent
35fc5ea6b0
commit
c79455b84d
@ -95,6 +95,7 @@ logging.basicConfig(format='[{levelname:^7}] [{module:^15}] {message}', style='{
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(LOG_LEVEL)
|
logger.setLevel(LOG_LEVEL)
|
||||||
logging.getLogger('util').setLevel(LOG_LEVEL)
|
logging.getLogger('util').setLevel(LOG_LEVEL)
|
||||||
|
logging.getLogger('NV').setLevel(LOG_LEVEL)
|
||||||
|
|
||||||
|
|
||||||
# Helper
|
# Helper
|
||||||
|
@ -5,6 +5,8 @@ from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_
|
|||||||
from sqlalchemy.engine import Engine
|
from sqlalchemy.engine import Engine
|
||||||
from sqlalchemy.orm import sessionmaker, declarative_base
|
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||||
|
|
||||||
|
from util import NV
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +25,8 @@ class Origin(Base):
|
|||||||
return f'Origin(origin_ref={self.origin_ref}, hostname={self.hostname})'
|
return f'Origin(origin_ref={self.origin_ref}, hostname={self.hostname})'
|
||||||
|
|
||||||
def serialize(self) -> dict:
|
def serialize(self) -> dict:
|
||||||
|
_ = NV().find(self.guest_driver_version)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'origin_ref': self.origin_ref,
|
'origin_ref': self.origin_ref,
|
||||||
# 'service_instance_xid': self.service_instance_xid,
|
# 'service_instance_xid': self.service_instance_xid,
|
||||||
@ -30,6 +34,7 @@ class Origin(Base):
|
|||||||
'guest_driver_version': self.guest_driver_version,
|
'guest_driver_version': self.guest_driver_version,
|
||||||
'os_platform': self.os_platform,
|
'os_platform': self.os_platform,
|
||||||
'os_version': self.os_version,
|
'os_version': self.os_version,
|
||||||
|
'$driver': _ if _ is not None else None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
44
app/util.py
44
app/util.py
@ -36,3 +36,47 @@ def generate_key() -> "RsaKey":
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.debug(f'Generating RSA-Key')
|
log.debug(f'Generating RSA-Key')
|
||||||
return RSA.generate(bits=2048)
|
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
|
||||||
|
137
test/create_driver_matrix_json.py
Normal file
137
test/create_driver_matrix_json.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
URL = 'https://docs.nvidia.com/grid/'
|
||||||
|
|
||||||
|
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': 'DriverVersions'})
|
||||||
|
tables = data.findAll('table')
|
||||||
|
for table in tables:
|
||||||
|
# parse software-branch (e.g. "vGPU software 17 Releases" and remove " Releases" for "matrix_key")
|
||||||
|
software_branch = table.parent.find_previous_sibling('button', {'class': 'accordion'}).text.strip()
|
||||||
|
software_branch = software_branch.replace(' Releases', '')
|
||||||
|
matrix_key = software_branch.lower()
|
||||||
|
|
||||||
|
# driver version info from table-heads (ths) and table-rows (trs)
|
||||||
|
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': 'AllReleaseBranches'})
|
||||||
|
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')
|
Loading…
Reference in New Issue
Block a user