Dashboard cards, backup last-seen
This commit is contained in:
@@ -26,7 +26,8 @@ SHAREROOT_SETTING = 'geenforce_share_root'
|
||||
|
||||
from ..models import (
|
||||
ManifestScope, ManifestEntry, ManifestPublishedVersion,
|
||||
ManifestEnforcementReport, ManifestPayload, ManifestBlob, ENTRY_TYPES, PHASES,
|
||||
ManifestEnforcementReport, ManifestEnforcementResult, ManifestPayload,
|
||||
ManifestBlob, ENTRY_TYPES, PHASES,
|
||||
)
|
||||
from ..serializer import scope_to_manifest, entry_to_dict
|
||||
from ..importer import build_entry, populate_entry
|
||||
@@ -954,3 +955,57 @@ def get_report(reportid):
|
||||
'message': r.message,
|
||||
} for r in report.results],
|
||||
})
|
||||
|
||||
|
||||
@geenforce_bp.route('/dashboard/failures', methods=['GET'])
|
||||
@jwt_required()
|
||||
@require_permission('geenforce.manage')
|
||||
def dashboard_failures():
|
||||
"""Entries that FAILED on their PC's most recent enforcement cycle.
|
||||
|
||||
The dashboard card behind this answers a question nothing else does: what is
|
||||
broken on the floor right now. The reports table has held it all along -
|
||||
status, the failing entry, its exit code and the engine's message - and the
|
||||
only way to see any of it was to open one PC's report modal, one PC at a
|
||||
time. A bay returned 500 to every collector report for a day and a half
|
||||
before anyone looked.
|
||||
|
||||
Per ENTRY, not per report: "three PCs failed" is a number, while "Install
|
||||
OpenText failed with exit 1603 on WJSF1234" is something a person can act
|
||||
on. Only current reports (iscurrent) are considered, so a failure that has
|
||||
since been fixed disappears on its own rather than needing dismissing.
|
||||
"""
|
||||
rows = (db.session.query(ManifestEnforcementResult, ManifestEnforcementReport)
|
||||
.join(ManifestEnforcementReport,
|
||||
ManifestEnforcementResult.reportid ==
|
||||
ManifestEnforcementReport.reportid)
|
||||
.filter(ManifestEnforcementReport.iscurrent.is_(True),
|
||||
ManifestEnforcementResult.action == 'failed')
|
||||
.order_by(ManifestEnforcementReport.receivedat.desc())
|
||||
.limit(50)
|
||||
.all())
|
||||
|
||||
# Resolve hostnames to computerids in ONE query so the card can link to the
|
||||
# PC rather than just naming it. A row whose PC is unknown to shopdb still
|
||||
# shows - the failure is real even when the inventory is behind.
|
||||
hostnames = {report.hostname for _result, report in rows}
|
||||
idbyhost = {}
|
||||
if hostnames:
|
||||
try:
|
||||
from plugins.computers.models import Computer
|
||||
for computer in Computer.query.filter(
|
||||
Computer.hostname.in_(hostnames)).all():
|
||||
idbyhost[(computer.hostname or '').lower()] = computer.computerid
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
return success_response([{
|
||||
'hostname': report.hostname,
|
||||
'computerid': idbyhost.get((report.hostname or '').lower()),
|
||||
'scopename': report.scopename,
|
||||
'entryname': result.entryname,
|
||||
'exitcode': result.exitcode,
|
||||
'message': (result.message or '').strip()[:200],
|
||||
'receivedat': (report.receivedat.isoformat() + 'Z'
|
||||
if report.receivedat else None),
|
||||
} for result, report in rows])
|
||||
|
||||
Reference in New Issue
Block a user