diff --git a/lso/__init__.py b/lso/__init__.py index 746cd003a67a9418f9493ac989e391178f93e896..6c3d936488ab4a5d4717642c7d5043552b4d4454 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 51320f386f073f44129a2f85fa805400a8b7a834..a5e24c844df853a1207504830121fdef80cd7b9b 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 d6f7292b47ab2eb44bd89e6addcdc8a412648146..25c74eee7db2b3047f267ef867ff88d5a0fe77e1 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 0000000000000000000000000000000000000000..670848e438d61abde2634de0a899d476a9c602d4 --- /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, + )