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:
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user