KPI agents Qualys: actifs/inactifs/sans agent sur dashboard + page Agents avec liste détaillée
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ca16e42ef8
commit
29a377887f
@ -25,6 +25,9 @@ async def dashboard(request: Request, db=Depends(get_db)):
|
||||
stats["eol"] = db.execute(text("SELECT COUNT(*) FROM servers WHERE licence_support='eol'")).scalar()
|
||||
stats["qualys_assets"] = db.execute(text("SELECT COUNT(*) FROM qualys_assets")).scalar()
|
||||
stats["qualys_tags"] = db.execute(text("SELECT COUNT(*) FROM qualys_tags")).scalar()
|
||||
stats["qualys_active"] = db.execute(text("SELECT COUNT(*) FROM qualys_assets WHERE agent_status ILIKE '%active%' AND agent_status NOT ILIKE '%inactive%'")).scalar()
|
||||
stats["qualys_inactive"] = db.execute(text("SELECT COUNT(*) FROM qualys_assets WHERE agent_status ILIKE '%inactive%'")).scalar()
|
||||
stats["qualys_no_agent"] = db.execute(text("SELECT COUNT(*) FROM servers WHERE etat='en_production' AND NOT EXISTS (SELECT 1 FROM qualys_assets qa WHERE LOWER(qa.hostname) = LOWER(servers.hostname))")).scalar()
|
||||
|
||||
# Par domaine
|
||||
domains = db.execute(text("""
|
||||
|
||||
@ -450,9 +450,23 @@ async def qualys_agents_page(request: Request, db=Depends(get_db)):
|
||||
keys = get_activation_keys(db)
|
||||
summary = get_agents_summary(db)
|
||||
|
||||
# Serveurs en prod sans agent Qualys
|
||||
no_agent = db.execute(text("""
|
||||
SELECT s.hostname, s.os_family, d.name as domain, e.name as env, z.name as zone
|
||||
FROM servers s
|
||||
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
||||
LEFT JOIN domains d ON de.domain_id = d.id
|
||||
LEFT JOIN environments e ON de.environment_id = e.id
|
||||
LEFT JOIN zones z ON s.zone_id = z.id
|
||||
WHERE s.etat = 'en_production'
|
||||
AND NOT EXISTS (SELECT 1 FROM qualys_assets qa WHERE LOWER(qa.hostname) = LOWER(s.hostname))
|
||||
ORDER BY s.hostname
|
||||
""")).fetchall()
|
||||
|
||||
ctx = base_context(request, db, user)
|
||||
ctx.update({
|
||||
"app_name": APP_NAME, "keys": keys, "summary": summary,
|
||||
"no_agent_servers": no_agent,
|
||||
})
|
||||
return templates.TemplateResponse("qualys_agents.html", ctx)
|
||||
|
||||
|
||||
@ -516,7 +516,11 @@ def get_agents_summary(db):
|
||||
GROUP BY agent_version ORDER BY cnt DESC LIMIT 20
|
||||
""")).fetchall()
|
||||
|
||||
return {"statuses": rows, "versions": versions}
|
||||
total = db.execute(text("SELECT COUNT(*) FROM qualys_assets")).scalar()
|
||||
active = db.execute(text("SELECT COUNT(*) FROM qualys_assets WHERE agent_status ILIKE '%%active%%' AND agent_status NOT ILIKE '%%inactive%%'")).scalar()
|
||||
inactive = db.execute(text("SELECT COUNT(*) FROM qualys_assets WHERE agent_status ILIKE '%%inactive%%'")).scalar()
|
||||
|
||||
return {"statuses": rows, "versions": versions, "total_assets": total, "active": active, "inactive": inactive}
|
||||
|
||||
|
||||
def invalidate_search_cache():
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-accent">{{ stats.total_servers }}</div><div class="text-xs text-gray-500">Serveurs</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-green">{{ stats.patchable }}</div><div class="text-xs text-gray-500">Patchables SecOps</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-white">{{ stats.linux }} / {{ stats.windows }}</div><div class="text-xs text-gray-500">Linux / Windows</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-yellow">{{ stats.qualys_tags }}</div><div class="text-xs text-gray-500">Tags Qualys</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-green">{{ stats.qualys_active }}</div><div class="text-xs text-gray-500">Agents actifs</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold {% if stats.qualys_no_agent > 0 %}text-cyber-red{% else %}text-cyber-green{% endif %}">{{ stats.qualys_no_agent }}</div><div class="text-xs text-gray-500">Sans agent</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-red">{{ stats.eol }}</div><div class="text-xs text-gray-500">EOL</div></div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -9,6 +9,14 @@
|
||||
<a href="/qualys/search" class="btn-sm bg-cyber-border text-cyber-accent px-4 py-2">Recherche</a>
|
||||
</div>
|
||||
|
||||
<!-- KPIs agents -->
|
||||
<div style="display:flex;flex-wrap:nowrap;gap:8px;margin-bottom:16px;">
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-accent">{{ summary.total_assets or 0 }}</div><div class="text-xs text-gray-500">Total assets</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-green">{{ summary.active or 0 }}</div><div class="text-xs text-gray-500">Agents actifs</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-red">{{ summary.inactive or 0 }}</div><div class="text-xs text-gray-500">Agents inactifs</div></div>
|
||||
<div class="card p-3 text-center" style="flex:1;min-width:0"><div class="text-2xl font-bold text-cyber-red">{{ no_agent_servers|length }}</div><div class="text-xs text-gray-500">Sans agent (prod)</div></div>
|
||||
</div>
|
||||
|
||||
<!-- Activation Keys -->
|
||||
<div class="card p-4 mb-4">
|
||||
<h3 class="text-sm font-bold text-cyber-accent mb-3">Activation Keys</h3>
|
||||
@ -74,4 +82,31 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Serveurs sans agent Qualys -->
|
||||
{% if no_agent_servers %}
|
||||
<div class="card p-4 mb-4">
|
||||
<h3 class="text-sm font-bold text-cyber-red mb-3">Serveurs en production sans agent Qualys ({{ no_agent_servers|length }})</h3>
|
||||
<table class="w-full table-cyber text-xs">
|
||||
<thead><tr>
|
||||
<th class="text-left p-2">Hostname</th>
|
||||
<th class="p-2">OS</th>
|
||||
<th class="p-2">Domaine</th>
|
||||
<th class="p-2">Env</th>
|
||||
<th class="p-2">Zone</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for s in no_agent_servers %}
|
||||
<tr>
|
||||
<td class="p-2 font-mono text-cyber-accent">{{ s.hostname }}</td>
|
||||
<td class="p-2 text-center">{{ s.os_family or '-' }}</td>
|
||||
<td class="p-2 text-center text-gray-400">{{ s.domain or '-' }}</td>
|
||||
<td class="p-2 text-center">{{ s.env or '-' }}</td>
|
||||
<td class="p-2 text-center">{% if s.zone == 'DMZ' %}<span class="badge badge-red">DMZ</span>{% else %}{{ s.zone or '-' }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user