Compare commits

..

4 Commits

5 changed files with 105 additions and 53 deletions

View File

@ -292,7 +292,8 @@ After first success you have to replace `--issue` with `--renew`.
| `DATABASE` | `sqlite:///db.sqlite` | See [official SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/core/engines.html) | | `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 | | `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 | | `SITE_KEY_XID` | `00000000-0000-0000-0000-000000000000` | Site identification uuid |
| `INSTANCE_REF` | `00000000-0000-0000-0000-000000000000` | Instance 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 | | `INSTANCE_KEY_RSA` | `<app-dir>/cert/instance.private.pem` | Site-wide private RSA key for singing JWTs |
| `INSTANCE_KEY_PUB` | `<app-dir>/cert/instance.public.pem` | Site-wide public key | | `INSTANCE_KEY_PUB` | `<app-dir>/cert/instance.public.pem` | Site-wide public key |

View File

@ -32,10 +32,12 @@ app = FastAPI(title='FastAPI-DLS', description='Minimal Delegated License Servic
db = create_engine(str(env('DATABASE', 'sqlite:///db.sqlite'))) db = create_engine(str(env('DATABASE', 'sqlite:///db.sqlite')))
db_init(db), migrate(db) db_init(db), migrate(db)
# everything prefixed with "INSTANCE_*" is used as "SERVICE_INSTANCE_*" or "SI_*" in official dls service
DLS_URL = str(env('DLS_URL', 'localhost')) DLS_URL = str(env('DLS_URL', 'localhost'))
DLS_PORT = int(env('DLS_PORT', '443')) DLS_PORT = int(env('DLS_PORT', '443'))
SITE_KEY_XID = str(env('SITE_KEY_XID', '00000000-0000-0000-0000-000000000000')) SITE_KEY_XID = str(env('SITE_KEY_XID', '00000000-0000-0000-0000-000000000000'))
INSTANCE_REF = str(env('INSTANCE_REF', '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_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')))) INSTANCE_KEY_PUB = load_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__), 'cert/instance.public.pem'))))
TOKEN_EXPIRE_DELTA = relativedelta(hours=1) # days=1 TOKEN_EXPIRE_DELTA = relativedelta(hours=1) # days=1
@ -89,6 +91,7 @@ async def _config():
'DLS_PORT': str(DLS_PORT), 'DLS_PORT': str(DLS_PORT),
'SITE_KEY_XID': str(SITE_KEY_XID), 'SITE_KEY_XID': str(SITE_KEY_XID),
'INSTANCE_REF': str(INSTANCE_REF), 'INSTANCE_REF': str(INSTANCE_REF),
'ALLOTMENT_REF': [ALLOTMENT_REF],
'TOKEN_EXPIRE_DELTA': str(TOKEN_EXPIRE_DELTA), 'TOKEN_EXPIRE_DELTA': str(TOKEN_EXPIRE_DELTA),
'LEASE_EXPIRE_DELTA': str(LEASE_EXPIRE_DELTA), 'LEASE_EXPIRE_DELTA': str(LEASE_EXPIRE_DELTA),
'LEASE_RENEWAL_PERIOD': str(LEASE_RENEWAL_PERIOD), 'LEASE_RENEWAL_PERIOD': str(LEASE_RENEWAL_PERIOD),
@ -191,7 +194,7 @@ async def _client_token():
"nbf": timegm(cur_time.timetuple()), "nbf": timegm(cur_time.timetuple()),
"exp": timegm(exp_time.timetuple()), "exp": timegm(exp_time.timetuple()),
"update_mode": "ABSOLUTE", "update_mode": "ABSOLUTE",
"scope_ref_list": [str(uuid4())], # this is our LEASE_REF "scope_ref_list": [ALLOTMENT_REF],
"fulfillment_class_ref_list": [], "fulfillment_class_ref_list": [],
"service_instance_configuration": { "service_instance_configuration": {
"nls_service_instance_ref": INSTANCE_REF, "nls_service_instance_ref": INSTANCE_REF,
@ -224,26 +227,25 @@ async def _client_token():
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py # 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', description='find or create an origin') @app.post('/auth/v1/origin', description='find or create an origin')
async def auth_v1_origin(request: Request): async def auth_v1_origin(request: Request):
j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow()
origin_ref = j['candidate_origin_ref'] origin_ref = j.get('candidate_origin_ref')
logging.info(f'> [ origin ]: {origin_ref}: {j}') logging.info(f'> [ origin ]: {origin_ref}: {j}')
data = Origin( data = Origin(
origin_ref=origin_ref, origin_ref=origin_ref,
hostname=j['environment']['hostname'], hostname=j.get('environment').get('hostname'),
guest_driver_version=j['environment']['guest_driver_version'], guest_driver_version=j.get('environment').get('guest_driver_version'),
os_platform=j['environment']['os_platform'], os_version=j['environment']['os_version'], os_platform=j.get('environment').get('os_platform'), os_version=j.get('environment').get('os_version'),
) )
Origin.create_or_update(db, data) Origin.create_or_update(db, data)
response = { response = {
"origin_ref": origin_ref, "origin_ref": origin_ref,
"environment": j['environment'], "environment": j.get('environment'),
"svc_port_set_list": None, "svc_port_set_list": None,
"node_url_list": None, "node_url_list": None,
"node_query_order": None, "node_query_order": None,
@ -255,25 +257,24 @@ async def auth_v1_origin(request: Request):
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py # venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py
# { "environment" : { "guest_driver_version" : "guest_driver_version", "hostname" : "myhost", "ip_address_list" : [ "192.168.1.129" ], "os_version" : "os_version", "os_platform" : "os_platform", "fingerprint" : { "mac_address_list" : [ "e4:b9:7a:e5:7b:ff" ] }, "host_driver_version" : "host_driver_version" }, "origin_ref" : "00112233-4455-6677-8899-aabbccddeeff" }
@app.post('/auth/v1/origin/update', description='update an origin evidence') @app.post('/auth/v1/origin/update', description='update an origin evidence')
async def auth_v1_origin_update(request: Request): async def auth_v1_origin_update(request: Request):
j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow()
origin_ref = j['origin_ref'] origin_ref = j.get('origin_ref')
logging.info(f'> [ update ]: {origin_ref}: {j}') logging.info(f'> [ update ]: {origin_ref}: {j}')
data = Origin( data = Origin(
origin_ref=origin_ref, origin_ref=origin_ref,
hostname=j['environment']['hostname'], hostname=j.get('environment').get('hostname'),
guest_driver_version=j['environment']['guest_driver_version'], guest_driver_version=j.get('environment').get('guest_driver_version'),
os_platform=j['environment']['os_platform'], os_version=j['environment']['os_version'], os_platform=j.get('environment').get('os_platform'), os_version=j.get('environment').get('os_version'),
) )
Origin.create_or_update(db, data) Origin.create_or_update(db, data)
response = { response = {
"environment": j['environment'], "environment": j.get('environment'),
"prompts": None, "prompts": None,
"sync_timestamp": cur_time.isoformat() "sync_timestamp": cur_time.isoformat()
} }
@ -283,12 +284,11 @@ async def auth_v1_origin_update(request: Request):
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py # 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 # 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', description='get an authorization code') @app.post('/auth/v1/code', description='get an authorization code')
async def auth_v1_code(request: Request): async def auth_v1_code(request: Request):
j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow()
origin_ref = j['origin_ref'] origin_ref = j.get('origin_ref')
logging.info(f'> [ code ]: {origin_ref}: {j}') logging.info(f'> [ code ]: {origin_ref}: {j}')
delta = relativedelta(minutes=15) delta = relativedelta(minutes=15)
@ -297,8 +297,8 @@ async def auth_v1_code(request: Request):
payload = { payload = {
'iat': timegm(cur_time.timetuple()), 'iat': timegm(cur_time.timetuple()),
'exp': timegm(expires.timetuple()), 'exp': timegm(expires.timetuple()),
'challenge': j['code_challenge'], 'challenge': j.get('code_challenge'),
'origin_ref': j['origin_ref'], 'origin_ref': j.get('origin_ref'),
'key_ref': SITE_KEY_XID, 'key_ref': SITE_KEY_XID,
'kid': SITE_KEY_XID 'kid': SITE_KEY_XID
} }
@ -316,17 +316,16 @@ async def auth_v1_code(request: Request):
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py # 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 # venv/lib/python3.9/site-packages/nls_core_auth/auth.py - TokenResponse
# {"auth_code":"...","code_verifier":"..."}
@app.post('/auth/v1/token', description='exchange auth code and verifier for token') @app.post('/auth/v1/token', description='exchange auth code and verifier for token')
async def auth_v1_token(request: Request): async def auth_v1_token(request: Request):
j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow() j, cur_time = json.loads((await request.body()).decode('utf-8')), datetime.utcnow()
payload = jwt.decode(token=j['auth_code'], key=jwt_decode_key) payload = jwt.decode(token=j.get('auth_code'), key=jwt_decode_key)
origin_ref = payload['origin_ref'] origin_ref = payload.get('origin_ref')
logging.info(f'> [ auth ]: {origin_ref}: {j}') logging.info(f'> [ auth ]: {origin_ref}: {j}')
# validate the code challenge # validate the code challenge
if payload['challenge'] != b64enc(sha256(j['code_verifier'].encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'): if payload.get('challenge') != b64enc(sha256(j.get('code_verifier').encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'):
raise HTTPException(status_code=401, detail='expected challenge did not match verifier') raise HTTPException(status_code=401, detail='expected challenge did not match verifier')
access_expires_on = cur_time + TOKEN_EXPIRE_DELTA access_expires_on = cur_time + TOKEN_EXPIRE_DELTA
@ -354,23 +353,26 @@ async def auth_v1_token(request: Request):
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py # venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
# {'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', description='request multiple leases (borrow) for current origin') @app.post('/leasing/v1/lessor', description='request multiple leases (borrow) for current origin')
async def leasing_v1_lessor(request: Request): async def leasing_v1_lessor(request: Request):
j, token, cur_time = json.loads((await request.body()).decode('utf-8')), __get_token(request), datetime.utcnow() j, token, cur_time = json.loads((await request.body()).decode('utf-8')), __get_token(request), datetime.utcnow()
origin_ref = token.get('origin_ref') origin_ref = token.get('origin_ref')
scope_ref_list = j['scope_ref_list'] scope_ref_list = j.get('scope_ref_list')
logging.info(f'> [ create ]: {origin_ref}: create leases for scope_ref_list {scope_ref_list}') logging.info(f'> [ create ]: {origin_ref}: create leases for scope_ref_list {scope_ref_list}')
lease_result_list = [] lease_result_list = []
for scope_ref in scope_ref_list: for scope_ref in scope_ref_list:
if scope_ref not in [ALLOTMENT_REF]:
raise HTTPException(status_code=500, detail=f'no service instances found for scopes: ["{scope_ref}"]')
lease_ref = str(uuid4())
expires = cur_time + LEASE_EXPIRE_DELTA expires = cur_time + LEASE_EXPIRE_DELTA
lease_result_list.append({ lease_result_list.append({
"ordinal": 0, "ordinal": 0,
# https://docs.nvidia.com/license-system/latest/nvidia-license-system-user-guide/index.html # https://docs.nvidia.com/license-system/latest/nvidia-license-system-user-guide/index.html
"lease": { "lease": {
"ref": scope_ref, "ref": lease_ref,
"created": cur_time.isoformat(), "created": cur_time.isoformat(),
"expires": expires.isoformat(), "expires": expires.isoformat(),
"recommended_lease_renewal": LEASE_RENEWAL_PERIOD, "recommended_lease_renewal": LEASE_RENEWAL_PERIOD,
@ -379,7 +381,7 @@ async def leasing_v1_lessor(request: Request):
} }
}) })
data = Lease(origin_ref=origin_ref, lease_ref=scope_ref, lease_created=cur_time, lease_expires=expires) data = Lease(origin_ref=origin_ref, scope_ref=scope_ref, lease_ref=lease_ref, lease_created=cur_time, lease_expires=expires)
Lease.create_or_update(db, data) Lease.create_or_update(db, data)
response = { response = {
@ -429,7 +431,7 @@ async def leasing_v1_lease_renew(request: Request, lease_ref: str):
response = { response = {
"lease_ref": lease_ref, "lease_ref": lease_ref,
"expires": expires.isoformat(), "expires": expires.isoformat(),
"recommended_lease_renewal": 0.16, "recommended_lease_renewal": LEASE_RENEWAL_PERIOD,
"offline_lease": True, "offline_lease": True,
"prompts": None, "prompts": None,
"sync_timestamp": cur_time.isoformat(), "sync_timestamp": cur_time.isoformat(),
@ -491,7 +493,7 @@ async def leasing_v1_lessor_lease_remove(request: Request):
async def leasing_v1_lessor_shutdown(request: Request): async def leasing_v1_lessor_shutdown(request: Request):
j, cur_time = json.loads((await request.body()).decode('utf-8')) j, cur_time = json.loads((await request.body()).decode('utf-8'))
token = j['token'] token = j.get('token')
token = jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False}) token = jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
origin_ref = token.get('origin_ref') origin_ref = token.get('origin_ref')

View File

@ -72,17 +72,19 @@ class Lease(Base):
lease_ref = Column(CHAR(length=36), primary_key=True, nullable=False, index=True) # uuid4 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 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
lease_created = Column(DATETIME(), nullable=False) lease_created = Column(DATETIME(), nullable=False)
lease_expires = Column(DATETIME(), nullable=False) lease_expires = Column(DATETIME(), nullable=False)
lease_updated = Column(DATETIME(), nullable=False) lease_updated = Column(DATETIME(), nullable=False)
def __repr__(self): def __repr__(self):
return f'Lease(origin_ref={self.origin_ref}, lease_ref={self.lease_ref}, expires={self.lease_expires})' return f'Lease(origin_ref={self.origin_ref}, scope_ref={self.scope_ref}, lease_ref={self.lease_ref}, expires={self.lease_expires})'
def serialize(self) -> dict: def serialize(self) -> dict:
return { return {
'lease_ref': self.lease_ref, 'lease_ref': self.lease_ref,
'origin_ref': self.origin_ref, 'origin_ref': self.origin_ref,
'scope_ref': self.scope_ref,
'lease_created': self.lease_created.isoformat(), 'lease_created': self.lease_created.isoformat(),
'lease_expires': self.lease_expires.isoformat(), 'lease_expires': self.lease_expires.isoformat(),
'lease_updated': self.lease_updated.isoformat(), 'lease_updated': self.lease_updated.isoformat(),
@ -178,4 +180,14 @@ def migrate(engine: Engine):
Lease.__table__.drop(bind=engine) Lease.__table__.drop(bind=engine)
init(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_0_to_1_1()
upgrade_1_2_to_1_3()

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

@ -3,7 +3,7 @@ from hashlib import sha256
from calendar import timegm from calendar import timegm
from datetime import datetime from datetime import datetime
from os.path import dirname, join from os.path import dirname, join
from uuid import uuid4 from uuid import uuid4, UUID
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from jose import jwt, jwk from jose import jwt, jwk
@ -20,8 +20,7 @@ from app.util import load_key
client = TestClient(main.app) client = TestClient(main.app)
ORIGIN_REF, LEASE_REF = str(uuid4()), str(uuid4()) ORIGIN_REF, ALLOTMENT_REF, SECRET = str(uuid4()), '20000000-0000-0000-0000-000000000001', 'HelloWorld'
SECRET = "HelloWorld"
# INSTANCE_KEY_RSA = generate_key() # INSTANCE_KEY_RSA = generate_key()
# INSTANCE_KEY_PUB = INSTANCE_KEY_RSA.public_key() # INSTANCE_KEY_PUB = INSTANCE_KEY_RSA.public_key()
@ -44,11 +43,10 @@ def test_index():
assert response.status_code == 200 assert response.status_code == 200
def test_health(): def test_health():
response = client.get('/-/health') response = client.get('/-/health')
assert response.status_code == 200 assert response.status_code == 200
assert response.json()['status'] == 'up' assert response.json().get('status') == 'up'
def test_config(): def test_config():
@ -105,7 +103,7 @@ def test_auth_v1_origin():
response = client.post('/auth/v1/origin', json=payload) response = client.post('/auth/v1/origin', json=payload)
assert response.status_code == 200 assert response.status_code == 200
assert response.json()['origin_ref'] == ORIGIN_REF assert response.json().get('origin_ref') == ORIGIN_REF
def auth_v1_origin_update(): def auth_v1_origin_update():
@ -126,7 +124,7 @@ def auth_v1_origin_update():
response = client.post('/auth/v1/origin/update', json=payload) response = client.post('/auth/v1/origin/update', json=payload)
assert response.status_code == 200 assert response.status_code == 200
assert response.json()['origin_ref'] == ORIGIN_REF assert response.json().get('origin_ref') == ORIGIN_REF
def test_auth_v1_code(): def test_auth_v1_code():
@ -138,8 +136,8 @@ def test_auth_v1_code():
response = client.post('/auth/v1/code', json=payload) response = client.post('/auth/v1/code', json=payload)
assert response.status_code == 200 assert response.status_code == 200
payload = jwt.get_unverified_claims(token=response.json()['auth_code']) payload = jwt.get_unverified_claims(token=response.json().get('auth_code'))
assert payload['origin_ref'] == ORIGIN_REF assert payload.get('origin_ref') == ORIGIN_REF
def test_auth_v1_token(): def test_auth_v1_token():
@ -163,9 +161,9 @@ def test_auth_v1_token():
response = client.post('/auth/v1/token', json=payload) response = client.post('/auth/v1/token', json=payload)
assert response.status_code == 200 assert response.status_code == 200
token = response.json()['auth_token'] token = response.json().get('auth_token')
payload = jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False}) payload = jwt.decode(token=token, key=jwt_decode_key, algorithms=ALGORITHMS.RS256, options={'verify_aud': False})
assert payload['origin_ref'] == ORIGIN_REF assert payload.get('origin_ref') == ORIGIN_REF
def test_leasing_v1_lessor(): def test_leasing_v1_lessor():
@ -178,46 +176,59 @@ def test_leasing_v1_lessor():
'product': {'name': 'NVIDIA RTX Virtual Workstation'} 'product': {'name': 'NVIDIA RTX Virtual Workstation'}
}], }],
'proposal_evaluation_mode': 'ALL_OF', 'proposal_evaluation_mode': 'ALL_OF',
'scope_ref_list': [LEASE_REF] 'scope_ref_list': [ALLOTMENT_REF]
} }
response = client.post('/leasing/v1/lessor', json=payload, headers={'authorization': __bearer_token(ORIGIN_REF)}) response = client.post('/leasing/v1/lessor', json=payload, headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200 assert response.status_code == 200
lease_result_list = response.json()['lease_result_list'] lease_result_list = response.json().get('lease_result_list')
assert len(lease_result_list) == 1 assert len(lease_result_list) == 1
assert lease_result_list[0]['lease']['ref'] == LEASE_REF 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(): def test_leasing_v1_lessor_lease():
response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)}) response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200 assert response.status_code == 200
active_lease_list = response.json()['active_lease_list'] active_lease_list = response.json().get('active_lease_list')
assert len(active_lease_list) == 1 assert len(active_lease_list) == 1
assert active_lease_list[0] == LEASE_REF assert str(UUID(active_lease_list[0])) == active_lease_list[0]
def test_leasing_v1_lease_renew(): def test_leasing_v1_lease_renew():
response = client.put(f'/leasing/v1/lease/{LEASE_REF}', headers={'authorization': __bearer_token(ORIGIN_REF)}) response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
active_lease_list = response.json().get('active_lease_list')
lease_ref = active_lease_list[0]
###
response = client.put(f'/leasing/v1/lease/{lease_ref}', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200 assert response.status_code == 200
assert response.json()['lease_ref'] == LEASE_REF assert response.json().get('lease_ref') == lease_ref
def test_leasing_v1_lease_delete(): def test_leasing_v1_lease_delete():
response = client.delete(f'/leasing/v1/lease/{LEASE_REF}', headers={'authorization': __bearer_token(ORIGIN_REF)}) response = client.get('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
active_lease_list = response.json().get('active_lease_list')
lease_ref = active_lease_list[0]
###
response = client.delete(f'/leasing/v1/lease/{lease_ref}', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200 assert response.status_code == 200
assert response.json()['lease_ref'] == LEASE_REF assert response.json().get('lease_ref') == lease_ref
def test_leasing_v1_lessor_lease_remove(): def test_leasing_v1_lessor_lease_remove():
test_leasing_v1_lessor() lease_ref = test_leasing_v1_lessor()
response = client.delete('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)}) response = client.delete('/leasing/v1/lessor/leases', headers={'authorization': __bearer_token(ORIGIN_REF)})
assert response.status_code == 200 assert response.status_code == 200
released_lease_list = response.json()['released_lease_list'] released_lease_list = response.json().get('released_lease_list')
assert len(released_lease_list) == 1 assert len(released_lease_list) == 1
assert released_lease_list[0] == LEASE_REF assert released_lease_list[0] == lease_ref