1
mirror of https://github.com/home-assistant/core synced 2024-07-15 09:42:11 +02:00

Issue warning if glances server version is 2 (#105887)

* Issue warning if glances server version is 2

* Auto detect api version

* Apply suggestions

* Add HA version deprecation

* Apply suggestions from code review

* update config flow tests

* Fix breaks in ha version

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Rami Mosleh 2024-01-04 06:17:43 +02:00 committed by GitHub
parent 890615bb92
commit 2331f89936
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 34 deletions

View File

@ -1,13 +1,34 @@
"""The Glances component."""
import logging
from typing import Any
from glances_api import Glances
from glances_api.exceptions import (
GlancesApiAuthorizationError,
GlancesApiError,
GlancesApiNoDataAvailable,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, CONF_VERIFY_SSL, Platform
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryError,
ConfigEntryNotReady,
HomeAssistantError,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from .const import DOMAIN
from .coordinator import GlancesDataUpdateCoordinator
@ -16,10 +37,19 @@ PLATFORMS = [Platform.SENSOR]
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Set up Glances from config entry."""
api = get_api(hass, dict(config_entry.data))
try:
api = await get_api(hass, dict(config_entry.data))
except GlancesApiAuthorizationError as err:
raise ConfigEntryAuthFailed from err
except GlancesApiError as err:
raise ConfigEntryNotReady from err
except ServerVersionMismatch as err:
raise ConfigEntryError(err) from err
coordinator = GlancesDataUpdateCoordinator(hass, config_entry, api)
await coordinator.async_config_entry_first_refresh()
@ -39,8 +69,38 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
def get_api(hass: HomeAssistant, entry_data: dict[str, Any]) -> Glances:
async def get_api(hass: HomeAssistant, entry_data: dict[str, Any]) -> Glances:
"""Return the api from glances_api."""
entry_data.pop(CONF_NAME, None)
httpx_client = get_async_client(hass, verify_ssl=entry_data[CONF_VERIFY_SSL])
return Glances(httpx_client=httpx_client, **entry_data)
for version in (3, 2):
api = Glances(
host=entry_data[CONF_HOST],
port=entry_data[CONF_PORT],
version=version,
ssl=entry_data[CONF_SSL],
username=entry_data.get(CONF_USERNAME),
password=entry_data.get(CONF_PASSWORD),
httpx_client=httpx_client,
)
try:
await api.get_ha_sensor_data()
except GlancesApiNoDataAvailable as err:
_LOGGER.debug("Failed to connect to Glances API v%s: %s", version, err)
continue
if version == 2:
async_create_issue(
hass,
DOMAIN,
"deprecated_version",
breaks_in_ha_version="2024.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_version",
)
_LOGGER.debug("Connected to Glances API v%s", version)
return api
raise ServerVersionMismatch("Could not connect to Glances API version 2 or 3")
class ServerVersionMismatch(HomeAssistantError):
"""Raise exception if we fail to connect to Glances API."""

View File

@ -21,15 +21,8 @@ from homeassistant.const import (
)
from homeassistant.data_entry_flow import FlowResult
from . import get_api
from .const import (
CONF_VERSION,
DEFAULT_HOST,
DEFAULT_PORT,
DEFAULT_VERSION,
DOMAIN,
SUPPORTED_VERSIONS,
)
from . import ServerVersionMismatch, get_api
from .const import DEFAULT_HOST, DEFAULT_PORT, DOMAIN
DATA_SCHEMA = vol.Schema(
{
@ -37,7 +30,6 @@ DATA_SCHEMA = vol.Schema(
vol.Optional(CONF_USERNAME): str,
vol.Optional(CONF_PASSWORD): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
vol.Required(CONF_VERSION, default=DEFAULT_VERSION): vol.In(SUPPORTED_VERSIONS),
vol.Optional(CONF_SSL, default=False): bool,
vol.Optional(CONF_VERIFY_SSL, default=False): bool,
}
@ -65,9 +57,8 @@ class GlancesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
assert self._reauth_entry
if user_input is not None:
user_input = {**self._reauth_entry.data, **user_input}
api = get_api(self.hass, user_input)
try:
await api.get_ha_sensor_data()
await get_api(self.hass, user_input)
except GlancesApiAuthorizationError:
errors["base"] = "invalid_auth"
except GlancesApiConnectionError:
@ -101,12 +92,11 @@ class GlancesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._async_abort_entries_match(
{CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT]}
)
api = get_api(self.hass, user_input)
try:
await api.get_ha_sensor_data()
await get_api(self.hass, user_input)
except GlancesApiAuthorizationError:
errors["base"] = "invalid_auth"
except GlancesApiConnectionError:
except (GlancesApiConnectionError, ServerVersionMismatch):
errors["base"] = "cannot_connect"
else:
return self.async_create_entry(

View File

@ -8,9 +8,6 @@ CONF_VERSION = "version"
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 61208
DEFAULT_VERSION = 3
DEFAULT_SCAN_INTERVAL = timedelta(seconds=60)
SUPPORTED_VERSIONS = [2, 3]
CPU_ICON = f"mdi:cpu-{64 if sys.maxsize > 2**32 else 32}-bit"

View File

@ -7,7 +7,6 @@
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"port": "[%key:common::config_flow::data::port%]",
"version": "Glances API Version (2 or 3)",
"ssl": "[%key:common::config_flow::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
@ -30,5 +29,11 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"issues": {
"deprecated_version": {
"title": "Glances servers with version 2 is deprecated",
"description": "Glances servers with version 2 is deprecated and will not be supported in future versions of HA. It is recommended to update your server to Glances version 3 then reload the integration."
}
}
}

View File

@ -6,7 +6,6 @@ MOCK_USER_INPUT: dict[str, Any] = {
"host": "0.0.0.0",
"username": "username",
"password": "password",
"version": 3,
"port": 61208,
"ssl": False,
"verify_ssl": True,

View File

@ -4,6 +4,7 @@ from unittest.mock import MagicMock
from glances_api.exceptions import (
GlancesApiAuthorizationError,
GlancesApiConnectionError,
GlancesApiNoDataAvailable,
)
import pytest
@ -47,6 +48,7 @@ async def test_form(hass: HomeAssistant) -> None:
[
(GlancesApiAuthorizationError, "invalid_auth"),
(GlancesApiConnectionError, "cannot_connect"),
(GlancesApiNoDataAvailable, "cannot_connect"),
],
)
async def test_form_fails(
@ -54,7 +56,7 @@ async def test_form_fails(
) -> None:
"""Test flow fails when api exception is raised."""
mock_api.return_value.get_ha_sensor_data.side_effect = [error, HA_SENSOR_DATA]
mock_api.return_value.get_ha_sensor_data.side_effect = error
result = await hass.config_entries.flow.async_init(
glances.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
@ -65,12 +67,6 @@ async def test_form_fails(
assert result["type"] == FlowResultType.FORM
assert result["errors"] == {"base": message}
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=MOCK_USER_INPUT
)
assert result["type"] == FlowResultType.CREATE_ENTRY
async def test_form_already_configured(hass: HomeAssistant) -> None:
"""Test host is already configured."""

View File

@ -1,17 +1,19 @@
"""Tests for Glances integration."""
from unittest.mock import MagicMock
from unittest.mock import AsyncMock, MagicMock
from glances_api.exceptions import (
GlancesApiAuthorizationError,
GlancesApiConnectionError,
GlancesApiNoDataAvailable,
)
import pytest
from homeassistant.components.glances.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from . import MOCK_USER_INPUT
from . import HA_SENSOR_DATA, MOCK_USER_INPUT
from tests.common import MockConfigEntry
@ -27,11 +29,34 @@ async def test_successful_config_entry(hass: HomeAssistant) -> None:
assert entry.state == ConfigEntryState.LOADED
async def test_entry_deprecated_version(
hass: HomeAssistant, issue_registry: ir.IssueRegistry, mock_api: AsyncMock
) -> None:
"""Test creating an issue if glances server is version 2."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT)
entry.add_to_hass(hass)
mock_api.return_value.get_ha_sensor_data.side_effect = [
GlancesApiNoDataAvailable("endpoint: 'all' is not valid"),
HA_SENSOR_DATA,
HA_SENSOR_DATA,
]
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state == ConfigEntryState.LOADED
issue = issue_registry.async_get_issue(DOMAIN, "deprecated_version")
assert issue is not None
assert issue.severity == ir.IssueSeverity.WARNING
@pytest.mark.parametrize(
("error", "entry_state"),
[
(GlancesApiAuthorizationError, ConfigEntryState.SETUP_ERROR),
(GlancesApiConnectionError, ConfigEntryState.SETUP_RETRY),
(GlancesApiNoDataAvailable, ConfigEntryState.SETUP_ERROR),
],
)
async def test_setup_error(