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:
- [ ] 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
- [ ] Rename error in unexpected error
- [ ] Use background tasks for alerting

View file

@ -105,7 +105,7 @@ async def update_from_config(db: Session, config: schemas.Config):
db.commit()
async def get_severity_counts(db: Session):
async def get_severity_counts(db: Session) -> dict:
# Get the last result of each task
subquery = (
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
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

View file

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

View file

@ -7,6 +7,7 @@ from sqlalchemy import desc
from sqlalchemy.orm import Session, aliased
from argos.schemas import Config
from argos.server import queries
from argos.server.models import Result, Task
from argos.server.routes.dependencies import get_config, get_db
@ -16,6 +17,23 @@ templates = Jinja2Templates(directory="argos/server/templates")
@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)):
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()
return templates.TemplateResponse(
"index.html",
"details.html",
{
"request": request,
"domains": domains,

View file

@ -3,3 +3,15 @@
code {
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,7 +9,6 @@
</tr>
</thead>
<tbody>
{% for result in last_seen %}
<tr>
<td>{{ result.agent_id }}</td>

View file

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

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" %}
{% block title %}<h2>{{domain}}</h2>{% endblock %}
{% block title %}<h2>{{ domain }}</h2>{% endblock %}
{% block content %}
<div id="domains" class="frame">
<table id="domains-list" role="grid">
@ -19,11 +19,12 @@
<td>{{ task.url }}</td>
<td>{{ task.check }}</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>
</tr>
{% endfor %}
</tbody>
</table>
</div>

View file

@ -2,34 +2,27 @@
{% 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>
<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>
</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 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>
</div>
</div>
{% endblock %}

View file

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