Skip to content

Commit

Permalink
Switch to official Zabbix Python API
Browse files Browse the repository at this point in the history
The Zabbix project released an official Python API library called
"zabbix_utils" which can replace the current library that this component
is using.

This just does a conversion to the existing synchronous API. The new
Python library also supports an asynchronous API which could be used to
further migrate this plugin.
  • Loading branch information
kruton committed Nov 26, 2024
1 parent 4093a68 commit 2ff74b1
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 24 deletions.
35 changes: 18 additions & 17 deletions homeassistant/components/zabbix/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
from urllib.error import HTTPError
from urllib.parse import urljoin

from pyzabbix import ZabbixAPI, ZabbixAPIException, ZabbixMetric, ZabbixSender
import voluptuous as vol
from zabbix_utils import ItemValue, Sender, ZabbixAPI
from zabbix_utils.exceptions import APIRequestError

from homeassistant.const import (
CONF_HOST,
Expand All @@ -34,14 +35,15 @@
)
from homeassistant.helpers.typing import ConfigType

from .const import DOMAIN
from .const import CONF_SENDER_PORT, DOMAIN

_LOGGER = logging.getLogger(__name__)

CONF_PUBLISH_STATES_HOST = "publish_states_host"

DEFAULT_SSL = False
DEFAULT_PATH = "zabbix"
DEFAULT_SENDER_PORT = 10051

TIMEOUT = 5
RETRY_DELAY = 20
Expand All @@ -60,6 +62,7 @@
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PATH, default=DEFAULT_PATH): cv.string,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
vol.Optional(CONF_SENDER_PORT, default=DEFAULT_SENDER_PORT): cv.port,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PUBLISH_STATES_HOST): cv.string,
}
Expand All @@ -86,7 +89,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
try:
zapi = ZabbixAPI(url=url, user=username, password=password)
_LOGGER.debug("Connected to Zabbix API Version %s", zapi.api_version())
except ZabbixAPIException as login_exception:
except APIRequestError as login_exception:
_LOGGER.error("Unable to login to the Zabbix API: %s", login_exception)
return False
except HTTPError as http_error:
Expand All @@ -104,7 +107,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:

def event_to_metrics(
event: Event, float_keys: set[str], string_keys: set[str]
) -> list[ZabbixMetric] | None:
) -> list[ItemValue] | None:
"""Add an event to the outgoing Zabbix list."""
state = event.data.get("new_state")
if state is None or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE):
Expand Down Expand Up @@ -145,14 +148,14 @@ def event_to_metrics(
float_keys.update(floats)
if len(float_keys) != float_keys_count:
floats_discovery = [{"{#KEY}": float_key} for float_key in float_keys]
metric = ZabbixMetric(
metric = ItemValue(
publish_states_host,
"homeassistant.floats_discovery",
json.dumps(floats_discovery),
)
metrics.append(metric)
for key, value in floats.items():
metric = ZabbixMetric(
metric = ItemValue(
publish_states_host, f"homeassistant.float[{key}]", value
)
metrics.append(metric)
Expand All @@ -161,8 +164,8 @@ def event_to_metrics(
return metrics

if publish_states_host:
zabbix_sender = ZabbixSender(zabbix_server=conf[CONF_HOST])
instance = ZabbixThread(zabbix_sender, event_to_metrics)
sender = Sender(server=conf[CONF_HOST], port=conf[CONF_SENDER_PORT])
instance = ZabbixThread(sender, event_to_metrics)
instance.setup(hass)

return True
Expand All @@ -175,15 +178,13 @@ class ZabbixThread(threading.Thread):

def __init__(
self,
zabbix_sender: ZabbixSender,
event_to_metrics: Callable[
[Event, set[str], set[str]], list[ZabbixMetric] | None
],
sender: Sender,
event_to_metrics: Callable[[Event, set[str], set[str]], list[ItemValue] | None],
) -> None:
"""Initialize the listener."""
threading.Thread.__init__(self, name="Zabbix")
self.queue: queue.Queue = queue.Queue()
self.zabbix_sender = zabbix_sender
self.sender = sender
self.event_to_metrics = event_to_metrics
self.write_errors = 0
self.shutdown = False
Expand All @@ -208,12 +209,12 @@ def _event_listener(self, event: Event[EventStateChangedData]) -> None:
item = (time.monotonic(), event)
self.queue.put(item)

def get_metrics(self) -> tuple[int, list[ZabbixMetric]]:
def get_metrics(self) -> tuple[int, list[ItemValue]]:
"""Return a batch of events formatted for writing."""
queue_seconds = QUEUE_BACKLOG_SECONDS + self.MAX_TRIES * RETRY_DELAY

count = 0
metrics: list[ZabbixMetric] = []
metrics: list[ItemValue] = []

dropped = 0

Expand Down Expand Up @@ -243,12 +244,12 @@ def get_metrics(self) -> tuple[int, list[ZabbixMetric]]:

return count, metrics

def write_to_zabbix(self, metrics: list[ZabbixMetric]) -> None:
def write_to_zabbix(self, metrics: list[ItemValue]) -> None:
"""Write preprocessed events to zabbix, with retry."""

for retry in range(self.MAX_TRIES + 1):
try:
self.zabbix_sender.send(metrics)
self.sender.send(metrics)

if self.write_errors:
_LOGGER.error("Resumed, lost %d events", self.write_errors)
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/zabbix/const.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""Constants for Zabbix."""

DOMAIN = "zabbix"
from typing import Final

CONF_SENDER_PORT: Final = "sender_port"
DOMAIN: Final = "zabbix"
4 changes: 2 additions & 2 deletions homeassistant/components/zabbix/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/zabbix",
"iot_class": "local_polling",
"loggers": ["pyzabbix"],
"loggers": ["zabbix_utils"],
"quality_scale": "legacy",
"requirements": ["py-zabbix==1.1.7"]
"requirements": ["zabbix-utils==2.0.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/zabbix/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import logging
from typing import Any

from pyzabbix import ZabbixAPI
import voluptuous as vol
from zabbix_utils import ZabbixAPI

from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
Expand Down
6 changes: 3 additions & 3 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1710,9 +1710,6 @@ py-sucks==0.9.10
# homeassistant.components.synology_dsm
py-synologydsm-api==2.5.3

# homeassistant.components.zabbix
py-zabbix==1.1.7

# homeassistant.components.atome
pyAtome==0.1.1

Expand Down Expand Up @@ -3068,6 +3065,9 @@ youtubeaio==1.1.5
# homeassistant.components.media_extractor
yt-dlp[default]==2024.11.04

# homeassistant.components.zabbix
zabbix-utils==2.0.1

# homeassistant.components.zamg
zamg==0.3.6

Expand Down

0 comments on commit 2ff74b1

Please sign in to comment.