diff --git a/src/aggregate/aggregate_router.py b/src/aggregate/aggregate_router.py index b33556b..81864e3 100644 --- a/src/aggregate/aggregate_router.py +++ b/src/aggregate/aggregate_router.py @@ -1,21 +1,16 @@ # contains the router for the aggregates endpoint -from fastapi import APIRouter, Query -from enum import Enum +from fastapi import APIRouter, Query, Request from typing import List -from .aggregate_schema import AggregateSchema +from .aggregate_schema import AggregateSchema, MetricEnum from .aggregate_service import get_aggregates -class MetricEnum(str, Enum): - relative = "relative" - absolute = "absolute" - - router = APIRouter(tags=["aggregates"]) @router.get("/aggregates", response_model=List[AggregateSchema]) async def aggregates_endpoint( + request: Request, metric: MetricEnum = Query(MetricEnum.relative, description="Metric type"), ): - return await get_aggregates(metric) + return await get_aggregates(request, metric) diff --git a/src/aggregate/aggregate_schema.py b/src/aggregate/aggregate_schema.py index 4edd30a..d197e4f 100644 --- a/src/aggregate/aggregate_schema.py +++ b/src/aggregate/aggregate_schema.py @@ -1,8 +1,15 @@ # contains the schema definitions for aggregates from pydantic import BaseModel +from enum import Enum class AggregateSchema(BaseModel): aggregate: str node: str - available: str + available: int + available_str: str + + +class MetricEnum(str, Enum): + relative = "relative" + absolute = "absolute" diff --git a/src/aggregate/aggregate_service.py b/src/aggregate/aggregate_service.py index 0a40557..7a37d87 100644 --- a/src/aggregate/aggregate_service.py +++ b/src/aggregate/aggregate_service.py @@ -1,24 +1,38 @@ # contains the business logic for aggregates + from typing import List -from .aggregate_schema import AggregateSchema + +from fastapi import Request +from .aggregate_schema import AggregateSchema, MetricEnum +from logging import getLogger +from ..utils import round_bytes, get_data_from_ontap + +logger = getLogger("uvicorn") +logger.setLevel("DEBUG") -async def get_aggregates(metric: str = "relative") -> List[AggregateSchema]: +async def get_aggregates(request: Request, metric: str = "relative") -> List[AggregateSchema]: # Dummy data for demonstration # You can use the metric parameter to filter or modify results as needed # For now, just return the same data and show metric usage - print(f"Metric used: {metric}") + logger.debug(f"Metric used: {metric}") + client = request.app.requests_client + __aggregates = await get_data_from_ontap(client, logger, "172.16.57.2", "admin", "Netapp12", "storage/aggregates", "fields=name,uuid,space,node,home_node") + logger.debug(__aggregates) + __aggregates = __aggregates.get("records") + if metric == MetricEnum.relative: + __aggregates = sorted(__aggregates, key=lambda r: r["space"]["block_storage"].get("used_percent"), reverse=True) + elif metric == MetricEnum.absolute: + __aggregates = sorted(__aggregates, key=lambda r: r["space"]["block_storage"].get("available"), reverse=False) aggregates: list = [ AggregateSchema( - aggregate="Aggregate A", node="cluster01-01", available="100.0TB" - ), - AggregateSchema( - aggregate="Aggregate B", node="cluster01-01", available="200.5GB" - ), - AggregateSchema( - aggregate="Aggregate C", node="cluster01-02", available="300.75MB" - ), + aggregate=a["name"], + node=a["node"]["name"], + available=a["space"]["block_storage"]["available"], + available_str=round_bytes(a["space"]["block_storage"]["available"]), + ) + for a in __aggregates ] return aggregates diff --git a/src/main.py b/src/main.py index 8e902bf..ea4abdf 100644 --- a/src/main.py +++ b/src/main.py @@ -1,14 +1,26 @@ -from src.service import load_config -from fastapi import FastAPI import logging + +from fastapi import FastAPI +from contextlib import asynccontextmanager +import httpx + from src.aggregate import aggregate_router +from src.service import load_config logger = logging.getLogger("uvicorn") logger.info("Starting application") config = load_config() -app = FastAPI() + +@asynccontextmanager +async def lifespan(app: FastAPI): + app.requests_client = httpx.AsyncClient(verify=False) + yield + await app.requests_client.aclose() + + +app = FastAPI(lifespan=lifespan) app.include_router(aggregate_router) diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..febeea5 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,24 @@ +import httpx + +def round_bytes(size_in_bytes: int) -> str: + # Helper function to convert bytes to a human-readable format + for unit in ["B", "KiB", "MiB", "GiB", "TiB", "PiB"]: + if size_in_bytes < 1024: + return f"{size_in_bytes:.2f}{unit}" + size_in_bytes /= 1024 + return f"{size_in_bytes:.2f}EB" + + +async def get_data_from_ontap(client, logger, hostname: str, username: str, password: str, endpoint: str, query_string: str = ""): + url = f"https://{hostname}/api/{endpoint}" + if query_string: + url += f"?{query_string}" + async with client as _client: + try: + logger.debug(f"Fetching data from ONTAP: {url}") + response = await _client.get(url, auth=(username, password)) + response.raise_for_status() + return response.json() + except httpx.HTTPError as e: + logger.error(f"HTTP error occurred: {e}") + return None