feat: authoritative plugin fields

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2026-01-24 22:20:30 +11:00
parent 2acc180fd5
commit 3775e21dc7
11 changed files with 75 additions and 70 deletions

View File

@@ -280,6 +280,7 @@ function getDeviceData() {
const fieldName = setting.setKey.replace('NEWDEV_', '');
if (trackedFields[fieldName] && fieldName !== "devFQDN" && mac != "new") {
const sourceField = fieldName + "Source";
const currentSource = deviceData[sourceField] || "N/A";
const isLocked = currentSource === "LOCKED";
const lockIcon = isLocked ? "fa-lock" : "fa-lock-open";
@@ -302,12 +303,16 @@ function getDeviceData() {
const fieldName2 = setting.setKey.replace('NEWDEV_', '');
if (trackedFields[fieldName2] && mac != "new") {
const sourceField = fieldName2 + "Source";
const currentSource = deviceData[sourceField] || "N/A";
const sourceTitle = getString("FieldLock_Source_Label") + currentSource;
const sourceColor = currentSource === "USER" ? "text-warning" : (currentSource === "LOCKED" ? "text-danger" : "text-muted");
inlineControl += `<span class="input-group-addon pointer ${sourceColor}" title="${sourceTitle}">
${currentSource}
</span>`;
// only show if data available
if (deviceData[sourceField] != "")
{
const currentSource = deviceData[sourceField] || "N/A";
const sourceTitle = getString("FieldLock_Source_Label") + currentSource;
const sourceColor = currentSource === "USER" ? "text-warning" : (currentSource === "LOCKED" ? "text-danger" : "text-muted");
inlineControl += `<span class="input-group-addon pointer ${sourceColor}" title="${sourceTitle}">
${currentSource}
</span>`;
}
}
// handle devChildrenDynamic or NEWDEV_devChildrenNicsDynamic - selected values and options are the same

View File

@@ -92,11 +92,11 @@ class Device(ObjectType):
devReqNicsOnline = Int(description="Required NICs online flag")
devMacSource = String(description="Source tracking for devMac (USER, LOCKED, NEWDEV, or plugin prefix)")
devNameSource = String(description="Source tracking for devName (USER, LOCKED, NEWDEV, or plugin prefix)")
devFqdnSource = String(description="Source tracking for devFQDN (USER, LOCKED, NEWDEV, or plugin prefix)")
devLastIpSource = String(description="Source tracking for devLastIP (USER, LOCKED, NEWDEV, or plugin prefix)")
devFQDNSource = String(description="Source tracking for devFQDN (USER, LOCKED, NEWDEV, or plugin prefix)")
devLastIPSource = String(description="Source tracking for devLastIP (USER, LOCKED, NEWDEV, or plugin prefix)")
devVendorSource = String(description="Source tracking for devVendor (USER, LOCKED, NEWDEV, or plugin prefix)")
devSsidSource = String(description="Source tracking for devSSID (USER, LOCKED, NEWDEV, or plugin prefix)")
devParentMacSource = String(description="Source tracking for devParentMAC (USER, LOCKED, NEWDEV, or plugin prefix)")
devSSIDSource = String(description="Source tracking for devSSID (USER, LOCKED, NEWDEV, or plugin prefix)")
devParentMACSource = String(description="Source tracking for devParentMAC (USER, LOCKED, NEWDEV, or plugin prefix)")
devParentPortSource = String(description="Source tracking for devParentPort (USER, LOCKED, NEWDEV, or plugin prefix)")
devParentRelTypeSource = String(description="Source tracking for devParentRelType (USER, LOCKED, NEWDEV, or plugin prefix)")
devVlanSource = String(description="Source tracking for devVlan")

View File

@@ -147,11 +147,11 @@ class DeviceInfo(BaseModel):
devStatus: Optional[str] = Field(None, description="Online/Offline status")
devMacSource: Optional[str] = Field(None, description="Source of devMac (USER, LOCKED, or plugin prefix)")
devNameSource: Optional[str] = Field(None, description="Source of devName")
devFqdnSource: Optional[str] = Field(None, description="Source of devFQDN")
devLastIpSource: Optional[str] = Field(None, description="Source of devLastIP")
devFQDNSource: Optional[str] = Field(None, description="Source of devFQDN")
devLastIPSource: Optional[str] = Field(None, description="Source of devLastIP")
devVendorSource: Optional[str] = Field(None, description="Source of devVendor")
devSsidSource: Optional[str] = Field(None, description="Source of devSSID")
devParentMacSource: Optional[str] = Field(None, description="Source of devParentMAC")
devSSIDSource: Optional[str] = Field(None, description="Source of devSSID")
devParentMACSource: Optional[str] = Field(None, description="Source of devParentMAC")
devParentPortSource: Optional[str] = Field(None, description="Source of devParentPort")
devParentRelTypeSource: Optional[str] = Field(None, description="Source of devParentRelType")
devVlanSource: Optional[str] = Field(None, description="Source of devVlan")

View File

@@ -96,11 +96,11 @@ sql_devices_all = """
IFNULL(devReqNicsOnline, '') AS devReqNicsOnline,
IFNULL(devMacSource, '') AS devMacSource,
IFNULL(devNameSource, '') AS devNameSource,
IFNULL(devFqdnSource, '') AS devFqdnSource,
IFNULL(devLastIpSource, '') AS devLastIpSource,
IFNULL(devFQDNSource, '') AS devFQDNSource,
IFNULL(devLastIPSource, '') AS devLastIPSource,
IFNULL(devVendorSource, '') AS devVendorSource,
IFNULL(devSsidSource, '') AS devSsidSource,
IFNULL(devParentMacSource, '') AS devParentMacSource,
IFNULL(devSSIDSource, '') AS devSSIDSource,
IFNULL(devParentMACSource, '') AS devParentMACSource,
IFNULL(devParentPortSource, '') AS devParentPortSource,
IFNULL(devParentRelTypeSource, '') AS devParentRelTypeSource,
IFNULL(devVlanSource, '') AS devVlanSource,

View File

@@ -24,11 +24,11 @@ from db.db_helper import row_to_json # noqa: E402 [flake8 lint suppression]
FIELD_SOURCE_MAP = {
"devMac": "devMacSource",
"devName": "devNameSource",
"devFQDN": "devFqdnSource",
"devLastIP": "devLastIpSource",
"devFQDN": "devFQDNSource",
"devLastIP": "devLastIPSource",
"devVendor": "devVendorSource",
"devSSID": "devSsidSource",
"devParentMAC": "devParentMacSource",
"devSSID": "devSSIDSource",
"devParentMAC": "devParentMACSource",
"devParentPort": "devParentPortSource",
"devParentRelType": "devParentRelTypeSource",
"devVlan": "devVlanSource",

View File

@@ -50,11 +50,11 @@ EXPECTED_DEVICES_COLUMNS = [
"devSourcePlugin",
"devMacSource",
"devNameSource",
"devFqdnSource",
"devLastIpSource",
"devFQDNSource",
"devLastIPSource",
"devVendorSource",
"devSsidSource",
"devParentMacSource",
"devSSIDSource",
"devParentMACSource",
"devParentPortSource",
"devParentRelTypeSource",
"devVlanSource",

View File

@@ -115,7 +115,7 @@ FIELD_SPECS = {
# ==========================================================
"devLastIP": {
"scan_col": "cur_IP",
"source_col": "devLastIpSource",
"source_col": "devLastIPSource",
"empty_values": ["", "null", "(unknown)", "(Unknown)"],
"priority": ["ARPSCAN", "NEWDEV", "N/A"],
"default_value": "0.0.0.0",
@@ -177,7 +177,7 @@ FIELD_SPECS = {
# ==========================================================
"devParentMAC": {
"scan_col": "cur_NetworkNodeMAC",
"source_col": "devParentMacSource",
"source_col": "devParentMACSource",
"empty_values": ["", "null"],
"priority": ["SNMPDSC", "UNIFIAPI", "UNFIMP", "NEWDEV", "N/A"],
},
@@ -801,11 +801,11 @@ def create_new_devices(db):
devSourcePlugin,
devMacSource,
devNameSource,
devFqdnSource,
devLastIpSource,
devFQDNSource,
devLastIPSource,
devVendorSource,
devSsidSource,
devParentMacSource,
devSSIDSource,
devParentMACSource,
devParentPortSource,
devParentRelTypeSource,
devVlanSource,
@@ -1031,7 +1031,7 @@ def update_devices_names(pm):
"devName", "devNameSource", plugin_settings
)
fqdn_clause = get_overwrite_sql_clause(
"devFQDN", "devFqdnSource", plugin_settings
"devFQDN", "devFQDNSource", plugin_settings
)
sql.executemany(
@@ -1048,9 +1048,9 @@ def update_devices_names(pm):
WHEN {fqdn_clause} THEN ?
ELSE devFQDN
END,
devFqdnSource = CASE
devFQDNSource = CASE
WHEN {fqdn_clause} THEN ?
ELSE devFqdnSource
ELSE devFQDNSource
END
WHERE devMac = ?""",
plugin_records,
@@ -1079,7 +1079,7 @@ def update_devices_names(pm):
for plugin_label, plugin_records in records_by_plugin.items():
plugin_settings = get_plugin_authoritative_settings(plugin_label)
fqdn_clause = get_overwrite_sql_clause(
"devFQDN", "devFqdnSource", plugin_settings
"devFQDN", "devFQDNSource", plugin_settings
)
# Apply FQDN-only updates
@@ -1089,9 +1089,9 @@ def update_devices_names(pm):
WHEN {fqdn_clause} THEN ?
ELSE devFQDN
END,
devFqdnSource = CASE
devFQDNSource = CASE
WHEN {fqdn_clause} THEN ?
ELSE devFqdnSource
ELSE devFQDNSource
END
WHERE devMac = ?""",
plugin_records,

View File

@@ -129,11 +129,11 @@ class TestFieldSourceMapping:
expected_fields = {
"devMac": "devMacSource",
"devName": "devNameSource",
"devFQDN": "devFqdnSource",
"devLastIP": "devLastIpSource",
"devFQDN": "devFQDNSource",
"devLastIP": "devLastIPSource",
"devVendor": "devVendorSource",
"devSSID": "devSsidSource",
"devParentMAC": "devParentMacSource",
"devSSID": "devSSIDSource",
"devParentMAC": "devParentMACSource",
"devParentPort": "devParentPortSource",
"devParentRelType": "devParentRelTypeSource",
"devVlan": "devVlanSource",

View File

@@ -414,7 +414,7 @@ class TestFieldLockIntegration:
device_data = device_handler.getDeviceData(test_mac)
assert device_data.get("devNameSource") == "NEWDEV"
assert device_data.get("devVendorSource") == "NEWDEV"
assert device_data.get("devSsidSource") == "NEWDEV"
assert device_data.get("devSSIDSource") == "NEWDEV"
# Simulate a UI "save" that resubmits the same values.
update_result = device_handler.setDeviceData(
@@ -430,7 +430,7 @@ class TestFieldLockIntegration:
device_data = device_handler.getDeviceData(test_mac)
assert device_data.get("devNameSource") == "NEWDEV"
assert device_data.get("devVendorSource") == "NEWDEV"
assert device_data.get("devSsidSource") == "NEWDEV"
assert device_data.get("devSSIDSource") == "NEWDEV"
def test_only_changed_fields_marked_user(self, test_mac):
"""When saving, only fields whose values changed should become USER."""
@@ -461,7 +461,7 @@ class TestFieldLockIntegration:
device_data = device_handler.getDeviceData(test_mac)
assert device_data.get("devNameSource") == "USER"
assert device_data.get("devVendorSource") == "NEWDEV"
assert device_data.get("devSsidSource") == "NEWDEV"
assert device_data.get("devSSIDSource") == "NEWDEV"
if __name__ == "__main__":

View File

@@ -39,19 +39,19 @@ def scan_db():
devNameSource TEXT DEFAULT 'NEWDEV',
devVendor TEXT,
devVendorSource TEXT DEFAULT 'NEWDEV',
devLastIpSource TEXT DEFAULT 'NEWDEV',
devLastIPSource TEXT DEFAULT 'NEWDEV',
devType TEXT,
devIcon TEXT,
devParentPort TEXT,
devParentPortSource TEXT DEFAULT 'NEWDEV',
devParentMAC TEXT,
devParentMacSource TEXT DEFAULT 'NEWDEV',
devParentMACSource TEXT DEFAULT 'NEWDEV',
devSite TEXT,
devSiteSource TEXT DEFAULT 'NEWDEV',
devSSID TEXT,
devSsidSource TEXT DEFAULT 'NEWDEV',
devSSIDSource TEXT DEFAULT 'NEWDEV',
devFQDN TEXT,
devFqdnSource TEXT DEFAULT 'NEWDEV',
devFQDNSource TEXT DEFAULT 'NEWDEV',
devParentRelType TEXT,
devParentRelTypeSource TEXT DEFAULT 'NEWDEV',
devVlan TEXT,
@@ -165,11 +165,11 @@ def scan_db_for_new_devices():
devSourcePlugin TEXT,
devMacSource TEXT,
devNameSource TEXT,
devFqdnSource TEXT,
devLastIpSource TEXT,
devFQDNSource TEXT,
devLastIPSource TEXT,
devVendorSource TEXT,
devSsidSource TEXT,
devParentMacSource TEXT,
devSSIDSource TEXT,
devParentMACSource TEXT,
devParentPortSource TEXT,
devParentRelTypeSource TEXT,
devVlanSource TEXT,
@@ -304,12 +304,12 @@ def test_create_new_devices_sets_sources(scan_db_for_new_devices):
devMacSource,
devNameSource,
devVendorSource,
devLastIpSource,
devSsidSource,
devParentMacSource,
devLastIPSource,
devSSIDSource,
devParentMACSource,
devParentPortSource,
devParentRelTypeSource,
devFqdnSource,
devFQDNSource,
devVlanSource
FROM Devices WHERE devMac = ?
""",
@@ -319,12 +319,12 @@ def test_create_new_devices_sets_sources(scan_db_for_new_devices):
assert row["devMacSource"] == "ARPSCAN"
assert row["devNameSource"] == "ARPSCAN"
assert row["devVendorSource"] == "ARPSCAN"
assert row["devLastIpSource"] == "ARPSCAN"
assert row["devSsidSource"] == "ARPSCAN"
assert row["devParentMacSource"] == "ARPSCAN"
assert row["devLastIPSource"] == "ARPSCAN"
assert row["devSSIDSource"] == "ARPSCAN"
assert row["devParentMACSource"] == "ARPSCAN"
assert row["devParentPortSource"] == "ARPSCAN"
assert row["devParentRelTypeSource"] == "NEWDEV"
assert row["devFqdnSource"] == "NEWDEV"
assert row["devFQDNSource"] == "NEWDEV"
assert row["devVlanSource"] == "NEWDEV"
@@ -337,7 +337,7 @@ def test_scan_updates_newdev_device_name(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
@@ -412,7 +412,7 @@ def test_scan_does_not_update_user_field_name(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
@@ -487,7 +487,7 @@ def test_scan_does_not_update_locked_field(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
@@ -562,7 +562,7 @@ def test_scan_updates_empty_vendor_field(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
@@ -637,7 +637,7 @@ def test_scan_updates_ip_addresses(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID,
devPrimaryIPv4, devPrimaryIPv6
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -717,7 +717,7 @@ def test_scan_updates_ipv6_without_changing_ipv4(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID,
devPrimaryIPv4, devPrimaryIPv6
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -796,7 +796,7 @@ def test_scan_updates_presence_status(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
@@ -855,7 +855,7 @@ def test_scan_multiple_devices_mixed_sources(scan_db, mock_device_handlers):
"""
INSERT INTO Devices (
devMac, devLastConnection, devPresentLastScan, devLastIP,
devName, devNameSource, devVendor, devVendorSource, devLastIpSource,
devName, devNameSource, devVendor, devVendorSource, devLastIPSource,
devType, devIcon, devParentPort, devParentMAC, devSite, devSSID
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",

View File

@@ -31,7 +31,7 @@ def ip_test_db():
devPresentLastScan INTEGER,
devForceStatus TEXT,
devLastIP TEXT,
devLastIpSource TEXT DEFAULT 'NEWDEV',
devLastIPSource TEXT DEFAULT 'NEWDEV',
devPrimaryIPv4 TEXT,
devPrimaryIPv4Source TEXT DEFAULT 'NEWDEV',
devPrimaryIPv6 TEXT,