From 0dc1f3ce0ac49d401b65d608239e29ecd7553647 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Thu, 30 Nov 2023 15:34:45 +0100 Subject: [PATCH] add new dynamic endpoint for executing playbooks --- lso/__init__.py | 7 ++++--- lso/playbook.py | 11 +++++++--- lso/routes/default.py | 2 +- lso/routes/playbook.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 lso/routes/playbook.py diff --git a/lso/__init__.py b/lso/__init__.py index 746cd00..6c3d936 100644 --- a/lso/__init__.py +++ b/lso/__init__.py @@ -6,7 +6,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from lso import config, environment -from lso.routes import default, ip_trunk, router +from lso.routes import default, ip_trunk, playbook, router def create_app() -> FastAPI: @@ -25,14 +25,15 @@ def create_app() -> FastAPI: ) app.include_router(default.router, prefix="/api") + app.include_router(playbook.router, prefix="/api/playbook") app.include_router(router.router, prefix="/api/router") app.include_router(ip_trunk.router, prefix="/api/ip_trunk") # test that config params are loaded and available config.load() - logging.info("FastAPI app initialized") - environment.setup_logging() + logging.info("FastAPI app initialized") + return app diff --git a/lso/playbook.py b/lso/playbook.py index 51320f3..a5e24c8 100644 --- a/lso/playbook.py +++ b/lso/playbook.py @@ -176,12 +176,17 @@ def _run_playbook_proc( logger.error(msg) -def run_playbook(playbook_path: Path, extra_vars: dict, inventory: str, callback: HttpUrl) -> PlaybookLaunchResponse: +def run_playbook( + playbook_path: Path, + extra_vars: dict[str, Any], + inventory: dict[str, Any] | str, + callback: HttpUrl, +) -> PlaybookLaunchResponse: """Run an Ansible playbook against a specified inventory. :param Path playbook_path: playbook to be executed. - :param dict extra_vars: Any extra vars needed for the playbook to run. - :param [str] inventory: The inventory that the playbook is executed against. + :param dict[str, Any] extra_vars: Any extra vars needed for the playbook to run. + :param dict[str, Any] | str inventory: The inventory that the playbook is executed against. :param :class:`HttpUrl` callback: Callback URL where the playbook should send a status update when execution is completed. This is used for workflow-orchestrator to continue with the next step in a workflow. :return: Result of playbook launch, this could either be successful or unsuccessful. diff --git a/lso/routes/default.py b/lso/routes/default.py index d6f7292..25c74ee 100644 --- a/lso/routes/default.py +++ b/lso/routes/default.py @@ -8,7 +8,7 @@ from importlib import metadata from fastapi import APIRouter from pydantic import BaseModel, constr -API_VERSION = "0.1" +API_VERSION = "0.2" VersionString = constr(pattern=r"\d+\.\d+") router = APIRouter() diff --git a/lso/routes/playbook.py b/lso/routes/playbook.py new file mode 100644 index 0000000..670848e --- /dev/null +++ b/lso/routes/playbook.py @@ -0,0 +1,46 @@ +"""The API endpoint from which Ansible playbooks can be executed.""" + +from typing import Any + +from fastapi import APIRouter +from pydantic import BaseModel, HttpUrl + +from lso.playbook import PlaybookLaunchResponse, get_playbook_path, run_playbook + +router = APIRouter() + + +class PlaybookRunParams(BaseModel): + """Parameters for executing an Ansible playbook.""" + + #: The filename of a playbook that is executed. It should be present inside the directory defined in the + #: configuration option ``ansible_playbooks_root_dir``. + playbook_name: str + #: The address where LSO should call back to upon completion. + callback: HttpUrl + #: The inventory to run the playbook against. This inventory can also include any host vars, if needed. When + #: including host vars, it should be a dictionary. Can be a simple string containing hostnames when no host vars are + #: needed. In the latter case, multiple hosts should be separated with a newline character. + inventory: dict[str, Any] | str + #: Extra variables that should get passed to the playbook. This includes any required configuration objects + #: from the workflow orchestrator, commit comments, whether this execution should be a dry run, a trouble ticket + #: number, etc. Which extra vars are required solely depends on what inputs the playbook requires. + extra_vars: dict + + +@router.post("/") +def run_playbook_endpoint(params: PlaybookRunParams) -> PlaybookLaunchResponse: + """Launch an Ansible playbook to modify or deploy a subscription instance. + + The response will contain either a job ID, or error information. + + :param params :class:`PlaybookRunParams`: Parameters for executing a playbook. + :return: Response from the Ansible runner, including a run ID. + :rtype: :class:`lso.playbook.PlaybookLaunchResponse` + """ + return run_playbook( + playbook_path=get_playbook_path(params.playbook_name), + extra_vars=params.extra_vars, + inventory=params.inventory, + callback=params.callback, + ) -- GitLab