A dashboard widget that condenses your F5 BIG-IP fleet into a single 0–100 score and a Good / Warning / Critical badge, with a per-check breakdown and object-level drill-down. This page is the under-the-hood tour: what is measured, from where, how baselines are learned, and how the number reaches your dashboard.
A Report Card is poller-writes / API-reads. A background evaluator runs on a timer, reads live telemetry, and writes an immutable snapshot. The widget just reads the newest snapshot for its card type — so the dashboard is always a cheap, instant read, never a live device call.
Each snapshot carries:
| Card | What it grades | Learned baseline? |
|---|---|---|
| BIG-IP Metrics | Are BIG-IPs up, in a valid HA state, reporting fresh stats, in-sync, cleanly provisioned? | No — state / liveness |
| Anomaly Detection | Connections, pool member availability, CPU, and memory vs. their normal range | Yes (except pool) |
| Application Performance | HTTP response time, 4xx ratio, 5xx ratio per virtual server | Yes |
| Security | Certificate expiry exposure and ASM/WAF critical-event bursts | No — bucketed penalties |
From Versus Platform → Dashboards, click Add Widget, choose F5 Report Card, pick which card to show (BIG-IP Metrics, Anomaly Detection, Application Performance, or Security), and optionally set a title. The widget begins polling immediately and refreshes every 30 seconds. Add it four times to see all four cards side by side — each widget stores only its card_type; the work has already happened server-side.
The evaluator runs as a daemon thread in the application worker, started on boot when in-process schedulers are enabled. It is idempotent per process and safe to run redundantly — extra runs simply raise snapshot cadence, which is thinned away later.
A single loop drives everything off a 60-second base tick:
| Every… | What runs |
|---|---|
| 60s (every tick) | BIG-IP Metrics evaluator — a cheap DB read, no device call |
| ~5 min (every 5th tick) | Collectors first (device-health signals, HTTP stats, WAF events), then the Anomaly, Application Performance, and Security evaluators |
| ~1 hour (every 60th tick) | Certificate / SSL-profile refresh, plus snapshot thinning + retention cleanup |
Collectors always run before the evaluators in the same cycle, so each evaluation sees fresh data. The 5-minute cadence matches the underlying telemetry refresh, so baseline learning gets exactly one new sample per cycle. Every collector and evaluator swallows its own errors — one failing card can never poison another.
Collectors talk to each managed BIG-IP (active/standby, with a credential) over iControl REST:
| Collector | iControl endpoints | Writes |
|---|---|---|
| Device health | /cm/failover-status, /cm/sync-status, /sys/provision | DeviceHealthSignal (failover, sync, per-module provision) |
| HTTP stats | /ltm/profile/http/stats, AVR /analytics/http/report | AppPerfStats (cumulative 4xx/5xx/request counters + avg response time, per VS) |
| WAF events | /asm/event-logs/requests (last ~6 min, deduped) | WafEvent (one row per ASM event, ASM-provisioned devices only) |
| Cert refresh | /sys/crypto/cert, /ltm/profile/client-ssl + server-ssl | Certificate (with days-to-expiry) + client-side TLSProfile bindings |
Because F5 reports HTTP stats per profile, not per virtual server, the HTTP collector sums the stats of every HTTP profile attached to a VS to synthesize per-VS counters; a VS with no HTTP profile produces no row and is excluded from grading. Base metrics — CPU, memory, disk, throughput, and per-VS connection counts — are not collected here; they come from the platform's existing stats poller, and the evaluators read those tables directly.
Every card maps its final score to a badge the same way, with a critical floor on top: a single Critical finding drags a would-be Good badge down to at least Warning, so a green badge never hides a critical object.
| Score | Badge |
|---|---|
| 90–100 | Good |
| 60–89 | Warning |
| 0–59 | Critical |
| null | N/A — nothing eligible / not enough baseline yet |
score = 100
for each check:
impact% = objects_deviating / objects_eligible × 100
severity = 1.5 if any critical else 1.0
penalty = min(100, impact% × severity)
score -= (check_weight / total_weight) × penalty
score = clamp(round(score), 0, 100)Security sums fixed penalties: score = 100 − min(100, sum_of_penalties), with N/A when there are neither eligible certificates nor ASM-provisioned devices.
The simplest: score = round(healthy_devices / total_devices × 100), and N/A when there are no BIG-IPs in inventory.
Anomaly and Application Performance grade against a learned baseline, so “normal” is defined per object and per time-of-week:
| Test | Used by | Trips when… |
|---|---|---|
| Statistical (σ) | Connections | value > mean + 3σ → Warning, > mean + 4σ → Critical (below an absolute floor of 100 conns, ignored) |
| Fixed-percent | CPU, Memory, Response time | value > mean × (1 + uplift%) — CPU +30%, Memory +15%, Response +50% — or above an absolute floor (Memory > 90%, Response > 1000ms) |
| Ratio-and-floor | 4xx / 5xx ratios | ratio > 2× baseline AND above a floor (4xx 5%, 5xx 1%) |
| Threshold | Pool availability | active members < ceil(50% of total) |
A single noisy sample never flips a badge. Each (object, check) keeps a hysteresis counter: an object must read over-threshold on 2 consecutive evaluations before it is marked deviating, and 5 consecutive under-threshold readings before it clears.
(device_health)Runs every 60 seconds; a pure DB read. A BIG-IP is unhealthy if any of:
active or standby.In Sync (same condition).Score is the healthy fraction of the fleet; any unhealthy device makes the check Critical. CM checks are skipped until the first signal is collected, so devices aren't falsely flagged on a cold start.
(anomaly)| Check | Weight | Metric & test |
|---|---|---|
| Pool availability | 0.35 | active members < ceil(50%) — Critical if 0 up, else Warning |
| Connections | 0.25 | per-VS cur_conns (5-min avg) vs. baseline, 3σ/4σ, floor 100 |
| CPU | 0.20 | device CPU% (5-min avg) > baseline + 30% |
| Memory | 0.20 | device memory% (5-min avg) > baseline + 15%, or absolute > 90% |
(app_perf)| Check | Weight | Metric & test |
|---|---|---|
| 5xx error ratio | 0.35 | Δ5xx / Δrequests > 2× baseline AND > 1% |
| Response time | 0.40 | avg response time > baseline + 50% or > 1000ms |
| 4xx error ratio | 0.25 | Δ4xx / Δrequests > 2× baseline AND > 5% |
Error ratios use deltas between consecutive cumulative counters; an interval is skipped when a counter goes backwards (a reboot/failover reset) or when there was no traffic — so a reboot never manufactures a fake error spike.
(security)Certificate expiry — for each certificate bound to a BIG-IP (resolved through the real client-SSL profile → certificate binding):
| Days to expiry | Penalty | Severity |
|---|---|---|
| Already expired | 40 | Critical |
| ≤ 14 days | 15 | Warning |
| ≤ 30 days | 12.5 | Warning |
| 31+ days | 0 | — |
WAF events — for each ASM-provisioned device, count critical/error ASM events in the last 5 minutes; more than 30 in the window costs 30 points and raises a Critical finding. Devices without ASM provisioned are green here, not N/A. Final score = 100 − min(100, total penalty).
When a check has deviating objects, the widget shows View Details, listing each deviating object — device, virtual server, pool, or certificate — with its measured value, its baseline, and the threshold it crossed (e.g. > 3σ above baseline). Each finding links straight to that object's page in Versus.
The widget reads a small, read-only API (tenant-scoped by the caller's token where multi-tenancy is enabled). Snapshots are append-only; reads never touch a device.
| Endpoint | Returns |
|---|---|
| GET /reportcards/?scope=fleet | Latest snapshot for all four cards (placeholder shell for any not yet computed) |
| GET /reportcards/<card_type>/ | Latest snapshot for one card — score, badge, evaluated-at, checks |
| GET /reportcards/<card_type>/history/?window=7d | Score time-series (for a future sparkline) |
| GET /reportcards/<card_type>/checks/<check_key>/ | Paginated findings for one check (25/page) |
Each card's fleet score is wired into Versus Observe as an alert rule, created disabled so you opt in:
postgresql datasource points at the application database.Un-pause the rule in Observe and route it to any contact point. Re-running the seeder is safe — the rules are created idempotently.
Snapshots and stats are down-sampled (RRD-style) rather than kept forever:
Deleting a snapshot cascades to its checks and findings.
On single-tenant and on-prem deployments this is inert. Where multi-tenancy is enabled, every row carries a tenant_id, the evaluator runs one scoped pass per active tenant (stamping each snapshot), and the read API scopes queries to the caller's tenant claim. Baselines, hysteresis state, and device-health signals are regenerable, so a background command can safely purge any tenant-less rows left from the rollout.
| Item | Details |
|---|---|
| Cards | BIG-IP Metrics, Anomaly Detection, Application Performance, Security |
| Score / badge | 0–100 → Good (90+), Warning (60–89), Critical (<60), N/A |
| Widget refresh | Every 30 seconds (reads the latest snapshot) |
| Evaluation cadence | Device health every 60s; anomaly/app-perf/security ~5 min; cert refresh + cleanup hourly |
| Baseline | Welford mean/variance, bucketed by hour-of-week (168 buckets), ~1 day to learn |
| Deviation tests | σ (connections), fixed-% (CPU/mem/response), ratio+floor (4xx/5xx), threshold (pool) |
| Stability | Hysteresis — 2 evals to trip, 5 to clear |
| Alerting | Observe rule per card, disabled by default, fires when fleet score < 60 |
| Retention | Snapshots 90 days, app-perf & WAF events 30 days (down-sampled) |