From 525e246514a1e20ecdc6249627fe2bfab8ff062b Mon Sep 17 00:00:00 2001 From: Bill Peck Date: Wed, 31 Jan 2024 15:49:00 -0500 Subject: [PATCH] WIP: swagger yaml defination Using openapi 3.0 specification to define the restAPI for beaker backend. skeleton api with some dummy methods to allow for testing the test framework. Once this is fleshed out we can start migrating the model code over and then create the actual rest methods. --- API/requirements.txt | 31 + API/setup.cfg | 14 + API/setup.py | 2 + API/src/bkr/api/__init__.py | 0 API/src/bkr/api/arches.py | 23 + API/src/bkr/api/distros/__init__.py | 14 + API/src/bkr/api/distros/tags.py | 14 + API/src/bkr/api/health.py | 3 + API/src/bkr/api/jobs/__init__.py | 14 + API/src/bkr/api/jobs/actions/cancel.py | 2 + API/src/bkr/api/jobs/sets/__init__.py | 14 + API/src/bkr/api/jobs/sets/actions/cancel.py | 2 + API/src/bkr/api/jobs/sets/recipes.py | 8 + API/src/bkr/api/lab_controllers/__init__.py | 34 + .../lab_controllers/distro_trees/__init__.py | 14 + .../lab_controllers/distro_trees/images.py | 14 + .../api/lab_controllers/distro_trees/repos.py | 14 + API/src/bkr/api/osmajors.py | 23 + API/src/bkr/api/osversions.py | 23 + API/src/bkr/api/systems/__init__.py | 78 ++ API/src/bkr/api/systems/access_policies.py | 11 + API/src/bkr/api/systems/actions/off.py | 2 + API/src/bkr/api/systems/actions/on.py | 2 + .../bkr/api/systems/actions/request_loan.py | 2 + API/src/bkr/api/systems/actions/reset.py | 2 + API/src/bkr/api/systems/activity.py | 11 + API/src/bkr/api/systems/cc.py | 8 + API/src/bkr/api/systems/commands.py | 11 + API/src/bkr/api/systems/excluded_families.py | 11 + API/src/bkr/api/systems/executed_recipes.py | 11 + API/src/bkr/api/systems/install_options.py | 11 + API/src/bkr/api/systems/loan.py | 14 + API/src/bkr/api/systems/notes.py | 11 + API/src/bkr/api/systems/problem_reports.py | 11 + API/src/bkr/api/systems/reservation.py | 14 + API/src/bkr/api/systems/status.py | 11 + API/src/bkr/api/systems/system_keys.py | 11 + API/src/bkr/api/templates/home.html | 14 + API/src/bkr/app.py | 18 + API/src/bkr/config.py | 29 + API/src/bkr/settings.py | 41 + API/src/bkr/swagger.yml | 1210 +++++++++++++++++ API/tests/conftest.py | 14 + API/tests/settings.py | 3 + API/tests/test_api.py | 4 + API/tests/test_systems.py | 30 + 46 files changed, 1858 insertions(+) create mode 100644 API/requirements.txt create mode 100644 API/setup.cfg create mode 100644 API/setup.py create mode 100644 API/src/bkr/api/__init__.py create mode 100644 API/src/bkr/api/arches.py create mode 100644 API/src/bkr/api/distros/__init__.py create mode 100644 API/src/bkr/api/distros/tags.py create mode 100644 API/src/bkr/api/health.py create mode 100644 API/src/bkr/api/jobs/__init__.py create mode 100644 API/src/bkr/api/jobs/actions/cancel.py create mode 100644 API/src/bkr/api/jobs/sets/__init__.py create mode 100644 API/src/bkr/api/jobs/sets/actions/cancel.py create mode 100644 API/src/bkr/api/jobs/sets/recipes.py create mode 100644 API/src/bkr/api/lab_controllers/__init__.py create mode 100644 API/src/bkr/api/lab_controllers/distro_trees/__init__.py create mode 100644 API/src/bkr/api/lab_controllers/distro_trees/images.py create mode 100644 API/src/bkr/api/lab_controllers/distro_trees/repos.py create mode 100644 API/src/bkr/api/osmajors.py create mode 100644 API/src/bkr/api/osversions.py create mode 100644 API/src/bkr/api/systems/__init__.py create mode 100644 API/src/bkr/api/systems/access_policies.py create mode 100644 API/src/bkr/api/systems/actions/off.py create mode 100644 API/src/bkr/api/systems/actions/on.py create mode 100644 API/src/bkr/api/systems/actions/request_loan.py create mode 100644 API/src/bkr/api/systems/actions/reset.py create mode 100644 API/src/bkr/api/systems/activity.py create mode 100644 API/src/bkr/api/systems/cc.py create mode 100644 API/src/bkr/api/systems/commands.py create mode 100644 API/src/bkr/api/systems/excluded_families.py create mode 100644 API/src/bkr/api/systems/executed_recipes.py create mode 100644 API/src/bkr/api/systems/install_options.py create mode 100644 API/src/bkr/api/systems/loan.py create mode 100644 API/src/bkr/api/systems/notes.py create mode 100644 API/src/bkr/api/systems/problem_reports.py create mode 100644 API/src/bkr/api/systems/reservation.py create mode 100644 API/src/bkr/api/systems/status.py create mode 100644 API/src/bkr/api/systems/system_keys.py create mode 100644 API/src/bkr/api/templates/home.html create mode 100644 API/src/bkr/app.py create mode 100644 API/src/bkr/config.py create mode 100644 API/src/bkr/settings.py create mode 100644 API/src/bkr/swagger.yml create mode 100644 API/tests/conftest.py create mode 100644 API/tests/settings.py create mode 100644 API/tests/test_api.py create mode 100644 API/tests/test_systems.py diff --git a/API/requirements.txt b/API/requirements.txt new file mode 100644 index 000000000..abcf00bb8 --- /dev/null +++ b/API/requirements.txt @@ -0,0 +1,31 @@ +attrs==23.1.0 +blinker==1.6.3 +certifi==2023.7.22 +charset-normalizer==3.3.0 +click==8.1.7 +clickclick==20.10.2 +connexion==3.0.5 +Flask==2.2.2 +flask-marshmallow==0.14.0 +Flask-SQLAlchemy==3.0.3 +greenlet==3.0.0 +idna==3.4 +inflection==0.5.1 +itsdangerous==2.1.2 +Jinja2==3.1.2 +jsonschema==4.19.1 +jsonschema-specifications==2023.7.1 +MarkupSafe==2.1.3 +marshmallow==3.20.1 +marshmallow-sqlalchemy==0.29.0 +packaging==23.2 +PyYAML==6.0.1 +referencing==0.30.2 +requests==2.31.0 +rpds-py==0.10.3 +six==1.16.0 +SQLAlchemy==2.0.22 +swagger-ui-bundle==0.0.9 +typing_extensions==4.8.0 +urllib3==2.0.6 +Werkzeug==2.2.2 diff --git a/API/setup.cfg b/API/setup.cfg new file mode 100644 index 000000000..f8f7f0e70 --- /dev/null +++ b/API/setup.cfg @@ -0,0 +1,14 @@ +[metadata] +name = bkr.api + +[options] +package_dir= + =src +packages = find: +zip_safe = False +python_requires = >= 3 +[options.packages.find] +where = src +exclude = + tests* + .gitignore diff --git a/API/setup.py b/API/setup.py new file mode 100644 index 000000000..8bf1ba938 --- /dev/null +++ b/API/setup.py @@ -0,0 +1,2 @@ +from setuptools import setup +setup() diff --git a/API/src/bkr/api/__init__.py b/API/src/bkr/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/API/src/bkr/api/arches.py b/API/src/bkr/api/arches.py new file mode 100644 index 000000000..9c4799acf --- /dev/null +++ b/API/src/bkr/api/arches.py @@ -0,0 +1,23 @@ +from flask import abort + +ARCHES = {} + +def post(arch): + if arch and arch not in ARCHES: + ARCHES[arch] = { + "arch": arch, + } + return ARCHES[arch], 201 + else: + abort( + 406, + f"{arch} already exists", + ) + +def search(offset=0, limit=None): + start = 0 + end = len(ARCHES.values()) + if offset and limit: + start = offset * limit + end = start + limit + return list(ARCHES.values())[start:end] diff --git a/API/src/bkr/api/distros/__init__.py b/API/src/bkr/api/distros/__init__.py new file mode 100644 index 000000000..fa3c432e6 --- /dev/null +++ b/API/src/bkr/api/distros/__init__.py @@ -0,0 +1,14 @@ +def post(distro): + pass + +def search(offset=0, limit=None): + pass + +def get(id): + pass + +def put(id): + pass + +def delete(id): + pass diff --git a/API/src/bkr/api/distros/tags.py b/API/src/bkr/api/distros/tags.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/distros/tags.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/health.py b/API/src/bkr/api/health.py new file mode 100644 index 000000000..866d7fbb3 --- /dev/null +++ b/API/src/bkr/api/health.py @@ -0,0 +1,3 @@ +def search(): + return {'msg': 'ok'}, 200 + diff --git a/API/src/bkr/api/jobs/__init__.py b/API/src/bkr/api/jobs/__init__.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/jobs/__init__.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/jobs/actions/cancel.py b/API/src/bkr/api/jobs/actions/cancel.py new file mode 100644 index 000000000..9ef985142 --- /dev/null +++ b/API/src/bkr/api/jobs/actions/cancel.py @@ -0,0 +1,2 @@ +def put(): + pass diff --git a/API/src/bkr/api/jobs/sets/__init__.py b/API/src/bkr/api/jobs/sets/__init__.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/jobs/sets/__init__.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/jobs/sets/actions/cancel.py b/API/src/bkr/api/jobs/sets/actions/cancel.py new file mode 100644 index 000000000..9ef985142 --- /dev/null +++ b/API/src/bkr/api/jobs/sets/actions/cancel.py @@ -0,0 +1,2 @@ +def put(): + pass diff --git a/API/src/bkr/api/jobs/sets/recipes.py b/API/src/bkr/api/jobs/sets/recipes.py new file mode 100644 index 000000000..d8c35a1bf --- /dev/null +++ b/API/src/bkr/api/jobs/sets/recipes.py @@ -0,0 +1,8 @@ +def search(): + pass + +def get(): + pass + +def patch(): + pass diff --git a/API/src/bkr/api/lab_controllers/__init__.py b/API/src/bkr/api/lab_controllers/__init__.py new file mode 100644 index 000000000..f0cb14fbf --- /dev/null +++ b/API/src/bkr/api/lab_controllers/__init__.py @@ -0,0 +1,34 @@ +from flask import abort + +LABCONTROLLERS = {} + +def post(lab_controller): + fqdn = lab_controller.get("fqdn") + user_name = lab_controller.get("user_name", "") + email_address = lab_controller.get("email_address", "") + password = lab_controller.get("password", "") + + if fqdn and fqdn not in LABCONTROLLERS: + LABCONTROLLERS[fqdn] = { + "fqdn": fqdn, + "user_name": user_name, + "email_address": email_address, + "password": password, + } + return LABCONTROLLERS[fqdn], 201 + else: + abort( + 406, + f"Lab Controller with fqdn {fqdn} already exists", + ) + +def search(offset=0, limit=None): + start = 0 + end = len(LABCONTROLLERS.values()) + if offset and limit: + start = offset * limit + end = start + limit + return list(LABCONTROLLERS.values())[start:end] + +def get(lab_controller): + pass diff --git a/API/src/bkr/api/lab_controllers/distro_trees/__init__.py b/API/src/bkr/api/lab_controllers/distro_trees/__init__.py new file mode 100644 index 000000000..b9817a55d --- /dev/null +++ b/API/src/bkr/api/lab_controllers/distro_trees/__init__.py @@ -0,0 +1,14 @@ +def post(distro_tree): + pass + +def search(offset=0, limit=None): + pass + +def get(id): + pass + +def put(id): + pass + +def delete(id): + pass diff --git a/API/src/bkr/api/lab_controllers/distro_trees/images.py b/API/src/bkr/api/lab_controllers/distro_trees/images.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/lab_controllers/distro_trees/images.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/lab_controllers/distro_trees/repos.py b/API/src/bkr/api/lab_controllers/distro_trees/repos.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/lab_controllers/distro_trees/repos.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/osmajors.py b/API/src/bkr/api/osmajors.py new file mode 100644 index 000000000..9c4799acf --- /dev/null +++ b/API/src/bkr/api/osmajors.py @@ -0,0 +1,23 @@ +from flask import abort + +ARCHES = {} + +def post(arch): + if arch and arch not in ARCHES: + ARCHES[arch] = { + "arch": arch, + } + return ARCHES[arch], 201 + else: + abort( + 406, + f"{arch} already exists", + ) + +def search(offset=0, limit=None): + start = 0 + end = len(ARCHES.values()) + if offset and limit: + start = offset * limit + end = start + limit + return list(ARCHES.values())[start:end] diff --git a/API/src/bkr/api/osversions.py b/API/src/bkr/api/osversions.py new file mode 100644 index 000000000..9c4799acf --- /dev/null +++ b/API/src/bkr/api/osversions.py @@ -0,0 +1,23 @@ +from flask import abort + +ARCHES = {} + +def post(arch): + if arch and arch not in ARCHES: + ARCHES[arch] = { + "arch": arch, + } + return ARCHES[arch], 201 + else: + abort( + 406, + f"{arch} already exists", + ) + +def search(offset=0, limit=None): + start = 0 + end = len(ARCHES.values()) + if offset and limit: + start = offset * limit + end = start + limit + return list(ARCHES.values())[start:end] diff --git a/API/src/bkr/api/systems/__init__.py b/API/src/bkr/api/systems/__init__.py new file mode 100644 index 000000000..0bf1fd9d3 --- /dev/null +++ b/API/src/bkr/api/systems/__init__.py @@ -0,0 +1,78 @@ +from flask import abort +from bkr.api.lab_controllers import LABCONTROLLERS +from bkr.api.arches import ARCHES +#from bkr.model import System + +SYSTEMS = {} + +def post(system): + fqdn = system.get("fqdn") + owner = system.get("owner") + status = system.get("status", "unavailable") + status_reason = system.get("status_reason", "") + arches = system.get("arches", []) + power = system.get("power", {}) + location = system.get("location", "") + lender = system.get("lender", "") + vender = system.get("vender", "") + model = system.get("model", "") + serial = system.get("serial", "") + lab_controller = system.get("lab_controller", "") + + if lab_controller and lab_controller not in LABCONTROLLERS: + abort( + 406, + f"Lab Controller {lab_controller} doesn't exist", + ) + + for arch in arches: + if arch not in ARCHES: + abort( + 406, + f"{arch} doesn't exist, create it first.", + ) + + if fqdn and fqdn not in SYSTEMS: + SYSTEMS[fqdn] = { + "fqdn": fqdn, + "owner": owner, + "status": status, + "status_reason": status_reason, + "arches": arches, + "power": power, + "location": location, + "lender": lender, + "vender": vender, + "model": model, + "serial": serial, + "lab_controller": lab_controller, + } + return SYSTEMS[fqdn], 201 + else: + abort( + 406, + f"System with fqdn {fqdn} already exists", + ) + +def search(offset=0, limit=None): + start = 0 + end = len(SYSTEMS.values()) + if offset and limit: + start = offset * limit + end = start + limit + return list(SYSTEMS.values())[start:end] + +def get(fqdn): + if fqdn in SYSTEMS: + return SYSTEMS[fqdn] + else: + abort( + 404, + f"System with fqdn {fqdn} not found", + ) + +def put(fqdn): + pass + +def delete(fqdn): + pass diff --git a/API/src/bkr/api/systems/access_policies.py b/API/src/bkr/api/systems/access_policies.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/access_policies.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/actions/off.py b/API/src/bkr/api/systems/actions/off.py new file mode 100644 index 000000000..9ef985142 --- /dev/null +++ b/API/src/bkr/api/systems/actions/off.py @@ -0,0 +1,2 @@ +def put(): + pass diff --git a/API/src/bkr/api/systems/actions/on.py b/API/src/bkr/api/systems/actions/on.py new file mode 100644 index 000000000..9ef985142 --- /dev/null +++ b/API/src/bkr/api/systems/actions/on.py @@ -0,0 +1,2 @@ +def put(): + pass diff --git a/API/src/bkr/api/systems/actions/request_loan.py b/API/src/bkr/api/systems/actions/request_loan.py new file mode 100644 index 000000000..9ef985142 --- /dev/null +++ b/API/src/bkr/api/systems/actions/request_loan.py @@ -0,0 +1,2 @@ +def put(): + pass diff --git a/API/src/bkr/api/systems/actions/reset.py b/API/src/bkr/api/systems/actions/reset.py new file mode 100644 index 000000000..9ef985142 --- /dev/null +++ b/API/src/bkr/api/systems/actions/reset.py @@ -0,0 +1,2 @@ +def put(): + pass diff --git a/API/src/bkr/api/systems/activity.py b/API/src/bkr/api/systems/activity.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/activity.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/cc.py b/API/src/bkr/api/systems/cc.py new file mode 100644 index 000000000..90441970d --- /dev/null +++ b/API/src/bkr/api/systems/cc.py @@ -0,0 +1,8 @@ +def search(): + pass + +def post(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/commands.py b/API/src/bkr/api/systems/commands.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/commands.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/excluded_families.py b/API/src/bkr/api/systems/excluded_families.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/excluded_families.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/executed_recipes.py b/API/src/bkr/api/systems/executed_recipes.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/executed_recipes.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/install_options.py b/API/src/bkr/api/systems/install_options.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/install_options.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/loan.py b/API/src/bkr/api/systems/loan.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/systems/loan.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/notes.py b/API/src/bkr/api/systems/notes.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/notes.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/problem_reports.py b/API/src/bkr/api/systems/problem_reports.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/problem_reports.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/reservation.py b/API/src/bkr/api/systems/reservation.py new file mode 100644 index 000000000..d0f51589d --- /dev/null +++ b/API/src/bkr/api/systems/reservation.py @@ -0,0 +1,14 @@ +def search(): + pass + +def post(): + pass + +def put(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/status.py b/API/src/bkr/api/systems/status.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/status.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/systems/system_keys.py b/API/src/bkr/api/systems/system_keys.py new file mode 100644 index 000000000..59fcafba8 --- /dev/null +++ b/API/src/bkr/api/systems/system_keys.py @@ -0,0 +1,11 @@ +def search(): + pass + +def post(): + pass + +def get(): + pass + +def delete(): + pass diff --git a/API/src/bkr/api/templates/home.html b/API/src/bkr/api/templates/home.html new file mode 100644 index 000000000..3b9572830 --- /dev/null +++ b/API/src/bkr/api/templates/home.html @@ -0,0 +1,14 @@ + + + + + + + Flask REST API + + +

