Skip to content

Commit

Permalink
Fix show_host(s) available and proxy rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
pederhan committed Aug 28, 2024
1 parent 3b64257 commit b51b534
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 13 deletions.
17 changes: 14 additions & 3 deletions zabbix_cli/commands/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ def show_host(
) -> None:
"""Show a specific host."""
from zabbix_cli.commands.results.host import HostFilterArgs
from zabbix_cli.pyzabbix.utils import get_proxy_map

args = HostFilterArgs.from_command_args(
filter_legacy, active, maintenance, monitored
Expand All @@ -659,6 +660,7 @@ def show_host(
hostname_or_id,
select_groups=True,
select_templates=True,
select_interfaces=True,
sort_field="host",
sort_order="ASC",
search=True, # we allow wildcard patterns
Expand All @@ -667,6 +669,10 @@ def show_host(
active_interface=args.active,
)

# HACK: inject proxy map to host for rendering
proxy_map = get_proxy_map(app.state.client)
host.set_proxy(proxy_map)

render_result(host)


Expand Down Expand Up @@ -733,6 +739,7 @@ def show_hosts(
"""
from zabbix_cli.commands.results.host import HostFilterArgs
from zabbix_cli.models import AggregateResult
from zabbix_cli.pyzabbix.utils import get_proxy_map

args = HostFilterArgs.from_command_args(
filter_legacy, active, maintenance, monitored
Expand All @@ -749,14 +756,18 @@ def show_hosts(
maintenance=args.maintenance_status,
monitored=args.status,
active_interface=args.active,
limit=limit,
)

total_hosts = len(hosts) # store len before limiting
if limit:
hosts = hosts[: abs(limit)]
# HACK: inject proxy map for each host
proxy_map = get_proxy_map(app.state.client)
for host in hosts:
host.set_proxy(proxy_map)

render_result(AggregateResult(result=hosts))

# TODO: implement paging for large result sets
total_hosts = app.state.client.get_host_count()
if total_hosts > len(hosts): # we limited the results
info(
f"Only showing first {limit} of {total_hosts} hosts. Use [option]--limit 0[/] to show all."
Expand Down
21 changes: 19 additions & 2 deletions zabbix_cli/pyzabbix/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@ def get_host(
maintenance=maintenance,
monitored=monitored,
active_interface=active_interface,
limit=1,
)
if not hosts:
raise ZabbixNotFoundError(
Expand All @@ -770,7 +771,7 @@ def get_hosts(
sort_field: Optional[str] = None,
sort_order: Optional[Literal["ASC", "DESC"]] = None,
search: bool = True, # we generally always want to search when multiple hosts are requested
# **filter_kwargs,
limit: Optional[int] = None,
) -> List[Host]:
"""Fetches all hosts matching the given criteria(s).
Expand Down Expand Up @@ -853,10 +854,26 @@ def get_hosts(
params["sortfield"] = sort_field
if sort_order:
params["sortorder"] = sort_order
if limit:
params["limit"] = limit

resp: List[Any] = self.host.get(**params) or []
# TODO add result to cache
return [Host(**resp) for resp in resp]
return [Host(**r) for r in resp]

def get_host_count(self) -> int:
"""Fetches the total number of hosts in the Zabbix server."""
return self.count("host")

def count(self, object_type: str, params: Optional[ParamsType] = None) -> int:
"""Count the number of objects of a given type."""
params = params or {}
params["countOutput"] = True
try:
resp = getattr(self, object_type).get(**params)
return int(resp)
except (ZabbixAPIException, TypeError, ValueError) as e:
raise ZabbixAPICallError(f"Failed to fetch {object_type} count") from e

def create_host(
self,
Expand Down
40 changes: 33 additions & 7 deletions zabbix_cli/pyzabbix/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,14 @@ class Host(ZabbixAPIBaseModel):
# Compat for <7.0.0
validation_alias=AliasChoices("proxyid", "proxy_hostid"),
)
proxy_address: Optional[str] = None
proxy_groupid: Optional[str] = None # >= 7.0
maintenance_status: Optional[str] = None
# active_available is a new field in 7.0.
# Previous versions required checking the `available` field of its first interface.
# In zabbix-cli v2, this value was serialized as `zabbix_agent`.
active_available: Optional[str] = Field(
default=None,
validation_alias=AliasChoices(
"available", # < 7.0
"active_available", # >= 7.0
"zabbix_agent", # Zabbix-cli V2 name of this field
),
Expand All @@ -432,6 +433,9 @@ class Host(ZabbixAPIBaseModel):
macros: List[Macro] = Field(default_factory=list)
interfaces: List[HostInterface] = Field(default_factory=list)

# HACK: Add a field for the host's proxy that we can inject later
proxy: Optional[Proxy] = None

def __str__(self) -> str:
return f"{self.host!r} ({self.hostid})"

Expand All @@ -442,6 +446,27 @@ def model_simple_dump(self) -> Dict[str, Any]:
"hostid": self.hostid,
}

def set_proxy(self, proxy_map: Dict[str, Proxy]) -> None:
"""Set proxy info for the host given a mapping of proxy IDs to proxies."""
if not (proxy := proxy_map.get(str(self.proxyid))):
return
self.proxy = proxy

def get_active_status(self, with_code: bool = False) -> str:
"""Returns the active interface status as a formatted string."""
if self.zabbix_version.release >= (7, 0, 0):
return ActiveInterface.string_from_value(
self.active_available, with_code=with_code
)
# We are on pre-7.0.0, check the first interface
iface = self.interfaces[0] if self.interfaces else None
if iface:
return ActiveInterface.string_from_value(
iface.available, with_code=with_code
)
else:
return ActiveInterface.UNKNOWN.as_status(with_code=with_code)

# Legacy V2 JSON format compatibility
@field_serializer("maintenance_status", when_used="json")
def _LEGACY_maintenance_status_serializer(
Expand All @@ -455,14 +480,15 @@ def _LEGACY_maintenance_status_serializer(
return v

@computed_field
def zabbix_agent(self) -> Optional[Union[int, str]]:
@property
def zabbix_agent(self) -> str:
"""LEGACY: Serializes the zabbix agent status as a formatted string
in legacy mode, and as-is in new mode.
"""
# NOTE: use `self.active_available` instead of `self.zabbix_agent`
if self.legacy_json_format:
return ActiveInterface.string_from_value(self.active_available)
return self.active_available
return self.get_active_status(with_code=True)
return self.get_active_status()

@field_serializer("status", when_used="json")
def _LEGACY_status_serializer(
Expand Down Expand Up @@ -512,10 +538,10 @@ def __cols_rows__(self) -> ColsRowsType:
self.host,
"\n".join([group.name for group in self.groups]),
"\n".join([template.host for template in self.templates]),
ActiveInterface.string_from_value(self.active_available),
self.zabbix_agent,
MaintenanceStatus.string_from_value(self.maintenance_status),
MonitoringStatus.string_from_value(self.status),
self.proxy_address or "",
self.proxy.name if self.proxy else "",
]
]
return cols, rows
Expand Down
9 changes: 8 additions & 1 deletion zabbix_cli/pyzabbix/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import random
import re
from typing import TYPE_CHECKING
from typing import Dict
from typing import Optional

from zabbix_cli.exceptions import ZabbixAPICallError
Expand All @@ -14,7 +15,7 @@


def get_random_proxy(client: ZabbixAPI, pattern: Optional[str] = None) -> Proxy:
"""Fetches a random proxy, optionally matching a regex pattern."""
"""Fetch a random proxy, optionally matching a regex pattern."""
proxies = client.get_proxies()
if not proxies:
raise ZabbixNotFoundError("No proxies found")
Expand All @@ -27,3 +28,9 @@ def get_random_proxy(client: ZabbixAPI, pattern: Optional[str] = None) -> Proxy:
if not proxies:
raise ZabbixNotFoundError(f"No proxies matching pattern {pattern!r}")
return random.choice(proxies)


def get_proxy_map(client: ZabbixAPI) -> Dict[str, Proxy]:
"""Fetch all proxies and return a mapping of proxy IDs to Proxy objects."""
proxies = client.get_proxies()
return {proxy.proxyid: proxy for proxy in proxies}

0 comments on commit b51b534

Please sign in to comment.