diff --git a/CHANGELOG b/CHANGELOG index 24452637..c358212b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New command `update_config` to update an outdated configuration file with new options, as well as any currently applied overrides. - `show_config --secrets ` option for controlling the display mode of sensitive information in the configuration file. Defaults to `mask`. - New command `update_host` to update basic information about a host. +- New command `show_proxy_hosts` to show hosts monitored by a given proxy. ### Changed diff --git a/zabbix_cli/commands/proxy.py b/zabbix_cli/commands/proxy.py index 1347cc10..b6eda561 100644 --- a/zabbix_cli/commands/proxy.py +++ b/zabbix_cli/commands/proxy.py @@ -436,14 +436,14 @@ def show_proxies( hosts: bool = typer.Option( False, "--hosts", - help="Show hostnames of each host.", + help="Show hostnames of each host for every proxy.", is_flag=True, ), ) -> None: """Show all proxies. - Shows number of hosts for each proxy unless --hosts is passed in, - in which case the hostnames of each host is displayed instead. + Shows number of hosts for each proxy unless [option]--hosts[/] is passed in, + in which case the hostnames of each host are displayed instead. """ from zabbix_cli.commands.results.proxy import ShowProxiesResult from zabbix_cli.models import AggregateResult @@ -462,6 +462,23 @@ def show_proxies( ) +@app.command(name="show_proxy_hosts", rich_help_panel=HELP_PANEL) +def show_proxy_hosts( + ctx: typer.Context, + proxy: str = typer.Argument( + help="Proxy name or ID. Supports wildcards.", + show_default=False, + ), +) -> None: + """Show all hosts with for a given proxy.""" + from zabbix_cli.commands.results.proxy import ShowProxyHostsResult + + with app.status("Fetching proxy..."): + prox = app.state.client.get_proxy(proxy, select_hosts=True) + + render_result(ShowProxyHostsResult.from_result(prox)) + + @app.command( name="show_proxy_groups", rich_help_panel=HELP_PANEL, diff --git a/zabbix_cli/commands/results/proxy.py b/zabbix_cli/commands/results/proxy.py index 037f24e7..8a743fdf 100644 --- a/zabbix_cli/commands/results/proxy.py +++ b/zabbix_cli/commands/results/proxy.py @@ -169,12 +169,35 @@ def __cols_rows__(self) -> ColsRowsType: return cols, rows +class ShowProxyHostsResult(TableRenderable): + proxy: Proxy + hosts: HostList = [] + + @classmethod + def from_result(cls, proxy: Proxy) -> Self: + # HACK: remove the list of hosts from the proxy and store it separately + # so that we can render the proxy without the hosts + hosts = proxy.hosts + proxy.hosts = [] + return cls(proxy=proxy, hosts=hosts) + + def __cols_rows__(self) -> ColsRowsType: + cols = ["Proxy", "Hosts"] + rows: RowsType = [ + [ + self.proxy.name, + "\n".join(h.host for h in self.hosts), + ] + ] + return cols, rows + + class ShowProxyGroupHostsResult(TableRenderable): proxy_group: ProxyGroup hosts: HostList = [] def __cols_rows__(self) -> ColsRowsType: - cols = ["Name", "Hosts"] + cols = ["Proxy Group", "Hosts"] rows: RowsType = [ [ self.proxy_group.name, diff --git a/zabbix_cli/output/console.py b/zabbix_cli/output/console.py index 8235e412..fb00116c 100644 --- a/zabbix_cli/output/console.py +++ b/zabbix_cli/output/console.py @@ -18,6 +18,8 @@ from zabbix_cli.state import get_state if TYPE_CHECKING: + from rich.theme import Theme + from zabbix_cli.config.model import Config # stdout console used to print results @@ -32,6 +34,56 @@ ) +def get_theme(name: str) -> Theme: + """Get a Rich console theme by name.""" + return RICH_THEME # other themes NYI + + +class OutputManager: + out: Console = console + err: Console = err_console + + def __init__(self, config: Config) -> None: + self.config = config + self.configure() + + theme = get_theme(self.config.app.output.theme) + self.out = self._make_console(err=False, theme=theme) + self.err = self._make_console(err=True, theme=theme) + + def _make_console( + self, + err: bool = False, + highlight: bool = False, + soft_wrap: bool = False, + theme: Theme = RICH_THEME, + ) -> Console: + return Console( + stderr=err, + highlight=highlight, + soft_wrap=soft_wrap, + theme=theme, + ) + + def configure(self) -> None: + """Configure console output based on the application configuration.""" + if not self.config.app.output.color: + self.disable_color() + + def disable_color(self) -> None: + """Disable color output in consoles.""" + self.out._color_system = None # pyright: ignore[reportPrivateUsage] + self.err._color_system = None # pyright: ignore[reportPrivateUsage] + # HACK: set env var to disable color in Typer console + os.environ["NO_COLOR"] = "1" + + def enable_color(self) -> None: + """Enable color output in consoles if supported.""" + self.out._color_system = console._detect_color_system() # pyright: ignore[reportPrivateUsage] + self.err._color_system = console._detect_color_system() # pyright: ignore[reportPrivateUsage] + os.unsetenv("NO_COLOR") # remove hack + + RESERVED_EXTRA_KEYS = ( "name", "level",