+ Hello, World! +

+ + diff --git a/API/src/bkr/app.py b/API/src/bkr/app.py new file mode 100644 index 000000000..e980f40bf --- /dev/null +++ b/API/src/bkr/app.py @@ -0,0 +1,18 @@ +import logging +import sys +import time +from connexion.resolver import RestyResolver +from bkr import config + +logger = logging.getLogger(__name__) + + +def create_app(): + app = config.connex_app + app.add_api("swagger.yml", resolver=RestyResolver('bkr.api')) + return app + + +if __name__ == "__main__": + app = create_app() + app.run(host="0.0.0.0", port=8000) diff --git a/API/src/bkr/config.py b/API/src/bkr/config.py new file mode 100644 index 000000000..27d12266b --- /dev/null +++ b/API/src/bkr/config.py @@ -0,0 +1,29 @@ +import os +import pathlib + +import sqlalchemy +import connexion +from flask_sqlalchemy import SQLAlchemy +from flask_marshmallow import Marshmallow + + +basedir = pathlib.Path(__file__).parent.resolve() +connex_app = connexion.App(__name__, specification_dir=basedir) + +app = connex_app.app +app.config.from_object("bkr.settings") +app.config.from_object(os.environ.get("BKR_SETTINGS_MODULE")) + +config = app.config +db = SQLAlchemy(app) +ma = Marshmallow(app) + + +def get_engine(db_uri): + return sqlalchemy.create_engine( + db_uri, + pool_size=app.config["SQLALCHEMY_POOL_SIZE"], + max_overflow=app.config["SQLALCHEMY_MAX_OVERFLOW"], + encoding="utf8", + ) + diff --git a/API/src/bkr/settings.py b/API/src/bkr/settings.py new file mode 100644 index 000000000..8b6827e21 --- /dev/null +++ b/API/src/bkr/settings.py @@ -0,0 +1,41 @@ +# Global parameters about the API itself +# +import os + +HOST = os.getenv("API_HOST", "127.0.0.1") +PORT = int(os.getenv("API_PORT", "5000")) +DEBUG = True +JSONIFY_PRETTYPRINT_REGULAR = False + +# Database (SQLAlchemy) related parameters +# +DB_USER = os.getenv("DB_USER", "bkr") +DB_PASSWORD = os.getenv("DB_PASSWORD", "bkr") +DB_HOST = os.getenv("DB_HOST", "127.0.0.1") +DB_PORT = int(os.getenv("DB_PORT", "5432")) +DB_NAME = os.getenv("DB_NAME", "beaker") +DEFAULT_SQLALCHEMY_DATABASE_URI = ( + "postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}".format( + db_user=DB_USER, + db_password=DB_PASSWORD, + db_host=DB_HOST, + db_port=DB_PORT, + db_name=DB_NAME, + ) +) +SQLALCHEMY_DATABASE_URI = os.getenv( + "SQLALCHEMY_DATABASE_URI", DEFAULT_SQLALCHEMY_DATABASE_URI +) + +# The following two lines will output the SQL statements +# executed by SQLAlchemy. Useful while debugging and in +# development. Turned off by default +# -------- +SQLALCHEMY_ECHO = False +SQLALCHEMY_NATIVE_UNICODE = True +SQLALCHEMY_POOL_SIZE = 5 +SQLALCHEMY_MAX_OVERFLOW = 25 + +# Logging related parameters +LOG_LEVEL = "INFO" +LOG_FORMAT = "[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s" diff --git a/API/src/bkr/swagger.yml b/API/src/bkr/swagger.yml new file mode 100644 index 000000000..c5d26c04e --- /dev/null +++ b/API/src/bkr/swagger.yml @@ -0,0 +1,1210 @@ +openapi: 3.0.0 +servers: + - description: Beaker API + url: /api +info: + version: "1.0.0" + title: Sample Application Project + description: >- + Sample Beaker API. +paths: + /health: + get: + tags: [Health] + description: Health Check + responses: + '200': + description: Status message from server describing current health + /systems: + get: + tags: + - Systems + description: >- + All the systems registered in Inventory + parameters: + - $ref: '#/components/parameters/PageLimit' + - $ref: '#/components/parameters/PageOffset' + responses: + "200": + description: "Successfully read systems list" + content: + application/json: + schema: + type: "array" + items: + $ref: "#/components/schemas/System" + application/xml: + schema: + type: "array" + xml: + name: systems + items: + $ref: "#/components/schemas/System" + post: + tags: + - Systems + description: "Create a System" + requestBody: + x-body-name: "system" + description: "System to create" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/System" + examples: + SystemExample: + $ref: '#/components/examples/System' + responses: + "201": + description: "Successfully created System" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}: + get: + tags: + - Systems + description: Obtain information about a system + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system" + '400': + $ref: '#/components/responses/400Error' + put: + tags: + - Systems + description: "Update a System" + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully updated System" + '201': + description: "Successfully created System" + '204': + description: "No changes for System" + '400': + $ref: '#/components/responses/400Error' + requestBody: + x-body-name: "system" + description: "System to update" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/System" + examples: + SystemExample: + $ref: '#/components/examples/System' + delete: + tags: + - Systems + description: "Delete a System" + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + "204": + description: "Successfully deleted System" + /systems/{fqdn}/cc: + get: + tags: + - Systems + description: Obtain information about a system CC entries + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system CC entries" + '400': + $ref: '#/components/responses/400Error' + post: + tags: + - Systems + description: "Add cc email to system" + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "email" + description: "System to create" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/EMAIL" + examples: + SystemExample: + $ref: '#/components/examples/EMAIL' + responses: + "201": + description: "Successfully created CC entry for System" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/cc/{email}: + delete: + tags: + - Systems + description: Delete a system CC entry + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/EMAIL' + responses: + '204': + description: "Successfully deleted system CC entry" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/problem-reports: + get: + tags: + - Systems + description: Obtain information about a system problem reports + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system problem reports" + '400': + $ref: '#/components/responses/400Error' + post: + tags: + - Systems + description: Add problem report to system + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "problem_report" + description: "Problem report to create" + required: True + content: + application/json: + schema: + type: "object" + required: + - message + properties: + message: + type: "string" + examples: + reportProblemExample: + value: + message: >- + This system is not powering on and is failing to netboot. + responses: + "201": + description: "Successfully created problem report entry for System" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/problem-reports/{id}: + delete: + tags: + - Systems + description: Delete a system problem report entry + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '204': + description: "Successfully deleted system problem report entry" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/problem-reports/{id}: + get: + tags: + - Systems + description: Obtain information about a system problem report + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a system problem report" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/reservation: + put: + tags: + - Systems + description: "Reserve system" + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "reserve" + description: "Reserve a system" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/EMAIL" + examples: + SystemExample: + $ref: '#/components/examples/EMAIL' + responses: + "201": + description: "Successfully Reserved System" + '400': + $ref: '#/components/responses/400Error' + get: + tags: + - Systems + description: Obtain information about a system reservation + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system reservation" + '400': + $ref: '#/components/responses/400Error' + delete: + tags: + - Systems + description: Delete a system reservation + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '204': + description: "Successfully deleted system reservation" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/loan: + put: + tags: + - Systems + description: "A loan gives exclusive access to a system" + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "loan" + description: "Loan a system" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/EMAIL" + examples: + SystemExample: + $ref: '#/components/examples/EMAIL' + responses: + "201": + description: "Successfully Reserved System" + '400': + $ref: '#/components/responses/400Error' + get: + tags: + - Systems + description: Obtain information about a system loan + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system loan" + '400': + $ref: '#/components/responses/400Error' + delete: + tags: + - Systems + description: Delete a system loan + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '204': + description: "Successfully deleted system loan" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/access_policies: + post: + tags: + - Systems + description: Add an access policy to a system + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "access_policy" + description: "Access policy to create" + required: True + content: + application/json: + schema: + type: "object" + required: + - message + properties: + message: + type: "string" + examples: + reportProblemExample: + value: + message: >- + This system is not powering on and is failing to netboot. + responses: + "201": + description: "Successfully created access policy entry for System" + '400': + $ref: '#/components/responses/400Error' + get: + tags: + - Systems + description: Obtain information about a system access policies + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system access policies" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/access_policies/{id}: + get: + tags: + - Systems + description: Obtain information about a system access policy + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a system access policy" + '400': + $ref: '#/components/responses/400Error' + delete: + tags: + - Systems + description: Delete a system access policy + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '204': + description: "Successfully deleted system access policy" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/status: + get: + tags: + - Systems + description: Obtain information about a system status + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system status" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/actions/on: + put: + tags: + - Systems + description: Power on system + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Action Power On was Successful" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/actions/off: + put: + tags: + - Systems + description: Power off system + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Action Power Off was Successful" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/actions/reset: + put: + tags: + - Systems + description: Power reset system + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Action Power Reset was Successful" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/actions/request-loan: + put: + tags: + - Systems + description: Request loan for system + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Loan request was successful" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/notes: + get: + tags: + - Systems + description: Obtain information about a system notes + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system notes" + '400': + $ref: '#/components/responses/400Error' + post: + tags: + - Systems + description: Add note to a system + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "note" + description: "Note to add" + required: True + content: + application/json: + schema: + type: "object" + required: + - note + properties: + note: + type: "string" + examples: + addSystemNoteExample: + value: + message: >- + Some additional info about this system. + responses: + "201": + description: "Successfully created note entry for System" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/notes/{id}: + get: + tags: + - Systems + description: Obtain information about a system note + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a system note" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/activity: + get: + tags: + - Systems + description: Obtain information about a system activity + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system activity" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/executed-recipes: + get: + tags: + - Systems + description: Obtain information about a system executed recipes + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system executed recipes" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/install-options: + get: + tags: + - Systems + description: Obtain information about a system install options + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system install options" + '400': + $ref: '#/components/responses/400Error' + post: + tags: + - Systems + description: Add install option to a system + parameters: + - $ref: '#/components/parameters/FQDN' + requestBody: + x-body-name: "install_option" + description: "Install option to add" + required: True + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/InstallOptions' + - $ref: '#/components/schemas/InstallOptionsOSMajor' + - $ref: '#/components/schemas/InstallOptionsOSVersion' + examples: + addInstallOptionExample: + value: + osMajor: "RedHatEnterpriseLinux8" + ks_meta: "--ignore-disk=sda" + kernel_options: "console=ttyS1" + kernel_options_post: "" + responses: + "201": + description: "Successfully created install_option for System" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/install-options/{id}: + get: + tags: + - Systems + description: Obtain information about a system install option + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a system install option" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/excluded-families: + get: + tags: + - Systems + description: Obtain information about a system excluded families + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system excluded families" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/excluded-families/{id}: + get: + tags: + - Systems + description: Obtain information about a system excluded family + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a system excluded family" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/system-keys: + get: + tags: + - Systems + description: Obtain information about a system keys + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a system keys" + '400': + $ref: '#/components/responses/400Error' + /systems/{fqdn}/system-keys/{id}: + get: + tags: + - Systems + description: Obtain information about a system key + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a system key" + /arches: + get: + tags: + - Arches + description: >- + All the arches registered in Inventory + responses: + '200': + description: "Successfully read arch list" + /osmajors: + get: + tags: + - OSMajor + description: >- + All the OS Major versions registered in Inventory + responses: + '200': + description: "Successfully read osmajor list" + /osversions: + get: + tags: + - OSVersions + description: >- + All the OS Versions registered in Inventory + responses: + '200': + description: "Successfully read osmajor list" + /distros: + get: + tags: + - Distros + description: >- + All the Distros registered in Inventory + responses: + '200': + description: "Successfully read distro list" + /distros/{id}: + get: + tags: + - Distros + description: Obtain information about a Distro + parameters: + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned a Distro" + '400': + $ref: '#/components/responses/400Error' + /distros/{id}/tags: + get: + tags: + - Distros + description: >- + All the tags for this distro + parameters: + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully read tag list for this distro" + /lab_controllers: + get: + tags: + - Lab-Controllers + description: >- + All the Lab Controllers registered in Inventory + security: [] + parameters: + - $ref: '#/components/parameters/PageLimit' + - $ref: '#/components/parameters/PageOffset' + responses: + '200': + description: "Successfully read lab controller list" + content: + application/json: + schema: + type: object + properties: + fqdn: + type: string + post: + tags: + - Lab-Controllers + description: "Create a Lab Controller" + requestBody: + x-body-name: "lab_controller" + description: "Lab Controller to create" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/LabController" + examples: + SystemExample: + $ref: '#/components/examples/LabController' + responses: + "201": + description: "Successfully created Lab Controller" + '400': + $ref: '#/components/responses/400Error' + /lab_controllers/{fqdn}: + get: + tags: + - Lab-Controllers + description: Obtain information about a lab controller + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a lab controller" + '400': + $ref: '#/components/responses/400Error' + /lab_controllers/{fqdn}/distro_trees: + get: + tags: + - Lab-Controllers + description: Obtain information about a lab controller distro trees + parameters: + - $ref: '#/components/parameters/FQDN' + responses: + '200': + description: "Successfully returned a lab controller distro trees" + '400': + $ref: '#/components/responses/400Error' + /lab_controllers/{fqdn}/distro_trees/{id}/repos: + get: + tags: + - Lab-Controllers + description: Obtain information about a distro tree repos + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned distro tree repos" + '400': + $ref: '#/components/responses/400Error' + /lab_controllers/{fqdn}/distro_trees/{id}/images: + get: + tags: + - Lab-Controllers + description: Obtain information about a distro tree images + parameters: + - $ref: '#/components/parameters/FQDN' + - $ref: '#/components/parameters/ID' + responses: + '200': + description: "Successfully returned distro tree images" + '400': + $ref: '#/components/responses/400Error' + /jobs: + get: + tags: + - Jobs + description: >- + All the Jobs + security: [] + parameters: + - $ref: '#/components/parameters/PageLimit' + - $ref: '#/components/parameters/PageOffset' + responses: + '200': + description: "Successfully read jobs list" + content: + application/json: + schema: + type: "array" + items: + $ref: "#/components/schemas/Job" + application/xml: + schema: + type: "array" + xml: + name: jobs + items: + $ref: "#/components/schemas/Job" + post: + tags: + - Jobs + description: "Create a Job" + requestBody: + x-body-name: "job" + description: "Job to create" + required: True + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + examples: + SystemExample: + $ref: '#/components/examples/Job' + application/xml: + schema: + $ref: "#/components/schemas/Job" + examples: + SystemExample: + $ref: '#/components/examples/Job' + responses: + "201": + description: "Successfully created a Job" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}: + get: + tags: + - Jobs + description: Obtain information about a specific job + parameters: + - $ref: '#/components/parameters/JID' + responses: + '200': + description: "Successfully returned Job details" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}/actions/cancel: + put: + tags: + - Jobs + description: Cancel a specific job + parameters: + - $ref: '#/components/parameters/JID' + responses: + '200': + description: "Successfully cancelled Job" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}/sets: + get: + tags: + - Jobs + description: Obtain information about a specific job's sets + parameters: + - $ref: '#/components/parameters/JID' + responses: + '200': + description: "Successfully returned Job set's details" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}/sets/{sID}/actions/cancel: + put: + tags: + - Jobs + description: Cancel a specific set of Recipes + parameters: + - $ref: '#/components/parameters/JID' + - $ref: '#/components/parameters/SID' + responses: + '200': + description: "Successfully cancelled set of Recipes" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}/sets/{sID}/recipes: + get: + tags: + - Jobs + description: Obtain information about a specific job's sets recipes + parameters: + - $ref: '#/components/parameters/JID' + - $ref: '#/components/parameters/SID' + responses: + '200': + description: "Successfully returned Job set's details" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}/sets/{sID}/recipes/{rID}: + get: + tags: + - Jobs + description: Obtain information about a specific recipe + parameters: + - $ref: '#/components/parameters/JID' + - $ref: '#/components/parameters/SID' + - $ref: '#/components/parameters/RID' + responses: + '200': + description: "Successfully returned recipe details" + '400': + $ref: '#/components/responses/400Error' + /jobs/{jID}/sets/{sID}/recipes/{rID}: + patch: + tags: + - Jobs + description: Update the status / result of a specific recipe + parameters: + - $ref: '#/components/parameters/JID' + - $ref: '#/components/parameters/SID' + - $ref: '#/components/parameters/RID' + responses: + '200': + description: "Successfully updated recipe details" + '400': + $ref: '#/components/responses/400Error' + +components: + parameters: + PageLimit: + name: limit + in: query + description: Limits the number of items on a page + schema: + type: integer + examples: + limit-example: + value: 100 + PageOffset: + name: offset + in: query + description: Specifies the page number of the items to be displayed + schema: + type: integer + examples: + offset-example: + value: 0 + FQDN: + name: "fqdn" + description: "Fully qualified domain name of system to get" + in: path + required: True + schema: + type: "string" + examples: + fqdn-example: + value: "host.example.com" + ID: + name: "id" + description: "id of the record to get" + in: path + required: True + schema: + type: "string" + examples: + id-example: + value: "1262" + JID: + name: "jID" + description: "id of the job to get" + in: path + required: True + schema: + type: "string" + examples: + id-example: + value: "1262" + SID: + name: "sID" + description: "id of the set to get" + in: path + required: True + schema: + type: "string" + examples: + id-example: + value: "1262" + RID: + name: "rID" + description: "id of the recipe to get" + in: path + required: True + schema: + type: "string" + examples: + id-example: + value: "1262" + EMAIL: + name: "email" + description: "Email address to include in Carbon Copy (CC)" + in: path + required: True + schema: + type: "string" + examples: + email-example: + value: "user@example.com" + schemas: + EMAIL: + type: "object" + required: + - email + properties: + email: + type: "string" + LabController: + type: "object" + required: + - fqdn + properties: + fqdn: + type: "string" + user_name: + type: "string" + email_address: + type: "string" + password: + type: "string" + removed: + type: "boolean" + disabled: + type: "boolean" + Job: + type: "object" + required: + - sets + properties: + status: + type: "string" + whiteboard: + type: "string" + sets: + type: "array" + items: + type: "object" + properties: + status: + type: "string" + recipes: + type: "array" + items: + $ref: "#/components/schemas/Recipe" + Recipe: + type: "object" + required: + - distro_requires + properties: + status: + type: "string" + whiteboard: + type: "string" + host_requires: + type: "string" + distro_requires: + type: "string" + ks_meta: + type: "string" + kernel_options: + type: "string" + kernel_options_post: + type: "string" + System: + type: "object" + required: + - fqdn + properties: + fqdn: + type: "string" + owner: + type: "string" + status: + type: "string" + status_reason: + type: "string" + arches: + type: "array" + items: + type: "string" + power: + type: "object" + properties: + power_type: + type: "string" + power_address: + type: "string" + power_user: + type: "string" + power_password: + type: "string" + power_id: + type: "string" + power_quiescent_period: + type: "integer" + release_action: + type: "string" + reprovision_distro_tree: + type: "string" + location: + type: "string" + lender: + type: "string" + vender: + type: "string" + model: + type: "string" + serial: + type: "string" + lab_controller: + type: "string" + InstallOptions: + type: "object" + properties: + ks_meta: + type: "string" + kernel_options: + type: "string" + kernel_options_post: + type: "string" + InstallOptionsOSMajor: + type: "object" + properties: + osMajor: + type: "string" + ks_meta: + type: "string" + kernel_options: + type: "string" + kernel_options_post: + type: "string" + InstallOptionsOSVersion: + type: "object" + properties: + osVersion: + type: "string" + ks_meta: + type: "string" + kernel_options: + type: "string" + kernel_options_post: + type: "string" + responses: + 400Error: + description: Invalid request + content: + application/json: + schema: + type: object + properties: + message: + type: string + examples: + EMAIL: + value: + email: "user@example.com" + LabController: + value: + fqdn: "lab1.example.com" + user_name: "host/labctrl" + email_address: "labctrl@beaker-server.localdomain" + password: "labctrl" + removed: "false" + disabled: "false" + System: + value: + fqdn: "host.example.com" + owner: "user@fedora.com" + status: "available" + status_reason: "" + arches: + - "x86_64" + power: + power_type: "ipmi" + power_address: "127.0.0.1" + power_user: "admin" + power_password: "admin" + power_id: "6231" + power_quiescent_period: 60 + release_action: "PowerOff" + reprovision_distro_tree: "" + location: "Westford, MA" + lender: "IBM" + vender: "IBM" + model: "Z230" + serial: "A12345678" + lab_controller: "lab1.example.com" + Job: + value: + whiteboard: "This is an example Job" + sets: + - recipes: + - whiteboard: This is first set recipe 1 of 2 + ks_meta: "" + kernel_options: "console=ttys1" + kernel_options_post: "selinux=0" + distro_requires: "" + host_requires: "" + - whiteboard: This is first set recipe 2 of 2 + ks_meta: "" + kernel_options: "console=ttys1" + kernel_options_post: "" + distro_requires: "" + host_requires: "" + - recipes: + - whiteboard: This is second set recipe 1 of 2 + ks_meta: "--ignore-disk=sda" + kernel_options: "console=ttys1" + kernel_options_post: "" + distro_requires: "" + host_requires: + - whiteboard: This is second set recipe 2 of 2 + ks_meta: "" + kernel_options: "console=ttys1" + kernel_options_post: "" + distro_requires: "" + host_requires: "" + diff --git a/API/tests/conftest.py b/API/tests/conftest.py new file mode 100644 index 000000000..2eb2969f6 --- /dev/null +++ b/API/tests/conftest.py @@ -0,0 +1,14 @@ +import pytest +from bkr.app import create_app + +app = create_app() + +@pytest.fixture() +def client(): + with app.test_client() as c: + yield c + + +@pytest.fixture() +def runner(): + return app.test_cli_runner() diff --git a/API/tests/settings.py b/API/tests/settings.py new file mode 100644 index 000000000..848c0d52b --- /dev/null +++ b/API/tests/settings.py @@ -0,0 +1,3 @@ +DEBUG = False + +LOG_FILE = "/dev/null" diff --git a/API/tests/test_api.py b/API/tests/test_api.py new file mode 100644 index 000000000..aeed604e3 --- /dev/null +++ b/API/tests/test_api.py @@ -0,0 +1,4 @@ +def test_health(client): + response = client.get('/api/health') + assert response.status_code == 200 + diff --git a/API/tests/test_systems.py b/API/tests/test_systems.py new file mode 100644 index 000000000..fd9dea157 --- /dev/null +++ b/API/tests/test_systems.py @@ -0,0 +1,30 @@ +def test_lab_controller_create(client): + response = client.post("/api/lab_controllers", json={ + "fqdn": "lab1.example.com" + }) + assert response.json()["fqdn"] == "lab1.example.com" + +def test_system_create(client): + response = client.post("/api/systems", json={ + "fqdn": "host1.example.com", + "location": "Westford, MA", + "lender": "IBM", + "vender": "IBM", + "model": "Z230", + "serial": "A12345678", + "lab_controller": "lab1.example.com" + }) + assert response.json()["fqdn"] == "host1.example.com" + assert response.json()["status"] == "unavailable" + +def test_system_fail(client): + response = client.post("/api/systems", json={ + "fqdn": "host4.example.com", + "location": "Westford, MA", + "lender": "IBM", + "vender": "IBM", + "model": "Z230", + "serial": "A12345678", + "lab_controller": "nolab.example.com" + }) + assert response.status_code == 406