Add diagnostics support to UniFi Network integration (#64640)
* Add diagnostics support to UniFi Network integration * Remove system info * Add test data before trying to redact sensitive information * Redact sensitive information from config_entry.data * Redact a lot of different parts of the device descriptions Redact mac addresses in such a way its possible to still view topologypull/64765/head
parent
02df6eb10e
commit
20072140c6
|
@ -0,0 +1,99 @@
|
|||
"""Diagnostics support for UniFi Network."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from itertools import chain
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import REDACTED, async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
|
||||
from .const import CONF_CONTROLLER, DOMAIN as UNIFI_DOMAIN
|
||||
|
||||
TO_REDACT = {CONF_CONTROLLER, CONF_PASSWORD}
|
||||
REDACT_CLIENTS = {"bssid", "essid"}
|
||||
REDACT_DEVICES = {
|
||||
"anon_id",
|
||||
"gateway_mac",
|
||||
"geo_info",
|
||||
"serial",
|
||||
"x_authkey",
|
||||
"x_fingerprint",
|
||||
"x_iapp_key",
|
||||
"x_ssh_hostkey_fingerprint",
|
||||
"x_vwirekey",
|
||||
}
|
||||
REDACT_WLANS = {"bc_filter_list", "x_passphrase"}
|
||||
|
||||
|
||||
@callback
|
||||
def async_replace_data(data: Mapping, to_replace: dict[str, str]) -> dict[str, Any]:
|
||||
"""Replace sensitive data in a dict."""
|
||||
if not isinstance(data, (Mapping, list, set, tuple)):
|
||||
return to_replace.get(data, data)
|
||||
|
||||
redacted = {**data}
|
||||
|
||||
for key, value in redacted.items():
|
||||
if isinstance(value, dict):
|
||||
redacted[key] = async_replace_data(value, to_replace)
|
||||
elif isinstance(value, (list, set, tuple)):
|
||||
redacted[key] = [async_replace_data(item, to_replace) for item in value]
|
||||
elif isinstance(value, str):
|
||||
if value in to_replace:
|
||||
redacted[key] = to_replace[value]
|
||||
elif value.count(":") == 5:
|
||||
redacted[key] = REDACTED
|
||||
|
||||
return redacted
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
|
||||
diag: dict[str, Any] = {}
|
||||
macs_to_redact: dict[str, str] = {}
|
||||
|
||||
diag["config"] = async_redact_data(config_entry.data, TO_REDACT)
|
||||
diag["site_role"] = controller.site_role
|
||||
|
||||
counter = 0
|
||||
for mac in chain(controller.api.clients, controller.api.devices):
|
||||
macs_to_redact[mac] = format_mac(str(counter).zfill(12))
|
||||
counter += 1
|
||||
|
||||
for device in controller.api.devices.values():
|
||||
for entry in device.raw.get("ethernet_table", []):
|
||||
mac = entry.get("mac", "")
|
||||
if mac not in macs_to_redact:
|
||||
macs_to_redact[mac] = format_mac(str(counter).zfill(12))
|
||||
counter += 1
|
||||
|
||||
diag["options"] = async_replace_data(config_entry.options, macs_to_redact)
|
||||
|
||||
diag["entities"] = async_replace_data(controller.entities, macs_to_redact)
|
||||
diag["clients"] = {
|
||||
macs_to_redact[k]: async_redact_data(
|
||||
async_replace_data(v.raw, macs_to_redact), REDACT_CLIENTS
|
||||
)
|
||||
for k, v in controller.api.clients.items()
|
||||
}
|
||||
diag["devices"] = {
|
||||
macs_to_redact[k]: async_redact_data(
|
||||
async_replace_data(v.raw, macs_to_redact), REDACT_DEVICES
|
||||
)
|
||||
for k, v in controller.api.devices.items()
|
||||
}
|
||||
diag["dpi_apps"] = {k: v.raw for k, v in controller.api.dpi_apps.items()}
|
||||
diag["dpi_groups"] = {k: v.raw for k, v in controller.api.dpi_groups.items()}
|
||||
diag["wlans"] = {
|
||||
k: async_redact_data(async_replace_data(v.raw, macs_to_redact), REDACT_WLANS)
|
||||
for k, v in controller.api.wlans.items()
|
||||
}
|
||||
|
||||
return diag
|
|
@ -0,0 +1,235 @@
|
|||
"""Test UniFi Network diagnostics."""
|
||||
|
||||
from homeassistant.components.unifi.const import (
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS,
|
||||
CONF_ALLOW_UPTIME_SENSORS,
|
||||
CONF_BLOCK_CLIENT,
|
||||
)
|
||||
from homeassistant.components.unifi.device_tracker import CLIENT_TRACKER, DEVICE_TRACKER
|
||||
from homeassistant.components.unifi.sensor import RX_SENSOR, TX_SENSOR, UPTIME_SENSOR
|
||||
from homeassistant.components.unifi.switch import BLOCK_SWITCH, DPI_SWITCH, POE_SWITCH
|
||||
from homeassistant.const import Platform
|
||||
|
||||
from .test_controller import setup_unifi_integration
|
||||
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
|
||||
|
||||
async def test_entry_diagnostics(hass, hass_client, aioclient_mock):
|
||||
"""Test config entry diagnostics."""
|
||||
client = {
|
||||
"blocked": False,
|
||||
"hostname": "client_1",
|
||||
"ip": "10.0.0.1",
|
||||
"is_wired": True,
|
||||
"last_seen": 1562600145,
|
||||
"mac": "00:00:00:00:00:01",
|
||||
"name": "POE Client 1",
|
||||
"oui": "Producer",
|
||||
"sw_mac": "00:00:00:00:01:01",
|
||||
"sw_port": 1,
|
||||
"wired-rx_bytes": 1234000000,
|
||||
"wired-tx_bytes": 5678000000,
|
||||
}
|
||||
device = {
|
||||
"ethernet_table": [
|
||||
{
|
||||
"mac": "22:22:22:22:22:22",
|
||||
"num_port": 2,
|
||||
"name": "eth0",
|
||||
}
|
||||
],
|
||||
"device_id": "mock-id",
|
||||
"ip": "10.0.1.1",
|
||||
"mac": "00:00:00:00:01:01",
|
||||
"last_seen": 1562600145,
|
||||
"model": "US16P150",
|
||||
"name": "mock-name",
|
||||
"port_overrides": [],
|
||||
"port_table": [
|
||||
{
|
||||
"mac_table": [
|
||||
{
|
||||
"age": 1,
|
||||
"mac": "00:00:00:00:00:01",
|
||||
"static": False,
|
||||
"uptime": 3971792,
|
||||
"vlan": 1,
|
||||
},
|
||||
{
|
||||
"age": 1,
|
||||
"mac": "11:11:11:11:11:11",
|
||||
"static": True,
|
||||
"uptime": 0,
|
||||
"vlan": 0,
|
||||
},
|
||||
],
|
||||
"media": "GE",
|
||||
"name": "Port 1",
|
||||
"port_idx": 1,
|
||||
"poe_class": "Class 4",
|
||||
"poe_enable": True,
|
||||
"poe_mode": "auto",
|
||||
"poe_power": "2.56",
|
||||
"poe_voltage": "53.40",
|
||||
"portconf_id": "1a1",
|
||||
"port_poe": True,
|
||||
"up": True,
|
||||
},
|
||||
],
|
||||
"state": 1,
|
||||
"type": "usw",
|
||||
"version": "4.0.42.10433",
|
||||
}
|
||||
dpi_app = {
|
||||
"_id": "5f976f62e3c58f018ec7e17d",
|
||||
"apps": [],
|
||||
"blocked": True,
|
||||
"cats": ["4"],
|
||||
"enabled": True,
|
||||
"log": True,
|
||||
"site_id": "name",
|
||||
}
|
||||
dpi_group = {
|
||||
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||
"name": "Block Media Streaming",
|
||||
"site_id": "name",
|
||||
"dpiapp_ids": ["5f976f62e3c58f018ec7e17d"],
|
||||
}
|
||||
|
||||
options = {
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS: True,
|
||||
CONF_ALLOW_UPTIME_SENSORS: True,
|
||||
CONF_BLOCK_CLIENT: ["00:00:00:00:00:01"],
|
||||
}
|
||||
config_entry = await setup_unifi_integration(
|
||||
hass,
|
||||
aioclient_mock,
|
||||
options=options,
|
||||
clients_response=[client],
|
||||
devices_response=[device],
|
||||
dpiapp_response=[dpi_app],
|
||||
dpigroup_response=[dpi_group],
|
||||
)
|
||||
|
||||
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
|
||||
"config": {
|
||||
"controller": "**REDACTED**",
|
||||
"host": "1.2.3.4",
|
||||
"password": "**REDACTED**",
|
||||
"port": 1234,
|
||||
"site": "site_id",
|
||||
"username": "username",
|
||||
"verify_ssl": False,
|
||||
},
|
||||
"options": {
|
||||
"allow_bandwidth_sensors": True,
|
||||
"allow_uptime_sensors": True,
|
||||
"block_client": ["00:00:00:00:00:00"],
|
||||
},
|
||||
"site_role": "admin",
|
||||
"entities": {
|
||||
str(Platform.DEVICE_TRACKER): {
|
||||
CLIENT_TRACKER: ["00:00:00:00:00:00"],
|
||||
DEVICE_TRACKER: ["00:00:00:00:00:01"],
|
||||
},
|
||||
str(Platform.SENSOR): {
|
||||
RX_SENSOR: ["00:00:00:00:00:00"],
|
||||
TX_SENSOR: ["00:00:00:00:00:00"],
|
||||
UPTIME_SENSOR: ["00:00:00:00:00:00"],
|
||||
},
|
||||
str(Platform.SWITCH): {
|
||||
BLOCK_SWITCH: ["00:00:00:00:00:00"],
|
||||
DPI_SWITCH: ["5f976f4ae3c58f018ec7dff6"],
|
||||
POE_SWITCH: ["00:00:00:00:00:00"],
|
||||
},
|
||||
},
|
||||
"clients": {
|
||||
"00:00:00:00:00:00": {
|
||||
"blocked": False,
|
||||
"hostname": "client_1",
|
||||
"ip": "10.0.0.1",
|
||||
"is_wired": True,
|
||||
"last_seen": 1562600145,
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"name": "POE Client 1",
|
||||
"oui": "Producer",
|
||||
"sw_mac": "00:00:00:00:00:01",
|
||||
"sw_port": 1,
|
||||
"wired-rx_bytes": 1234000000,
|
||||
"wired-tx_bytes": 5678000000,
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"00:00:00:00:00:01": {
|
||||
"ethernet_table": [
|
||||
{
|
||||
"mac": "00:00:00:00:00:02",
|
||||
"num_port": 2,
|
||||
"name": "eth0",
|
||||
}
|
||||
],
|
||||
"device_id": "mock-id",
|
||||
"ip": "10.0.1.1",
|
||||
"mac": "00:00:00:00:00:01",
|
||||
"last_seen": 1562600145,
|
||||
"model": "US16P150",
|
||||
"name": "mock-name",
|
||||
"port_overrides": [],
|
||||
"port_table": [
|
||||
{
|
||||
"mac_table": [
|
||||
{
|
||||
"age": 1,
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"static": False,
|
||||
"uptime": 3971792,
|
||||
"vlan": 1,
|
||||
},
|
||||
{
|
||||
"age": 1,
|
||||
"mac": "**REDACTED**",
|
||||
"static": True,
|
||||
"uptime": 0,
|
||||
"vlan": 0,
|
||||
},
|
||||
],
|
||||
"media": "GE",
|
||||
"name": "Port 1",
|
||||
"port_idx": 1,
|
||||
"poe_class": "Class 4",
|
||||
"poe_enable": True,
|
||||
"poe_mode": "auto",
|
||||
"poe_power": "2.56",
|
||||
"poe_voltage": "53.40",
|
||||
"portconf_id": "1a1",
|
||||
"port_poe": True,
|
||||
"up": True,
|
||||
},
|
||||
],
|
||||
"state": 1,
|
||||
"type": "usw",
|
||||
"version": "4.0.42.10433",
|
||||
}
|
||||
},
|
||||
"dpi_apps": {
|
||||
"5f976f62e3c58f018ec7e17d": {
|
||||
"_id": "5f976f62e3c58f018ec7e17d",
|
||||
"apps": [],
|
||||
"blocked": True,
|
||||
"cats": ["4"],
|
||||
"enabled": True,
|
||||
"log": True,
|
||||
"site_id": "name",
|
||||
}
|
||||
},
|
||||
"dpi_groups": {
|
||||
"5f976f4ae3c58f018ec7dff6": {
|
||||
"_id": "5f976f4ae3c58f018ec7dff6",
|
||||
"name": "Block Media Streaming",
|
||||
"site_id": "name",
|
||||
"dpiapp_ids": ["5f976f62e3c58f018ec7e17d"],
|
||||
}
|
||||
},
|
||||
"wlans": {},
|
||||
}
|
Loading…
Reference in New Issue