From 22419ecf845d15d0c28d1635b76e36f8b3a9514f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 18 Sep 2025 11:45:51 +0200 Subject: [PATCH] read and write with readis --- Dockerfile | 9 +++++ config/inventory.yml | 8 +++++ src/database.py | 30 +++++++++++++++++ src/main.py | 78 ++++++++++++++++++++++++++++++++------------ src/utils.py | 9 +++++ 5 files changed, 113 insertions(+), 21 deletions(-) create mode 100644 Dockerfile create mode 100644 config/inventory.yml create mode 100644 src/database.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..15be366 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM python:latest + +WORKDIR /usr/local/bin +COPY requirements.txt requirements.txt +RUN pip install -r requirements.txt + +COPY src/start.py . + +CMD ["src/start.py"] \ No newline at end of file diff --git a/config/inventory.yml b/config/inventory.yml new file mode 100644 index 0000000..6433930 --- /dev/null +++ b/config/inventory.yml @@ -0,0 +1,8 @@ +- 1: + hostname: '172.16.57.2' + username: 'admin' + password: 'Netapp12' +- 2: + hostname: '172.16.56.2' + username: 'admin' + password: 'Netapp12' diff --git a/src/database.py b/src/database.py new file mode 100644 index 0000000..17d53c5 --- /dev/null +++ b/src/database.py @@ -0,0 +1,30 @@ +import json +import logging +from redis import Redis, ConnectionError + + +def setup_db_conn(redishost, redisport: str): + ''' Setup Redis connection and return it open''' + log = logging.getLogger('uvicorn') + try: + redisclient = Redis(host=redishost, port=redisport, decode_responses=True) + log.info(f"Connected to Redis DB {redishost} on port {redisport}") + return redisclient + except ConnectionError as e: + print(f"FATAL: Redis DB {redishost} is unreachable on port {redisport}. Err: {e}") + return None + except Exception as e: + print(f"FATAL: {e}") + return None + +def get_inventory_from_redis(redisclient: Redis): + ''' Read inventory from Redis ''' + cluster_inv = redisclient.hgetall('cluster_inventory') + if 'inventory' in cluster_inv: + return json.loads(cluster_inv['inventory']) + return {} + +def read_config_from_db(redisclient: Redis): + ''' Load inventory to global vars''' + global_inventory = get_inventory_from_redis(redisclient) + return global_inventory \ No newline at end of file diff --git a/src/main.py b/src/main.py index ea4abdf..294f7e2 100644 --- a/src/main.py +++ b/src/main.py @@ -1,35 +1,71 @@ +import os +import json import logging +import yaml -from fastapi import FastAPI +from pathlib import Path +from dotenv import load_dotenv +from redis import Redis from contextlib import asynccontextmanager -import httpx -from src.aggregate import aggregate_router -from src.service import load_config +from pydantic import BaseModel, ValidationError, SecretStr, AnyHttpUrl +from typing import Optional, Literal, List, Union +from fastapi import FastAPI -logger = logging.getLogger("uvicorn") -logger.info("Starting application") -config = load_config() +from database import setup_db_conn, get_inventory_from_redis, read_config_from_db +from utils import setup_logging + +def initialize_config(): + load_dotenv() + ENV_INVENTORYPATH = os.getenv('cluster_inventory_path') + ENV_REDISHOST = os.getenv('redis_host') + ENV_REDISPORT = os.getenv('redis_port') + + log.info(f"Fount Cluster Inventory file at: {ENV_INVENTORYPATH}") + if not ENV_INVENTORYPATH or not Path(ENV_INVENTORYPATH).is_file(): + print(f"FATAL: Inventory file {ENV_INVENTORYPATH} is missing or not a file.") + return False + try: + with open(ENV_INVENTORYPATH, 'r') as f: + inv = yaml.safe_load(f) + inventory = json.dumps(inv) + except Exception as e: + print(f"FATAL: Cannot read inventory file {ENV_INVENTORYPATH}. Err: {e}") + return False + + print(f'[INFO] Importing configuration to DB...') + try: + redis_conn = setup_db_conn(ENV_REDISHOST, ENV_REDISPORT) + redis_conn.hset('cluster_inventory', mapping={'inventory': inventory}) + redis_conn.close() + + log.info("Configuration has been loaded.") + return True + + except Exception as e: + print(f"FATAL: Redis DB error: {e}") + return False @asynccontextmanager async def lifespan(app: FastAPI): - app.requests_client = httpx.AsyncClient(verify=False) + ''' make loading it async''' + log = logging.getLogger('uvicorn') + cfg_init_result = initialize_config() + + inv_check = read_config_from_db(setup_db_conn(os.getenv('redis_host'), os.getenv('redis_port'))) + log.info(f"Data validity check (DEVELOPER MODE): {inv_check}") + if not cfg_init_result: + log.error("Configuration initialization failed. Exiting...") + exit(1) + yield - await app.requests_client.aclose() + log.info("Shutting down FastAPI app...") +setup_logging() +log = logging.getLogger('uvicorn') + +log.info("Starting FastAPI app...") app = FastAPI(lifespan=lifespan) -app.include_router(aggregate_router) - - -@app.get("/") -async def main(): - return {"Hello": "World"} - - -@app.get("/config") -async def get_config(): - """Endpoint to get the current configuration.""" - return config.model_dump() diff --git a/src/utils.py b/src/utils.py index febeea5..8ab9e69 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,3 +1,4 @@ +import logging import httpx def round_bytes(size_in_bytes: int) -> str: @@ -22,3 +23,11 @@ async def get_data_from_ontap(client, logger, hostname: str, username: str, pass except httpx.HTTPError as e: logger.error(f"HTTP error occurred: {e}") return None + +def setup_logging() -> None: + """Configure logging for the application""" + logging.basicConfig( + level=logging.DEBUG, + format="[%(asctime)s] [%(levelname)5s] %(message)s" + ) + print(f"Logger is initialized.")