From 589ca56cf725f1ab158a76778f191aeb8551a420 Mon Sep 17 00:00:00 2001 From: dougiteixeira <31328123+dougiteixeira@users.noreply.github.com> Date: Fri, 10 Nov 2023 02:21:57 -0300 Subject: [PATCH] Add the ability to download diagnostics --- custom_components/proxmoxve/coordinator.py | 8 +- custom_components/proxmoxve/diagnostics.py | 111 +++++++++++++++++++++ custom_components/proxmoxve/manifest.json | 2 +- custom_components/proxmoxve/models.py | 29 +++--- 4 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 custom_components/proxmoxve/diagnostics.py diff --git a/custom_components/proxmoxve/coordinator.py b/custom_components/proxmoxve/coordinator.py index 88fb451..07b6ade 100644 --- a/custom_components/proxmoxve/coordinator.py +++ b/custom_components/proxmoxve/coordinator.py @@ -53,6 +53,7 @@ def __init__( self.config_entry: ConfigEntry = self.config_entry self.proxmox = proxmox self.node_name = node_name + self.resource_id = node_name async def _async_update_data(self) -> ProxmoxNodeData: """Update data for Proxmox Node.""" @@ -112,6 +113,7 @@ def poll_api() -> dict[str, Any] | None: ) return ProxmoxNodeData( + type="NODE", model=api_status["cpuinfo"]["model"], status=api_status["status"], version=api_status["version"]["version"], @@ -152,6 +154,7 @@ def __init__( self.proxmox = proxmox self.node_name: str self.vm_id = qemu_id + self.resource_id = qemu_id async def _async_update_data(self) -> ProxmoxVMData: """Update data for Proxmox QEMU.""" @@ -218,6 +221,7 @@ def poll_api() -> dict[str, Any] | None: update_device_via(self, ProxmoxType.QEMU) return ProxmoxVMData( + type="QEMU", status=api_status["status"], name=api_status["name"], node=self.node_name, @@ -256,8 +260,9 @@ def __init__( self.hass = hass self.config_entry: ConfigEntry = self.config_entry self.proxmox = proxmox - self.vm_id = container_id self.node_name: str + self.vm_id = container_id + self.resource_id = container_id async def _async_update_data(self) -> ProxmoxLXCData: """Update data for Proxmox LXC.""" @@ -324,6 +329,7 @@ def poll_api() -> dict[str, Any] | None: update_device_via(self, ProxmoxType.LXC) return ProxmoxLXCData( + type="LXC", status=api_status["status"], name=api_status["name"], node=self.node_name, diff --git a/custom_components/proxmoxve/diagnostics.py b/custom_components/proxmoxve/diagnostics.py new file mode 100644 index 0000000..6a8ee11 --- /dev/null +++ b/custom_components/proxmoxve/diagnostics.py @@ -0,0 +1,111 @@ +"""Support for the Airzone diagnostics.""" +from __future__ import annotations +import datetime + +from typing import Any +from attr import asdict + +from proxmoxer import ProxmoxAPI + +from homeassistant.components.diagnostics.util import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_UNIQUE_ID +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.device_registry import DeviceEntry +from homeassistant.helpers import device_registry as dr, entity_registry as er + +from .const import COORDINATORS, DOMAIN, LOGGER, PROXMOX_CLIENT +from .coordinator import ProxmoxNodeCoordinator, ProxmoxQEMUCoordinator, ProxmoxLXCCoordinator + +TO_REDACT_CONFIG = ["host", "username", "password"] + +TO_REDACT_COORD = [] + +TO_REDACT_API = [] + +TO_REDACT_DATA = [] + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + + coordinators: dict[str, ProxmoxNodeCoordinator | ProxmoxQEMUCoordinator | ProxmoxLXCCoordinator] = hass.data[DOMAIN][config_entry.entry_id][COORDINATORS] + + proxmox_client = hass.data[DOMAIN][config_entry.entry_id][PROXMOX_CLIENT] + + proxmox = proxmox_client.get_api_client() + + resources = await hass.async_add_executor_job(proxmox.cluster.resources.get) + + nodes = {} + nodes_api= await hass.async_add_executor_job(proxmox.nodes().get) + + for node in nodes_api: + nodes[node["node"]] = node + nodes[node["node"]]["qemu"] = await hass.async_add_executor_job(proxmox.nodes(node["node"]).qemu.get) + nodes[node["node"]]["lxc"] = await hass.async_add_executor_job(proxmox.nodes(node["node"]).lxc.get) + + api_data = { + "resources": resources, + "nodes":nodes, + } + + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) + + devices = [] + + registry_devices = dr.async_entries_for_config_entry( + device_registry, config_entry.entry_id + ) + + for device in registry_devices: + entities = [] + + registry_entities = er.async_entries_for_device( + entity_registry, + device_id=device.id, + include_disabled_entities=True, + ) + + for entity_entry in registry_entities: + state_dict = None + if state := hass.states.get(entity_entry.entity_id): + state_dict = dict(state.as_dict()) + state_dict.pop("context", None) + + entities.append({"entry": asdict(entity_entry), "state": state_dict}) + + devices.append({"device": asdict(device), "entities": entities}) + + return { + "source": "config_entry", + "timestamp": datetime.datetime.now, + "config_entry": async_redact_data(config_entry.data, TO_REDACT_CONFIG), + "options": async_redact_data(config_entry.options, TO_REDACT_CONFIG), + "devices": async_redact_data(devices, TO_REDACT_DATA), + "proxmox_coordinators": { + coordinator.name: { + "data": async_redact_data( + coordinator.data.__dict__, TO_REDACT_COORD + ), + } + for i, coordinator in enumerate(coordinators.values()) + }, + "api_response": async_redact_data(api_data, TO_REDACT_API), + } + +async def async_get_device_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry, device: DeviceEntry +) -> dict: + """Return diagnostics for a device entry.""" + + config_entry_diagnostics = await async_get_config_entry_diagnostics(hass, config_entry) + + return { + "source": f"device - {device.id}", + **config_entry_diagnostics, + } \ No newline at end of file diff --git a/custom_components/proxmoxve/manifest.json b/custom_components/proxmoxve/manifest.json index c018fcb..2b4eb41 100644 --- a/custom_components/proxmoxve/manifest.json +++ b/custom_components/proxmoxve/manifest.json @@ -8,5 +8,5 @@ "issue_tracker": "https://github.com/dougiteixeira/proxmoxve/issues", "loggers": ["proxmoxer"], "requirements": ["proxmoxer==2.0.1"], - "version": "2.0.6" + "version": "2.1.0" } diff --git a/custom_components/proxmoxve/models.py b/custom_components/proxmoxve/models.py index 75dc730..664df05 100644 --- a/custom_components/proxmoxve/models.py +++ b/custom_components/proxmoxve/models.py @@ -48,56 +48,59 @@ class ProxmoxSwitchDescription(SwitchEntityDescription): class ProxmoxNodeData: """Data parsed from the Proxmox API for Node.""" - model: str - status: str - version: str - uptime: int + type: str cpu: float disk_total: float disk_used: float + model: str memory_total: float memory_used: float memory_free: float + status: str swap_total: float swap_free: float swap_used: float + uptime: int + version: str @dataclasses.dataclass class ProxmoxVMData: """Data parsed from the Proxmox API for QEMU.""" + type: str name: str - status: str node: str - health: str - uptime: int cpu: float + disk_total: float + disk_used: float + health: str memory_total: float memory_used: float memory_free: float network_in: float network_out: float - disk_total: float - disk_used: float + status: str + uptime: int @dataclasses.dataclass class ProxmoxLXCData: """Data parsed from the Proxmox API for LXC.""" + type: str name: str - status: str node: str - uptime: int cpu: float + disk_total: float + disk_used: float memory_total: float memory_used: float memory_free: float network_in: float network_out: float - disk_total: float - disk_used: float + status: str swap_total: float swap_free: float swap_used: float + uptime: int