Collector: ingest GE-Enforce/enrollment data with configurable pc-type mapping

Extends the computers collector so it can replace the classic api.asp
updateCompleteAsset path that the shopfloor PC fleet uses to auto-update data.

Collector schema (project naming) now accepts the GE-Enforce/enrollment shape:
machinenumber, pctype, pcsubtype, serialnumber, loggedinuser, lastboottime,
lastcheckin, ipaddress, vendorname, modelnumber, osname, installedsoftware.
- machinenumber -> Asset.assetnumber (skips the 9999 imaging placeholder, falls
  back to hostname), on create and update.
- pctype -> ComputerType via a configurable mapping (see below).
- vendor/model created if missing (free vocab); OS looked up (controlled, warns
  if unknown); pcsubtype accepted but not yet stored (warning).
- Dropped per scope: VNC/WinRM flags, warranty, DNC config, multi-NIC.

Configurable pc-type mapping (the gea-shopfloor-* imaging taxonomy ->
ComputerType): defaults + resolution live in plugins/computers/pctypemap.py
(plugin domain, contract-pure - reads Setting via shopdb.api); overrides stored
as pctypemap_<pxetype> settings, seeded on plugin install, edited in Settings >
System > "Collector PC Type Mapping" (new UI section).

Migration doc: docs/COLLECTOR-INTEGRATION.md maps classic api.asp fields +
GE-Enforce status fields to the collector schema, documents machine-number
sourcing (registry MachineNo first, then C:\Enrollment\machine-number.txt) and
that the transport is interim.

Tests: complete-asset payload maps machinenumber/pctype/vendor/model/os; 9999
placeholder falls back to hostname. 186 tests pass, naming green, app boots,
mapping UI verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-06-26 21:35:23 -04:00
parent d20682fd06
commit 10ed83e14c
6 changed files with 332 additions and 9 deletions

View File

@@ -115,3 +115,53 @@ def test_per_plugin_key_overrides_shared(client, db, app, computer_assettype):
finally:
app.config.pop('COLLECTOR_API_KEY_COMPUTERS', None)
app.config['COLLECTOR_API_KEY'] = None
def test_complete_asset_payload_maps_enrollment_data(client, db, collector_key,
computer_assettype):
"""A GE-Enforce-shaped payload maps machinenumber/pctype/vendor/model/os."""
from shopdb.core.models import OperatingSystem
from plugins.computers.models import Computer, ComputerType
# Seed the controlled-vocab rows the collector looks up.
db.session.add(ComputerType(computertype='Shopfloor PC'))
db.session.add(OperatingSystem(osname='Windows 11'))
db.session.commit()
payload = {
'hostname': 'WJSF1234',
'machinenumber': '0615',
'pctype': 'gea-shopfloor-collections',
'serialnumber': 'SN-ENROLL',
'vendorname': 'Dell',
'modelnumber': 'OptiPlex 7090',
'osname': 'Windows 11',
}
resp = client.post('/api/collector/computers', json=payload,
headers={'X-API-Key': KEY})
assert resp.status_code == 200, resp.get_json()
assert resp.get_json()['data']['action'] == 'created'
with client.application.app_context():
comp = Computer.query.filter(Computer.hostname.ilike('WJSF1234')).first()
assert comp is not None
assert comp.asset.assetnumber == '0615' # machinenumber -> assetnumber
assert comp.asset.serialnumber == 'SN-ENROLL'
assert comp.computertype.computertype == 'Shopfloor PC' # pctype mapped
assert comp.vendor.vendor == 'Dell' # created
assert comp.model.modelnumber == 'OptiPlex 7090' # created
assert comp.operatingsystem.osname == 'Windows 11'
def test_placeholder_machinenumber_9999_falls_back_to_hostname(client, db,
collector_key,
computer_assettype):
"""The 9999 imaging placeholder is ignored; assetnumber falls back to host."""
resp = client.post('/api/collector/computers',
json={'hostname': 'WJSF9999', 'machinenumber': '9999'},
headers={'X-API-Key': KEY})
assert resp.status_code == 200
from plugins.computers.models import Computer
with client.application.app_context():
comp = Computer.query.filter(Computer.hostname.ilike('WJSF9999')).first()
assert comp.asset.assetnumber == 'WJSF9999'