Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue warning if glances server version is 2 #105887

Merged
merged 7 commits into from Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 49 additions & 4 deletions homeassistant/components/glances/__init__.py
@@ -1,25 +1,47 @@
"""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.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 .const import CONF_VERSION, DOMAIN
from .coordinator import GlancesDataUpdateCoordinator

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 UnknownError as err:
raise ConfigEntryError(err) from err
coordinator = GlancesDataUpdateCoordinator(hass, config_entry, api)
await coordinator.async_config_entry_first_refresh()

Expand All @@ -39,8 +61,31 @@ 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)
entry_data.pop(CONF_VERSION, 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(httpx_client=httpx_client, version=version, **entry_data)
engrbm87 marked this conversation as resolved.
Show resolved Hide resolved
try:
await api.get_ha_sensor_data()
_LOGGER.debug("Connected to Glances API v%s", version)
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
if version == 2:
async_create_issue(
hass,
DOMAIN,
"deprecated_version",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_version",
)
return api
except GlancesApiNoDataAvailable as err:
_LOGGER.debug("Failed to connect to Glances API v%s: %s", version, err)
engrbm87 marked this conversation as resolved.
Show resolved Hide resolved
raise UnknownError("Could not connect to Glances API")
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved


class UnknownError(HomeAssistantError):
"""Raise exception if we fail to connect to Glances API."""
20 changes: 5 additions & 15 deletions homeassistant/components/glances/config_flow.py
Expand Up @@ -21,23 +21,15 @@
)
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 UnknownError, get_api
from .const import DEFAULT_HOST, DEFAULT_PORT, DOMAIN

DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST, default=DEFAULT_HOST): str,
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,
}
Expand Down Expand Up @@ -65,12 +57,11 @@ async def async_step_reauth_confirm(
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:
except (GlancesApiConnectionError, UnknownError):
errors["base"] = "cannot_connect"
else:
self.hass.config_entries.async_update_entry(
Expand Down Expand Up @@ -101,9 +92,8 @@ async def async_step_user(
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:
Expand Down
3 changes: 0 additions & 3 deletions homeassistant/components/glances/const.py
Expand Up @@ -8,9 +8,6 @@

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"
7 changes: 6 additions & 1 deletion homeassistant/components/glances/strings.json
Expand Up @@ -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%]"
},
Expand All @@ -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 to version 3 then reload the config entry."
engrbm87 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
1 change: 0 additions & 1 deletion tests/components/glances/__init__.py
Expand Up @@ -6,7 +6,6 @@
"host": "0.0.0.0",
"username": "username",
"password": "password",
"version": 3,
"port": 61208,
"ssl": False,
"verify_ssl": True,
Expand Down
29 changes: 27 additions & 2 deletions tests/components/glances/test_init.py
@@ -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

Expand All @@ -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(
Expand Down