mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-09-21 13:24:59 -04:00
Merge pull request #1800 from mauricio-camayo/dockerdisc-v2-import-on
DOCKERDISC v2: optional device creation for LAN-visible containers
This commit is contained in:
4 files changed
+245
-29
No files matched your search
@@ -5,15 +5,20 @@ the list of containers running on them - image, Compose project/service,
|
|||||||
network driver, and (for containers on a `macvlan`/`ipvlan` network) their
|
network driver, and (for containers on a `macvlan`/`ipvlan` network) their
|
||||||
own MAC/IP.
|
own MAC/IP.
|
||||||
|
|
||||||
It does **not** discover devices. NetAlertX's own ARP/Nmap scanners remain
|
It never creates the Docker **host** itself as a device - that must already
|
||||||
the only source of device presence. `DOCKERDISC` never creates a device row
|
exist in NetAlertX before this plugin can attach anything to it. A
|
||||||
- not for a container, and not for the Docker host itself, which must
|
**container**, on the other hand, can optionally get its own device too:
|
||||||
already exist in NetAlertX before this plugin can attach anything to it.
|
opt in with `DOCKERDISC_CREATE_DEV` and a container on a `macvlan`/`ipvlan`
|
||||||
|
network (its own LAN-visible MAC) can create or confirm a device of its
|
||||||
|
own, parented to its host. A container without its own MAC
|
||||||
|
(`bridge`/overlay/etc.) never creates one, regardless of that setting -
|
||||||
|
there's no LAN-visible identity to create a device from.
|
||||||
|
|
||||||
Maintainer's mental model for this plugin: **Device = Docker host → List of
|
Maintainer's mental model for this plugin: **Device = Docker host → List of
|
||||||
containers.** Every container found on a host shows up under that **host's
|
containers.** Every container found on a host shows up under that **host's
|
||||||
own** Device Details → Plugins → DOCKERDISC tab, not as a device of its
|
own** Device Details → Plugins → DOCKERDISC tab either way - opting a
|
||||||
own.
|
container into its own device is additive, it doesn't remove it from the
|
||||||
|
host's list.
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Connects via a read-only [Docker Socket
|
> Connects via a read-only [Docker Socket
|
||||||
@@ -128,6 +133,20 @@ create/start/stop/kill anything.
|
|||||||
- Docker Host MAC Address (Fallback) `DOCKERDISC_HOST_MAC` - optional if
|
- Docker Host MAC Address (Fallback) `DOCKERDISC_HOST_MAC` - optional if
|
||||||
auto-detection works for that host
|
auto-detection works for that host
|
||||||
|
|
||||||
|
#### Optional Settings
|
||||||
|
|
||||||
|
- Create/confirm devices for containers with a real MAC
|
||||||
|
`DOCKERDISC_CREATE_DEV` - off by default. A container without its own
|
||||||
|
MAC (`bridge`/overlay/etc.) never creates a device either way; this only
|
||||||
|
affects containers on a `macvlan`/`ipvlan` network.
|
||||||
|
- Allow updating existing devices from this plugin's data
|
||||||
|
`DOCKERDISC_IMPORT_ON` - on by default. Turn off to make a run purely
|
||||||
|
informational: no `CurrentScan` promotion at all, so a container's own
|
||||||
|
already-existing device (found independently by ARP/Nmap) won't get its
|
||||||
|
presence/IP/parent fields updated from this plugin either, regardless of
|
||||||
|
`DOCKERDISC_CREATE_DEV`. The two settings are independent - one doesn't
|
||||||
|
gate the other.
|
||||||
|
|
||||||
### Host MAC auto-detection
|
### Host MAC auto-detection
|
||||||
|
|
||||||
If `DOCKERDISC_HOST_MAC` is filled in, it's used immediately - no Socket
|
If `DOCKERDISC_HOST_MAC` is filled in, it's used immediately - no Socket
|
||||||
@@ -172,10 +191,21 @@ whether it has a real LAN-visible identity to show:
|
|||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- This plugin never writes to `devMac`, `devLastIP`, `devFirstConnection`,
|
- The Docker host's own `devMac`/`devLastIP`/`devFirstConnection`/
|
||||||
`devSourcePlugin`, or `devCustomProps` - ARP/Nmap remain authoritative
|
`devSourcePlugin`/`devCustomProps` are never touched - ARP/Nmap remain
|
||||||
for device identity and discovery-source attribution on every device,
|
authoritative for the host's identity and discovery-source attribution.
|
||||||
including the Docker host itself.
|
A container with its own macvlan/ipvlan MAC always maps to `CurrentScan`
|
||||||
|
(`DOCKERDISC_IMPORT_ON` permitting) - with `DOCKERDISC_CREATE_DEV` on,
|
||||||
|
it can originate a brand-new device (`devSourcePlugin` set to
|
||||||
|
`DOCKERDISC` at creation, like any other `CurrentScan`-mapped plugin);
|
||||||
|
either way, if that MAC is already a device (found independently by
|
||||||
|
ARP/Nmap, since it's LAN-visible), this plugin's row still confirms its
|
||||||
|
presence and updates its `devLastIP`/`devParentMAC` on every run - that
|
||||||
|
existing device's own `devSourcePlugin` isn't touched, since it's only
|
||||||
|
set once, at creation. Turn `DOCKERDISC_IMPORT_ON` off to skip all of
|
||||||
|
that and keep this plugin purely informational (its `Plugins_Objects`
|
||||||
|
listing still updates either way) - see [Plugin Import
|
||||||
|
Behavior](../../../docs/PLUGINS_IMPORT_BEHAVIOR.md).
|
||||||
- Only Socket Proxy permissions required: `CONTAINERS=1` (list containers,
|
- Only Socket Proxy permissions required: `CONTAINERS=1` (list containers,
|
||||||
their networks and labels), `INFO=1` (host-MAC auto-detection), and
|
their networks and labels), `INFO=1` (host-MAC auto-detection), and
|
||||||
`NETWORKS=1` (network driver lookup - one batched `GET /networks` call
|
`NETWORKS=1` (network driver lookup - one batched `GET /networks` call
|
||||||
@@ -184,6 +214,6 @@ whether it has a real LAN-visible identity to show:
|
|||||||
- Design history and open implementation questions in [issue #1721]
|
- Design history and open implementation questions in [issue #1721]
|
||||||
(https://github.com/netalertx/NetAlertX/issues/1721).
|
(https://github.com/netalertx/NetAlertX/issues/1721).
|
||||||
|
|
||||||
- Version: 0.1.0
|
- Version: 0.2.0
|
||||||
- Author: [mauricio-camayo](https://github.com/mauricio-camayo/)
|
- Author: [mauricio-camayo](https://github.com/mauricio-camayo/)
|
||||||
- Release Date: `2026-09-14`
|
- Release Date: `2026-09-18`
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"description": [
|
"description": [
|
||||||
{
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string": "Enriches known Docker hosts with their running containers - image, Compose project/service, network, and MAC/IP when available. Never creates devices; connects via a read-only Docker Socket Proxy."
|
"string": "Enriches known Docker hosts with their running containers."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"params": [
|
"params": [
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
"timeoutMultiplier": true
|
"timeoutMultiplier": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"mapped_to_table": "CurrentScan",
|
||||||
"database_column_definitions": [
|
"database_column_definitions": [
|
||||||
{
|
{
|
||||||
"column": "index",
|
"column": "index",
|
||||||
@@ -74,6 +75,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"column": "objectPrimaryId",
|
"column": "objectPrimaryId",
|
||||||
|
"mapped_to_column": "scanParentMAC",
|
||||||
"css_classes": "col-sm-2",
|
"css_classes": "col-sm-2",
|
||||||
"show": true,
|
"show": true,
|
||||||
"type": "device_mac",
|
"type": "device_mac",
|
||||||
@@ -210,6 +212,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"column": "extra",
|
"column": "extra",
|
||||||
|
"mapped_to_column": "scanLastIP",
|
||||||
"css_classes": "col-sm-3",
|
"css_classes": "col-sm-3",
|
||||||
"show": true,
|
"show": true,
|
||||||
"type": "label",
|
"type": "label",
|
||||||
@@ -225,6 +228,63 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"column": "helpVal1",
|
||||||
|
"mapped_to_column": "scanMac",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": false,
|
||||||
|
"type": "none",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "N/A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "helpVal2",
|
||||||
|
"mapped_to_column": "scanCreatesDevice",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": false,
|
||||||
|
"type": "none",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "N/A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "Dummy",
|
||||||
|
"mapped_to_column": "scanSourcePlugin",
|
||||||
|
"mapped_to_column_data": {
|
||||||
|
"value": "DOCKERDISC"
|
||||||
|
},
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": false,
|
||||||
|
"type": "none",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "N/A"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"column": "userData",
|
"column": "userData",
|
||||||
"css_classes": "col-sm-2",
|
"css_classes": "col-sm-2",
|
||||||
@@ -317,6 +377,41 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"function": "IMPORT_ON",
|
||||||
|
"type": {
|
||||||
|
"dataType": "boolean",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [
|
||||||
|
{
|
||||||
|
"type": "checkbox"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": true,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Allow updating existing devices from this plugin's data"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "On by default. Turn off to make this run purely informational - no CurrentScan promotion at all, so a container's own already-existing device (found independently by ARP/Nmap) won't get its presence/IP/parent updated from this plugin either, regardless of <a href=\"#DOCKERDISC_CREATE_DEV\"><code>DOCKERDISC_CREATE_DEV</code></a>."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"function": "CMD",
|
"function": "CMD",
|
||||||
"type": {
|
"type": {
|
||||||
@@ -624,6 +719,41 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"function": "CREATE_DEV",
|
||||||
|
"type": {
|
||||||
|
"dataType": "boolean",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [
|
||||||
|
{
|
||||||
|
"type": "checkbox"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": false,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Create/confirm devices for containers with a real MAC"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "When on, a container on a macvlan/ipvlan network (its own LAN-visible MAC) can create or confirm its own device entry, parented to its Docker host. Containers without their own MAC (bridge/overlay/etc.) never create a device either way."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"function": "WATCH",
|
"function": "WATCH",
|
||||||
"type": {
|
"type": {
|
||||||
|
|||||||
@@ -1,21 +1,30 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""NetAlertX plugin: DOCKERDISC - Docker discovery (enrichment, not import)
|
"""NetAlertX plugin: DOCKERDISC - Docker discovery
|
||||||
|
|
||||||
Does NOT discover devices. NetAlertX's own ARP/Nmap scanners remain the
|
For each configured Docker host, lists that host's containers under the
|
||||||
sole source of device presence. Instead, for each configured Docker host
|
*host's own* Device Details -> Plugins -> DOCKERDISC tab. The host itself
|
||||||
this plugin lists that host's containers under the *host's own* Device
|
is never created by this plugin - it must already exist in NetAlertX
|
||||||
Details -> Plugins -> DOCKERDISC tab.
|
(found the normal way, via ARP/Nmap).
|
||||||
|
|
||||||
- objectPrimaryId / foreignKey is always the Docker HOST's MAC - never a
|
- objectPrimaryId / foreignKey is always the Docker HOST's MAC - never a
|
||||||
container's own MAC. Every plugin object (one per container) attaches
|
container's own MAC. Every plugin object (one per container) attaches
|
||||||
to the host device, which must already exist in NetAlertX (found the
|
to the host device.
|
||||||
normal way, via ARP/Nmap). This plugin never creates a device row, for
|
|
||||||
either a host or a container.
|
|
||||||
- Because matching targets the host (persistent LAN identity), not the
|
- Because matching targets the host (persistent LAN identity), not the
|
||||||
container, EVERY container is listed - bridge/overlay ones included -
|
container, EVERY container is listed - bridge/overlay ones included -
|
||||||
not only macvlan/ipvlan ones. A container only gets its own MAC/IP
|
not only macvlan/ipvlan ones. A container only gets its own MAC/IP
|
||||||
shown (watched4/extra) when it has a macvlan/ipvlan network; otherwise
|
shown (watched4/extra) when it has a macvlan/ipvlan network; otherwise
|
||||||
those fields are "null".
|
those fields are "null".
|
||||||
|
- Also maps to CurrentScan (scanMac/scanCreatesDevice/scanParentMAC/
|
||||||
|
scanLastIP - see docs/PLUGINS_IMPORT_BEHAVIOR.md), gated by
|
||||||
|
DOCKERDISC_IMPORT_ON (whether this run promotes to CurrentScan at all)
|
||||||
|
and, independently, DOCKERDISC_CREATE_DEV (whether a container with
|
||||||
|
its own MAC may originate a brand-new device via scanCreatesDevice -
|
||||||
|
neither setting gates the other). A container without its own MAC
|
||||||
|
(bridge/overlay/etc.) always gets a blank scanMac, which blocks device
|
||||||
|
creation for the whole group regardless of scanCreatesDevice - it can
|
||||||
|
never be its own device. One with a real MAC is parented to its host
|
||||||
|
via scanParentMAC on every promoted run, whether or not CREATE_DEV
|
||||||
|
lets it also originate a device.
|
||||||
- One `hosts` entry = one Docker host: a read-only Docker Socket Proxy
|
- One `hosts` entry = one Docker host: a read-only Docker Socket Proxy
|
||||||
URL, plus a manual MAC fallback for when auto-detection (via the
|
URL, plus a manual MAC fallback for when auto-detection (via the
|
||||||
proxy's own /info endpoint) doesn't resolve to a known device. Never
|
proxy's own /info endpoint) doesn't resolve to a known device. Never
|
||||||
@@ -263,7 +272,13 @@ def first_network_driver(networks, driver_by_id):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def process_host(host_entry, deadline, plugin_objects):
|
def process_host(host_entry, deadline, plugin_objects, create_dev):
|
||||||
|
"""Lists one Docker host's containers as plugin objects under that
|
||||||
|
host's Device Details tab, and maps each to a CurrentScan row. Skips
|
||||||
|
the whole host (no containers listed) if its Socket Proxy URL is
|
||||||
|
missing, its MAC can't be resolved, or that MAC isn't a known device.
|
||||||
|
Returns the number of containers reported."""
|
||||||
|
|
||||||
host = DockerHost(
|
host = DockerHost(
|
||||||
proxy_url=host_entry.get('DOCKERDISC_SOCKET_PROXY_URL'),
|
proxy_url=host_entry.get('DOCKERDISC_SOCKET_PROXY_URL'),
|
||||||
manual_mac=host_entry.get('DOCKERDISC_HOST_MAC'),
|
manual_mac=host_entry.get('DOCKERDISC_HOST_MAC'),
|
||||||
@@ -317,6 +332,16 @@ def process_host(host_entry, deadline, plugin_objects):
|
|||||||
names = container.get('Names') or []
|
names = container.get('Names') or []
|
||||||
container_name = names[0].lstrip('/') if names else container.get('Id', '')[:12]
|
container_name = names[0].lstrip('/') if names else container.get('Id', '')[:12]
|
||||||
|
|
||||||
|
# scanMac/scanCreatesDevice (helpVal1/helpVal2, mapped in config.json)
|
||||||
|
# drive whether this row can promote to its own CurrentScan/Devices
|
||||||
|
# entry - see docs/PLUGINS_IMPORT_BEHAVIOR.md. A container without
|
||||||
|
# its own LAN-visible MAC (bridge/overlay/etc.) always gets a blank
|
||||||
|
# scanMac, which blocks device creation for the whole group
|
||||||
|
# regardless of scanCreatesDevice - it can never be its own device.
|
||||||
|
# One with a real MAC only creates/confirms a device when the user
|
||||||
|
# opted in via DOCKERDISC_CREATE_DEV.
|
||||||
|
can_create_device = bool(container_mac) and create_dev
|
||||||
|
|
||||||
plugin_objects.add_object(
|
plugin_objects.add_object(
|
||||||
primaryId=host_mac,
|
primaryId=host_mac,
|
||||||
secondaryId=handleEmpty(container_name),
|
secondaryId=handleEmpty(container_name),
|
||||||
@@ -326,6 +351,8 @@ def process_host(host_entry, deadline, plugin_objects):
|
|||||||
watched4=handleEmpty(container_mac),
|
watched4=handleEmpty(container_mac),
|
||||||
extra=handleEmpty(container_ip),
|
extra=handleEmpty(container_ip),
|
||||||
foreignKey=host_mac,
|
foreignKey=host_mac,
|
||||||
|
helpVal1=container_mac,
|
||||||
|
helpVal2='1' if can_create_device else '0',
|
||||||
)
|
)
|
||||||
added += 1
|
added += 1
|
||||||
|
|
||||||
@@ -333,9 +360,14 @@ def process_host(host_entry, deadline, plugin_objects):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""Entry point: reads the configured Docker hosts and DOCKERDISC_CREATE_DEV,
|
||||||
|
processes each host in turn against a shared per-run request-time
|
||||||
|
budget, and writes the combined result file."""
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] In script'])
|
mylog('verbose', [f'[{pluginName}] In script'])
|
||||||
|
|
||||||
host_configs = get_setting_value('DOCKERDISC_hosts') or []
|
host_configs = get_setting_value('DOCKERDISC_hosts') or []
|
||||||
|
create_dev = bool(get_setting_value('DOCKERDISC_CREATE_DEV'))
|
||||||
run_timeout = get_setting_value('DOCKERDISC_RUN_TIMEOUT') or REQUEST_TIMEOUT_DEFAULT
|
run_timeout = get_setting_value('DOCKERDISC_RUN_TIMEOUT') or REQUEST_TIMEOUT_DEFAULT
|
||||||
# One shared deadline for the whole run (every host, every request) -
|
# One shared deadline for the whole run (every host, every request) -
|
||||||
# config.json's "hosts" param has timeoutMultiplier set, so the outer
|
# config.json's "hosts" param has timeoutMultiplier set, so the outer
|
||||||
@@ -351,7 +383,7 @@ def main():
|
|||||||
total_added = 0
|
total_added = 0
|
||||||
for host_config in host_configs:
|
for host_config in host_configs:
|
||||||
host_entry = decode_settings_base64(host_config)
|
host_entry = decode_settings_base64(host_config)
|
||||||
total_added += process_host(host_entry, deadline, plugin_objects)
|
total_added += process_host(host_entry, deadline, plugin_objects, create_dev)
|
||||||
|
|
||||||
plugin_objects.write_result_file()
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ def _container(name, image, driver, mac=None, ip=None, project=None, service=Non
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_process_host_mixed_macvlan_and_bridge_containers():
|
def _run_mixed_containers(create_dev):
|
||||||
host_entry = {
|
host_entry = {
|
||||||
"DOCKERDISC_SOCKET_PROXY_URL": "http://proxy:2375",
|
"DOCKERDISC_SOCKET_PROXY_URL": "http://proxy:2375",
|
||||||
"DOCKERDISC_HOST_MAC": "aa:bb:cc:dd:ee:ff",
|
"DOCKERDISC_HOST_MAC": "aa:bb:cc:dd:ee:ff",
|
||||||
@@ -506,7 +506,7 @@ def test_process_host_mixed_macvlan_and_bridge_containers():
|
|||||||
with patch.object(host, "get_containers", return_value=containers):
|
with patch.object(host, "get_containers", return_value=containers):
|
||||||
with patch.object(host, "get_network_drivers", return_value={"net-lan": "macvlan", "net-bridge": "bridge"}) as mock_drivers:
|
with patch.object(host, "get_network_drivers", return_value={"net-lan": "macvlan", "net-bridge": "bridge"}) as mock_drivers:
|
||||||
with patch.object(dockerdisc, "DeviceInstance", _stub_device_instance(get_by_mac={"devMac": host_entry["DOCKERDISC_HOST_MAC"]})):
|
with patch.object(dockerdisc, "DeviceInstance", _stub_device_instance(get_by_mac={"devMac": host_entry["DOCKERDISC_HOST_MAC"]})):
|
||||||
added = dockerdisc.process_host(host_entry, _deadline(), plugin_objects)
|
added = dockerdisc.process_host(host_entry, _deadline(), plugin_objects, create_dev)
|
||||||
|
|
||||||
# one batched call for both containers' networks, not two
|
# one batched call for both containers' networks, not two
|
||||||
mock_drivers.assert_called_once()
|
mock_drivers.assert_called_once()
|
||||||
@@ -516,6 +516,13 @@ def test_process_host_mixed_macvlan_and_bridge_containers():
|
|||||||
assert plugin_objects.add_object.call_count == 2
|
assert plugin_objects.add_object.call_count == 2
|
||||||
|
|
||||||
pihole_call = plugin_objects.add_object.call_args_list[0].kwargs
|
pihole_call = plugin_objects.add_object.call_args_list[0].kwargs
|
||||||
|
redis_call = plugin_objects.add_object.call_args_list[1].kwargs
|
||||||
|
return pihole_call, redis_call
|
||||||
|
|
||||||
|
|
||||||
|
def test_process_host_mixed_macvlan_and_bridge_containers_create_dev_off():
|
||||||
|
pihole_call, redis_call = _run_mixed_containers(create_dev=False)
|
||||||
|
|
||||||
assert pihole_call["primaryId"] == "aa:bb:cc:dd:ee:ff" # host MAC, not the container's
|
assert pihole_call["primaryId"] == "aa:bb:cc:dd:ee:ff" # host MAC, not the container's
|
||||||
assert pihole_call["foreignKey"] == "aa:bb:cc:dd:ee:ff"
|
assert pihole_call["foreignKey"] == "aa:bb:cc:dd:ee:ff"
|
||||||
assert pihole_call["secondaryId"] == "pihole"
|
assert pihole_call["secondaryId"] == "pihole"
|
||||||
@@ -523,18 +530,35 @@ def test_process_host_mixed_macvlan_and_bridge_containers():
|
|||||||
assert pihole_call["watched3"] == "macvlan"
|
assert pihole_call["watched3"] == "macvlan"
|
||||||
assert pihole_call["watched4"] == "aa:aa:aa:aa:aa:01"
|
assert pihole_call["watched4"] == "aa:aa:aa:aa:aa:01"
|
||||||
assert pihole_call["extra"] == "192.168.1.50"
|
assert pihole_call["extra"] == "192.168.1.50"
|
||||||
|
# real MAC, but DOCKERDISC_CREATE_DEV is off - never originates a device
|
||||||
|
assert pihole_call["helpVal1"] == "aa:aa:aa:aa:aa:01"
|
||||||
|
assert pihole_call["helpVal2"] == "0"
|
||||||
|
|
||||||
redis_call = plugin_objects.add_object.call_args_list[1].kwargs
|
|
||||||
assert redis_call["primaryId"] == "aa:bb:cc:dd:ee:ff" # same host, not skipped for lacking a LAN MAC
|
assert redis_call["primaryId"] == "aa:bb:cc:dd:ee:ff" # same host, not skipped for lacking a LAN MAC
|
||||||
assert redis_call["watched3"] == "bridge"
|
assert redis_call["watched3"] == "bridge"
|
||||||
assert redis_call["watched4"] == "null" # no LAN-visible MAC for a bridge-only container
|
assert redis_call["watched4"] == "null" # no LAN-visible MAC for a bridge-only container
|
||||||
assert redis_call["extra"] == "null"
|
assert redis_call["extra"] == "null"
|
||||||
|
# no LAN-visible MAC - blank scanMac blocks device creation regardless
|
||||||
|
assert redis_call["helpVal1"] == ""
|
||||||
|
assert redis_call["helpVal2"] == "0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_process_host_mixed_macvlan_and_bridge_containers_create_dev_on():
|
||||||
|
pihole_call, redis_call = _run_mixed_containers(create_dev=True)
|
||||||
|
|
||||||
|
# real MAC + opted in - can create/confirm its own device
|
||||||
|
assert pihole_call["helpVal1"] == "aa:aa:aa:aa:aa:01"
|
||||||
|
assert pihole_call["helpVal2"] == "1"
|
||||||
|
|
||||||
|
# still no LAN-visible MAC - opting in doesn't change a bridge container's fate
|
||||||
|
assert redis_call["helpVal1"] == ""
|
||||||
|
assert redis_call["helpVal2"] == "0"
|
||||||
|
|
||||||
|
|
||||||
def test_process_host_skips_unconfigured_entry_without_any_request():
|
def test_process_host_skips_unconfigured_entry_without_any_request():
|
||||||
plugin_objects = MagicMock()
|
plugin_objects = MagicMock()
|
||||||
with patch("requests.get") as mock_get:
|
with patch("requests.get") as mock_get:
|
||||||
added = dockerdisc.process_host({"DOCKERDISC_SOCKET_PROXY_URL": "", "DOCKERDISC_HOST_MAC": ""}, _deadline(), plugin_objects)
|
added = dockerdisc.process_host({"DOCKERDISC_SOCKET_PROXY_URL": "", "DOCKERDISC_HOST_MAC": ""}, _deadline(), plugin_objects, False)
|
||||||
assert added == 0
|
assert added == 0
|
||||||
mock_get.assert_not_called()
|
mock_get.assert_not_called()
|
||||||
plugin_objects.add_object.assert_not_called()
|
plugin_objects.add_object.assert_not_called()
|
||||||
@@ -544,7 +568,7 @@ def test_process_host_skips_when_host_mac_unresolved():
|
|||||||
host_entry = {"DOCKERDISC_SOCKET_PROXY_URL": "http://proxy:2375", "DOCKERDISC_HOST_MAC": ""}
|
host_entry = {"DOCKERDISC_SOCKET_PROXY_URL": "http://proxy:2375", "DOCKERDISC_HOST_MAC": ""}
|
||||||
plugin_objects = MagicMock()
|
plugin_objects = MagicMock()
|
||||||
with patch.object(dockerdisc.DockerHost, "get_info", return_value=None):
|
with patch.object(dockerdisc.DockerHost, "get_info", return_value=None):
|
||||||
added = dockerdisc.process_host(host_entry, _deadline(), plugin_objects)
|
added = dockerdisc.process_host(host_entry, _deadline(), plugin_objects, False)
|
||||||
assert added == 0
|
assert added == 0
|
||||||
plugin_objects.add_object.assert_not_called()
|
plugin_objects.add_object.assert_not_called()
|
||||||
|
|
||||||
@@ -555,7 +579,7 @@ def test_process_host_skips_when_host_not_a_known_device_without_listing_contain
|
|||||||
with patch.object(dockerdisc.DockerHost, "get_info", return_value=None):
|
with patch.object(dockerdisc.DockerHost, "get_info", return_value=None):
|
||||||
with patch.object(dockerdisc, "DeviceInstance", _stub_device_instance(get_by_mac=None)): # not found
|
with patch.object(dockerdisc, "DeviceInstance", _stub_device_instance(get_by_mac=None)): # not found
|
||||||
with patch.object(dockerdisc.DockerHost, "get_containers") as mock_get_containers:
|
with patch.object(dockerdisc.DockerHost, "get_containers") as mock_get_containers:
|
||||||
added = dockerdisc.process_host(host_entry, _deadline(), plugin_objects)
|
added = dockerdisc.process_host(host_entry, _deadline(), plugin_objects, False)
|
||||||
assert added == 0
|
assert added == 0
|
||||||
mock_get_containers.assert_not_called()
|
mock_get_containers.assert_not_called()
|
||||||
plugin_objects.add_object.assert_not_called()
|
plugin_objects.add_object.assert_not_called()
|
||||||
|
|||||||
Reference in new issue
Block a user