Skip to content
Snippets Groups Projects
Verified Commit b46fd0d5 authored by Karel van Klink's avatar Karel van Klink :smiley_cat:
Browse files

replace PlaybookLaunchResponse with a generic JSONResponse

parent 1278d734
No related branches found
No related tags found
No related merge requests found
......@@ -4,7 +4,6 @@ import json
import logging
import threading
import uuid
from enum import StrEnum, auto
from pathlib import Path
from typing import Any
......@@ -12,8 +11,9 @@ import ansible_runner
import requests
import xmltodict
from dictdiffer import diff
from fastapi import Response, status
from pydantic import BaseModel, HttpUrl
from fastapi import status
from fastapi.responses import JSONResponse
from pydantic import HttpUrl
from lso import config
from lso.config import DEFAULT_REQUEST_TIMEOUT
......@@ -21,53 +21,29 @@ from lso.config import DEFAULT_REQUEST_TIMEOUT
logger = logging.getLogger(__name__)
class PlaybookJobStatus(StrEnum):
"""Enumerator for status codes of a playbook job that's running."""
#: All is well.
OK = auto()
#: An error has occurred.
ERROR = auto()
class PlaybookLaunchResponse(BaseModel):
"""Running a playbook gives this response.
:param PlaybookJobStatus status:
:param job_id:
:type job_id: str, optional
:param info:
:type info: str, optional
"""
#: Status of a Playbook job.
status: PlaybookJobStatus
#: The ID assigned to a job.
job_id: str = ""
#: Information on a job.
info: str = ""
def get_playbook_path(playbook_name: str) -> Path:
"""Get the path of a playbook on the local filesystem."""
config_params = config.load()
return Path(config_params.ansible_playbooks_root_dir) / playbook_name
def playbook_launch_success(job_id: str) -> PlaybookLaunchResponse:
def playbook_launch_success(job_id: str) -> JSONResponse:
"""Return a :class:`PlaybookLaunchResponse` for the successful start of a playbook execution.
:return PlaybookLaunchResponse: A playbook launch response that's successful.
:return JSONResponse: A playbook launch response that's successful.
"""
return PlaybookLaunchResponse(status=PlaybookJobStatus.OK, job_id=job_id)
return JSONResponse(content={"job_id": job_id}, status_code=status.HTTP_201_CREATED)
def playbook_launch_error(reason: str) -> PlaybookLaunchResponse:
def playbook_launch_error(reason: str, status_code: int = status.HTTP_400_BAD_REQUEST) -> JSONResponse:
"""Return a :class:`PlaybookLaunchResponse` for the erroneous start of a playbook execution.
:return PlaybookLaunchResponse: A playbook launch response that's unsuccessful.
:param str reason: The reason why a request has failed.
:param status status_code: The HTTP status code that should be associated with this request. Defaults to HTTP 400
"Bad request".
:return JSONResponse: A playbook launch response that's unsuccessful.
"""
return PlaybookLaunchResponse(status=PlaybookJobStatus.ERROR, info=reason)
return JSONResponse(content={"error": reason}, status_code=status_code)
def _process_json_output(runner: ansible_runner.Runner) -> list[dict[Any, Any]]: # ignore: C901
......@@ -151,7 +127,7 @@ def _run_playbook_proc(
:param str job_id: Identifier of the job that's executed.
:param str playbook_path: Ansible playbook to be executed.
:param dict extra_vars: Extra variables passed to the Ansible playbook.
:param str callback: Callback URL to PUT to when execution is completed.
:param str callback: Callback URL to return output to when execution is completed.
:param dict[str, Any] | str inventory: Ansible inventory to run the playbook against.
"""
ansible_playbook_run = ansible_runner.run(
......@@ -181,8 +157,7 @@ def run_playbook(
extra_vars: dict[str, Any],
inventory: dict[str, Any] | str,
callback: HttpUrl,
response: Response,
) -> PlaybookLaunchResponse:
) -> JSONResponse:
"""Run an Ansible playbook against a specified inventory.
:param Path playbook_path: playbook to be executed.
......@@ -191,17 +166,15 @@ def run_playbook(
: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.
:rtype: :class:`PlaybookLaunchResponse`
:rtype: :class:`fastapi.responses.JSONResponse`
"""
if not Path.exists(playbook_path):
response.status_code = status.HTTP_404_NOT_FOUND
msg = f"Filename '{playbook_path}' does not exist."
return playbook_launch_error(msg)
return playbook_launch_error(reason=msg, status_code=status.HTTP_404_NOT_FOUND)
if not ansible_runner.utils.isinventory(inventory):
response.status_code = status.HTTP_400_BAD_REQUEST
msg = "Invalid inventory provided. Should be a string, or JSON object."
return playbook_launch_error(msg)
return playbook_launch_error(reason=msg, status_code=status.HTTP_400_BAD_REQUEST)
job_id = str(uuid.uuid4())
thread = threading.Thread(
......
"""Routes for handling events related to the IP trunk service."""
from fastapi import APIRouter, Response
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel, HttpUrl
from lso.playbook import PlaybookLaunchResponse, get_playbook_path, run_playbook
from lso.playbook import get_playbook_path, run_playbook
router = APIRouter()
......@@ -72,7 +73,7 @@ class IPTrunkDeleteParams(IPTrunkParams):
@router.post("/")
def provision_ip_trunk(params: IPTrunkProvisioningParams, response: Response) -> PlaybookLaunchResponse:
def provision_ip_trunk(params: IPTrunkProvisioningParams) -> JSONResponse:
"""Launch a playbook to provision a new IP trunk service.
The response will contain either a job ID, or error information.
......@@ -80,7 +81,6 @@ def provision_ip_trunk(params: IPTrunkProvisioningParams, response: Response) ->
:param params: The parameters that define the new subscription object that
is to be deployed.
:type params: :class:`IPTrunkProvisioningParams`
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
"""
......@@ -100,17 +100,15 @@ def provision_ip_trunk(params: IPTrunkProvisioningParams, response: Response) ->
f"{params.subscription['iptrunk']['iptrunk_sides'][1]['iptrunk_side_node']['router_fqdn']}\n",
extra_vars=extra_vars,
callback=params.callback,
response=response,
)
@router.put("/")
def modify_ip_trunk(params: IPTrunkModifyParams, response: Response) -> PlaybookLaunchResponse:
def modify_ip_trunk(params: IPTrunkModifyParams) -> JSONResponse:
"""Launch a playbook that modifies an existing IP trunk service.
:param params: The parameters that define the change in configuration.
:type params: :class:`IPTrunkModifyParams`
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
"""
......@@ -130,18 +128,16 @@ def modify_ip_trunk(params: IPTrunkModifyParams, response: Response) -> Playbook
f"{params.subscription['iptrunk']['iptrunk_sides'][1]['iptrunk_side_node']['router_fqdn']}\n",
extra_vars=extra_vars,
callback=params.callback,
response=response,
)
@router.delete("/")
def delete_ip_trunk(params: IPTrunkDeleteParams, response: Response) -> PlaybookLaunchResponse:
def delete_ip_trunk(params: IPTrunkDeleteParams) -> JSONResponse:
"""Launch a playbook that deletes an existing IP trunk service.
:param params: Parameters that define the subscription that should get
terminated.
:type params: :class:`IPTrunkDeleteParams`
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
"""
......@@ -161,18 +157,16 @@ def delete_ip_trunk(params: IPTrunkDeleteParams, response: Response) -> Playbook
f"{params.subscription['iptrunk']['iptrunk_sides'][1]['iptrunk_side_node']['router_fqdn']}\n",
extra_vars=extra_vars,
callback=params.callback,
response=response,
)
@router.post("/perform_check")
def check_ip_trunk(params: IPTrunkCheckParams, response: Response) -> PlaybookLaunchResponse:
def check_ip_trunk(params: IPTrunkCheckParams) -> JSONResponse:
"""Launch a playbook that performs a check on an IP trunk service instance.
:param params: Parameters that define the check that is going to be
executed, including on which relevant subscription.
:type params: :class:`IPTrunkCheckParams`
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
"""
......@@ -184,19 +178,17 @@ def check_ip_trunk(params: IPTrunkCheckParams, response: Response) -> PlaybookLa
inventory=params.subscription["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["router_fqdn"],
extra_vars=extra_vars,
callback=params.callback,
response=response,
)
@router.post("/migrate")
def migrate_ip_trunk(params: IPTrunkMigrationParams, response: Response) -> PlaybookLaunchResponse:
def migrate_ip_trunk(params: IPTrunkMigrationParams) -> JSONResponse:
"""Launch a playbook to provision a new IP trunk service.
The response will contain either a job ID, or error information.
:param params: The parameters that define the new subscription object that is to be migrated.
:type params: :class:`IPTrunkMigrationParams`
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
"""
......@@ -220,5 +212,4 @@ def migrate_ip_trunk(params: IPTrunkMigrationParams, response: Response) -> Play
f"{params.new_side['new_node']['router']['router_fqdn']}\n",
extra_vars=extra_vars,
callback=params.callback,
response=response,
)
......@@ -2,10 +2,11 @@
from typing import Any
from fastapi import APIRouter, Response
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel, HttpUrl
from lso.playbook import PlaybookLaunchResponse, get_playbook_path, run_playbook
from lso.playbook import get_playbook_path, run_playbook
router = APIRouter()
......@@ -29,20 +30,18 @@ class PlaybookRunParams(BaseModel):
@router.post("/")
def run_playbook_endpoint(params: PlaybookRunParams, response: Response) -> PlaybookLaunchResponse:
def run_playbook_endpoint(params: PlaybookRunParams) -> JSONResponse:
"""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.
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
:rtype: :class:`fastapi.responses.JSONResponse`
"""
return run_playbook(
playbook_path=get_playbook_path(params.playbook_name),
extra_vars=params.extra_vars,
inventory=params.inventory,
callback=params.callback,
response=response,
)
"""Routes for handling device/base_config-related requests."""
from fastapi import APIRouter, Response
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel, HttpUrl
from lso import playbook
......@@ -35,12 +36,11 @@ class NodeProvisioningParams(BaseModel):
@router.post("/")
async def provision_node(params: NodeProvisioningParams, response: Response) -> playbook.PlaybookLaunchResponse:
async def provision_node(params: NodeProvisioningParams) -> JSONResponse:
"""Launch a playbook to provision a new node. The response will contain either a job id or error information.
:param params: Parameters for provisioning a new node
:type params: :class:`NodeProvisioningParams`
:param Response response: A FastAPI response used for returning error codes if needed
:return: Response from the Ansible runner, including a run ID.
:rtype: :class:`lso.playbook.PlaybookLaunchResponse`
"""
......@@ -56,5 +56,4 @@ async def provision_node(params: NodeProvisioningParams, response: Response) ->
inventory=f"{params.subscription['router']['router_fqdn']}",
extra_vars=extra_vars,
callback=params.callback,
response=response,
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment