Merge branch 'quick-overview' into 'main'

💄 — Quick supervision overview on homepage

See merge request framasoft/framaspace/argos!7
This commit is contained in:
Alexis Metaireau 2023-11-22 12:34:17 +00:00
commit e652659fb6
11 changed files with 135 additions and 77 deletions

View file

@ -19,7 +19,7 @@ Internally, a HTTP API is exposed, and a job queue is used to distribute the che
## Todo: ## Todo:
- [ ] Do not return empty list on / when no results from agents. - [ ] Do not return empty list on / when no results from agents.
- [ ] donner un aperçu rapide de létat de la supervision. - [X] donner un aperçu rapide de létat de la supervision.
- [ ] Allow passing a dict to check - [ ] Allow passing a dict to check
- [ ] Rename error in unexpected error - [ ] Rename error in unexpected error
- [ ] Use background tasks for alerting - [ ] Use background tasks for alerting
@ -29,4 +29,4 @@ Internally, a HTTP API is exposed, and a job queue is used to distribute the che
- [ ] Un flag de configuration permet dajouter automatiquement un job de vérification de redirection 301 de la version HTTP vers HTTPS - [ ] Un flag de configuration permet dajouter automatiquement un job de vérification de redirection 301 de la version HTTP vers HTTPS
- [ ] add an "unknown" severity for check errors - [ ] add an "unknown" severity for check errors
- [ ] Add a way to specify the severity of the alerts in the config - [ ] Add a way to specify the severity of the alerts in the config
- [ ] Add a command to generate new authentication token - [ ] Add a command to generate new authentication token

View file

@ -105,7 +105,7 @@ async def update_from_config(db: Session, config: schemas.Config):
db.commit() db.commit()
async def get_severity_counts(db: Session): async def get_severity_counts(db: Session) -> dict:
# Get the last result of each task # Get the last result of each task
subquery = ( subquery = (
db.query(Result.task_id, func.max(Result.id).label("max_result_id")) db.query(Result.task_id, func.max(Result.id).label("max_result_id"))
@ -122,6 +122,10 @@ async def get_severity_counts(db: Session):
# Execute the query and fetch the results # Execute the query and fetch the results
task_counts_by_severity = query.all() task_counts_by_severity = query.all()
counts_dict = dict(task_counts_by_severity)
for key in ("ok", "warning", "critical"):
counts_dict.setdefault(key, 0)
return task_counts_by_severity return task_counts_by_severity

View file

@ -73,8 +73,4 @@ async def get_stats(db: Session = Depends(get_db)):
@route.get("/severities") @route.get("/severities")
async def get_severity_counts(db: Session = Depends(get_db)): async def get_severity_counts(db: Session = Depends(get_db)):
"""Returns the number of results per severity""" """Returns the number of results per severity"""
counts = await queries.get_severity_counts(db) return await queries.get_severity_counts(db)
counts_dict = dict(counts)
for key in ("ok", "warning", "critical"):
counts_dict.setdefault(key, 0)
return counts_dict

View file

@ -7,6 +7,7 @@ from sqlalchemy import desc
from sqlalchemy.orm import Session, aliased from sqlalchemy.orm import Session, aliased
from argos.schemas import Config from argos.schemas import Config
from argos.server import queries
from argos.server.models import Result, Task from argos.server.models import Result, Task
from argos.server.routes.dependencies import get_config, get_db from argos.server.routes.dependencies import get_config, get_db
@ -16,6 +17,23 @@ templates = Jinja2Templates(directory="argos/server/templates")
@route.get("/") @route.get("/")
async def get_severity_counts(request: Request, db: Session = Depends(get_db)):
"""Returns the number of results per severity"""
counts_dict = await queries.get_severity_counts(db)
agents = db.query(Result.agent_id).distinct().all()
return templates.TemplateResponse(
"index.html",
{
"request": request,
"counts_dict": counts_dict,
"agents": agents,
},
)
@route.get("/details")
async def read_tasks(request: Request, db: Session = Depends(get_db)): async def read_tasks(request: Request, db: Session = Depends(get_db)):
tasks = db.query(Task).order_by(Task.domain).all() tasks = db.query(Task).order_by(Task.domain).all()
@ -46,7 +64,7 @@ async def read_tasks(request: Request, db: Session = Depends(get_db)):
agents = db.query(Result.agent_id).distinct().all() agents = db.query(Result.agent_id).distinct().all()
return templates.TemplateResponse( return templates.TemplateResponse(
"index.html", "details.html",
{ {
"request": request, "request": request,
"domains": domains, "domains": domains,

View file

@ -2,4 +2,16 @@
code { code {
white-space: pre-wrap; white-space: pre-wrap;
} }
.grid-index {
font-size: 2rem;
}
.text-center,
.grid-index {
text-align: center;
}
.grid-index article {
margin-top: 0;
margin-bottom: 1rem;
}

View file

@ -9,13 +9,12 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for result in last_seen %}
{% for result in last_seen %} <tr>
<tr> <td>{{ result.agent_id }}</td>
<td>{{ result.agent_id }}</td> <td>{{ result.submitted_at }}</td>
<td>{{ result.submitted_at }}</td> </tr>
</tr> {% endfor %}
{% endfor %}
</tbody> </tbody>
</table> </table>
{% endblock %} {% endblock %}

View file

@ -1,20 +1,23 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Argos</title> <title>Argos</title>
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head> <meta http-equiv="X-UA-Compatible" content="IE=Edge">
<body> <link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
<main class="container"> </head>
<div id="header"> <body>
{% block title %}<a href="/"><h1 id="title">Argos monitoring</h1></a>{% endblock %} <main class="container">
</div> <div id="header">
{% block title %}
<div id="content"> <a href="/"><h1 id="title">Argos monitoring</h1></a>
{% block content %} {% endblock %}
{% endblock %} </div>
</div> <div id="content">
</main> {% block content %}
</body> {% endblock %}
</html> </div>
</main>
</body>
</html>

View file

@ -0,0 +1,33 @@
{% extends "base.html" %}
{% block content %}
<div id="domains" class="frame">
<p>
{{ domains | length}} domains,
{{ total_task_count }} tasks,
<a href="/agents">{{ agents | length }} agent{% if agents | length > 1 %}s{% endif %}</a>
</p>
<table id="domains-list" role="grid">
<thead>
<tr>
<th>Domain</th>
<th class="today">Current status</th>
<th>Last check</th>
</tr>
</thead>
<tbody id="domains-body">
{% for (domain, status) in domains %}
<tr>
<td>
<a href="/domain/{{ domain }}">{{ domain }}</a>
</td>
<td class="status highlight">
{% if status == "ok" %}✅ OK {% elif statuts == "warning"%}⚠ Warning{% elif status == "critical"%}❌ Critical{% elif status == "to-process" %}⏱︎ Waiting for the jobs{% endif %}
</td>
<td>{{ last_checks.get(domain) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<h2>{{domain}}</h2>{% endblock %} {% block title %}<h2>{{ domain }}</h2>{% endblock %}
{% block content %} {% block content %}
<div id="domains" class="frame"> <div id="domains" class="frame">
<table id="domains-list" role="grid"> <table id="domains-list" role="grid">
@ -19,12 +19,13 @@
<td>{{ task.url }}</td> <td>{{ task.url }}</td>
<td>{{ task.check }}</td> <td>{{ task.check }}</td>
<td>{{ task.expected }}</td> <td>{{ task.expected }}</td>
<td class="status highlight"><a data-tooltip="Completed at {{ task.completed_at }}" href="/result/{{ task.last_result.id }}">{% if task.status == "success" %}✅ Success {% elif task.status == "error"%}⚠ Error{% elif task.status == "failure"%}❌ Failure{% endif %}</a></td> <td class="status highlight">
<a data-tooltip="Completed at {{ task.completed_at }}" href="/result/{{ task.last_result.id }}">{% if task.status == "success" %}✅ Success {% elif task.status == "error"%}⚠ Error{% elif task.status == "failure"%}❌ Failure{% endif %}</a>
</td>
<td><a href="/task/{{task.id}}/results">view all</a></td> <td><a href="/task/{{task.id}}/results">view all</a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -2,34 +2,27 @@
{% block content %} {% block content %}
<div id="domains" class="frame"> <div id="domains" class="frame">
<p> <p>
{{domains | length}} domains, <a href="/agents">{{ agents | length }} agent{% if agents| length > 1 %}s{% endif %}</a>
{{ total_task_count }} tasks, </p>
<a href="/agents">{{ agents |length }} agent{% if agents|length > 1 %}s{% endif %}</a>
<div class="container">
<div class="grid grid-index">
<article>
<header>✅ OK </header>
{{ counts_dict['ok'] }}
</article>
<article>
<header> ⚠️ Warning</header>
{{ counts_dict['warning'] }}
</article>
<article>
<header>❌ Critical</header>
{{ counts_dict['critical'] }}
</article>
</div>
<p class="text-center">
<a href="/details" role="button">Details</a>
</p> </p>
</div>
<table id="domains-list" role="grid">
<thead>
<tr>
<th>Domain</th>
<th class="today">Current status</th>
<th>Last check</th>
</thead>
<tbody id="domains-body">
{% for (domain, status) in domains %}
<tr>
<td>
<a href="/domain/{{domain}}">
{{ domain }} </a>
</td>
<td class="status highlight">{% if status == "ok" %}✅ OK {% elif statuts == "warning"%}⚠ Warning{% elif status == "critical"%}❌ Critical{% elif status == "to-process" %}⏱︎ Waiting for the jobs{% endif %}</td>
<td>{{ last_checks.get(domain) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,17 +1,16 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<h2>{{result}}</h2>{% endblock %} {% block title %}<h2>{{ result }}</h2>{% endblock %}
{% block content %} {% block content %}
<dl> <dl>
<dt>Task</dt> <dt>Task</dt>
<dd>{{result.task}}</dd> <dd>{{ result.task }}</dd>
<dt>Submitted at</dt> <dt>Submitted at</dt>
<dd>{{result.submitted_at}}</dd> <dd>{{ result.submitted_at }}</dd>
<dt>Status</dt> <dt>Status</dt>
<dd>{{result.status}}</dd> <dd>{{ result.status }}</dd>
<dt>Severity</dt> <dt>Severity</dt>
<dd>{{result.severity}}</dd> <dd>{{ result.severity }}</dd>
<dt>Context</dt> <dt>Context</dt>
<dd>{{result.context}}</dd> <dd>{{ result.context }}</dd>
</dl> </dl>
{% endblock %}
{% endblock %}