155 lines
4.6 KiB
Python
155 lines
4.6 KiB
Python
# load config so everything else can work
|
|
from pillar_tool.util import load_config, config
|
|
from pillar_tool.util.validation import validate_and_split_path_and_domain_name
|
|
|
|
load_config()
|
|
|
|
from http.server import BaseHTTPRequestHandler
|
|
|
|
from pillar_tool.db.base_model import as_dict
|
|
from pillar_tool.middleware.logging import request_logging_middleware
|
|
from pillar_tool.schemas import HostCreateParams
|
|
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
|
from pillar_tool.db.database import get_connection
|
|
from pillar_tool.db.queries.auth_queries import create_user
|
|
from pillar_tool.db.queries.host_queries import *
|
|
|
|
|
|
from fastapi import FastAPI
|
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
|
from starlette.requests import Request
|
|
from fastapi.responses import HTMLResponse, PlainTextResponse
|
|
from starlette.responses import JSONResponse
|
|
|
|
from pillar_tool.middleware.basicauth_backend import BasicAuthBackend
|
|
from pillar_tool.middleware.db_connection import db_connection_middleware
|
|
from pillar_tool.db.database import run_db_migrations
|
|
|
|
|
|
# run any pending migrations
|
|
run_db_migrations()
|
|
|
|
# get a database connection
|
|
db = get_connection()
|
|
|
|
# create default user if it does not exist
|
|
# noinspection PyBroadException
|
|
try:
|
|
create_user(db, "admin", "admin")
|
|
except:
|
|
pass
|
|
|
|
# commit and close the db
|
|
db.commit()
|
|
db.close()
|
|
|
|
|
|
def on_auth_error(request: Request, exc: Exception):
|
|
response = PlainTextResponse(str(exc), status_code=401)
|
|
response.headers["WWW-Authenticate"] = "Basic"
|
|
|
|
return response
|
|
|
|
def on_db_error(request: Request, exc: Exception):
|
|
response = PlainTextResponse(str(exc), status_code=500)
|
|
|
|
return response
|
|
|
|
def on_general_error(request: Request, exc: Exception):
|
|
print("wtf?")
|
|
response = PlainTextResponse(str(exc), status_code=500)
|
|
return response
|
|
|
|
app = FastAPI()
|
|
app.add_middleware(AuthenticationMiddleware, backend=BasicAuthBackend(), on_error=on_auth_error)
|
|
app.add_middleware(BaseHTTPMiddleware, dispatch=db_connection_middleware)
|
|
app.add_middleware(BaseHTTPMiddleware, dispatch=request_logging_middleware)
|
|
app.exception_handler(Exception)(on_general_error)
|
|
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {"message": "Hello World"}
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
# TODO: improve health check
|
|
return {"message": "Healthy"}
|
|
|
|
|
|
@app.get("/pillar/{host}")
|
|
async def pillar_get(req: Request, host: str):
|
|
print(req.headers)
|
|
#return JSONResponse(content=collect_pillar_data(host))
|
|
return JSONResponse({})
|
|
|
|
@app.post("/pillar/{host}")
|
|
async def pillar_set(request: Request, host: str, value: str):
|
|
return JSONResponse({
|
|
"captain.linvogel.internal": {
|
|
"states": ["state1", "state2"],
|
|
"test": {
|
|
"pillar": "value"
|
|
}
|
|
}
|
|
})
|
|
|
|
@app.get("/hosts")
|
|
async def host_list(request: Request):
|
|
all_hosts = list_all_hosts(request.state.db)
|
|
return JSONResponse([x.name for x in all_hosts if x.parent_id is None])
|
|
|
|
@app.get("/hostgroups")
|
|
async def hostgroup_list(request: Request):
|
|
all_hosts = list_all_hosts(request.state.db)
|
|
return JSONResponse([x.name for x in all_hosts if x.parent_id is not None])
|
|
|
|
@app.post("/host/{fqdn}")
|
|
async def host_add(request: Request, fqdn: str, params: HostCreateParams):
|
|
# Validate and split FQDN into hierarchical labels (e.g., "a/b/c" -> ["a", "b", "c"])
|
|
labels = validate_and_split_path_and_domain_name(fqdn)
|
|
if labels is None:
|
|
raise HTTPException(status_code=400, detail="Invalid Path provided")
|
|
|
|
# walk through the labels and create the requested groups and host
|
|
created = []
|
|
last_parent = None
|
|
parent: str | None = params.parent # start with the optional parent parameter
|
|
for label in labels:
|
|
new_host = create_host(request.state.db, label, parent)
|
|
last_parent = parent
|
|
if parent is not None:
|
|
# update path to parent for the next label
|
|
parent += f"/{new_host.name}"
|
|
else:
|
|
# set first level otherwise
|
|
parent = new_host.name
|
|
created.append(new_host)
|
|
|
|
# Prepare response with creation details
|
|
output = {
|
|
"message": "Host created",
|
|
"host": created[-1], # return the final host in the hierarchy
|
|
}
|
|
|
|
# include the full path to the new host if it exists
|
|
if last_parent is not None:
|
|
output.update({
|
|
"path": last_parent
|
|
})
|
|
|
|
return JSONResponse(output)
|
|
|
|
@app.delete("/host/{fqdn}")
|
|
async def host_delete(request: Request, fqdn: str):
|
|
delete_host(request.state.db, fqdn)
|
|
return JSONResponse({})
|
|
|
|
@app.get("/top/{fqdn}")
|
|
async def host_top(request: Request, fqdn: str):
|
|
# TODO: implement
|
|
return JSONResponse({})
|
|
|