Skip to content

CMDB Data Quality Developer Guide

Ed Mozley edited this page Aug 4, 2026 · 1 revision

CMDB data quality β€” Developer Guide

Six checks with no schema and no rule engine, why a clean check must still render, and why the negative control is the test here.

Shipped as #972.

The user-facing page is CMDB β†’ Data Quality. The foundations are CMDB β€” Developer Guide; this exists mainly to protect Impact analysis, and Β§2 explains why.


1. πŸ“ The files involved

Colour key: πŸ—„οΈ schema Β· βš™οΈ engine Β· πŸ”Œ API Β· πŸ–₯️ UI Β· 🎨 CSS Β· 🌍 i18n Β· πŸ§ͺ tests Β· πŸ“„ docs

🎨 File What it does
βš™οΈ includes/cmdb_audit.php the whole feature. cmdbRunAudit() plus one function per check, cmdbAuditResult() (the uniform shape), CMDB_AUDIT_STALE_MONTHS, CMDB_AUDIT_MAX_ITEMS
πŸ”Œ api/cmdb/get_audit.php ~20 lines. Auth, module access, run, return
πŸ–₯️ cmdb/audit.php the page and its .audit-* styles
πŸ–₯️ cmdb/audit.js CHECK_ORDER, card rendering, the two empty states
πŸ–₯️ cmdb/includes/header.php the nav button ($current_page === 'audit')
πŸ–₯️ cmdb/help.php section 13, which pushed Companies to 14
🌍 lang/en/cmdb.php + lang/pt-BR/cmdb.php the audit.* block, nav.audit, help.dataquality_*

πŸ”‘ No schema change at all. Every check reads columns that already exist. Nothing to verify, nothing to migrate.


2. πŸ”‘ Why these six checks and not "data quality" in general

The temptation is a general-purpose rule engine β€” iTop has one, and it is genuinely more powerful. It is also a much bigger feature, and a rule engine with no rules written is worth nothing on day one.

So instead: every check is derived from a declaration the install has already made.

Check The declaration it enforces
required_missing a property marked is_required
dependency_blank a property ticked spreads_impact
broken_reference an object_ref pointing at a real object
no_impact_edges at least one relationship type set to carry impact
disconnected (none β€” it is structural)
stale (none β€” a fixed six-month constant)

Nobody has to configure anything for the audit to say something useful, and nothing it reports is our opinion about their estate β€” it is their own stated intent, checked.

πŸ”‘ The framing is impact analysis, not tidiness. "Connected to nothing" is not flagged because it looks untidy; it is flagged because such an object can never appear in any blast radius, which makes it invisible to the thing the module is for. That is also why no_impact_edges is rendered first β€” if nothing carries impact, every other finding is moot, and there is no point tidying dependencies nothing will follow.

A rule builder can be added later without breaking any of this.


3. ⚠️ "Connected to nothing" is stricter than it first looks

The obvious query is no parent, no children, no relationships. On the estate this shipped against, that returns 13 of 39 objects. The real check returns 2.

The difference is object_ref properties. Eleven of those objects are pointed at by β€” or point at β€” another object through a typed property. They are connected; the naive query just doesn't look there.

⚠️ An audit that over-reports by 6Γ— is worse than no audit, because the first thing anyone does with a wrong list is stop opening the page. Both directions must be checked: properties this object sets, and properties elsewhere that point at it.

Ticket links deliberately do not rescue an object here. A CI referenced by a ticket is being used, but it still contributes nothing to impact analysis, which is what this check is about.


4. πŸ”‘ Every check renders, including the ones that pass

A check finding nothing is information β€” "every object reference points at something real" is worth reading. More importantly, hiding passing checks makes a clean result indistinguishable from a check that silently failed to run.

Cards therefore always render, with a green left border and the clean message. no_impact_edges is the one exception to the shape: it is install-level, so it shows OK / ! and a link to settings rather than a list of objects.

⚠️ Capped lists say so. CMDB_AUDIT_MAX_ITEMS bounds the rows returned; cmdbAuditResult() sets capped by comparing the true count with the returned rows, and the card renders "showing the first 100 of 412". A truncated list presented as complete is a worse failure than a slow page.


5. πŸ§ͺ Verifying a change here β€” the negative control IS the test

On healthy data five of the six checks return 0. That is indistinguishable from a query with a typo in it, a wrong join, or a WHERE clause that can never be true. Asserting "returns 0 on clean data" proves nothing whatsoever.

The test therefore works by injection: record a baseline, break exactly one thing, assert the relevant count moves by exactly +1 β€” and assert the other checks did not move.

Injected Expected
A required property added to a class with existing objects required_missing +1, everything else unchanged
An object_ref value pointing at a non-existent id broken_reference +1
A property ticked spreads_impact, left blank dependency_blank rises
A new object with no links disconnected +1
…then give it a parent disconnected βˆ’1 β€” proves the check can clear, not just count
Backdate updated_datetime 14 months stale +1
Set every impact_direction to none no_impact_edges flips 0 β†’ 1

⚠️ broken_reference cannot be reproduced on a fresh install. freeitsm.sql defines fk_cmdb_op_value_object with ON DELETE SET NULL, so the dangling row simply cannot be inserted. The check exists for Verify-grown installs, which have no CMDB foreign keys (see the foundations guide Β§5). The fixture uses SET FOREIGN_KEY_CHECKS = 0 to reproduce that install's shape. Skipping instead would leave the check unproven on the only installs where it can ever fire β€” and a skip that describes your data shape is always worth chasing.

Everything runs inside a transaction and rolls back.


6. ⬜ Not built yet

  • User-defined rules. The iTop equivalent. Additive β€” the built-ins can stay.
  • Per-class thresholds. CMDB_AUDIT_STALE_MONTHS is one constant for the whole install; six months is right for a server and absurd for a wall switch.
  • Trend. Nothing is stored, so there is no "findings are down from last month" β€” and nothing to chart.
  • Fix-it actions. Deliberately read-only for now. The analyst is the one who knows whether a standalone CI is a mistake.
  • Exposure through REST v1. The engine is callable; no route yet.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally