File: macro.py

package info (click to toggle)
zabbix-cli 3.5.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,892 kB
  • sloc: python: 18,211; makefile: 3
file content (268 lines) | stat: -rw-r--r-- 8,759 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
"""Commands to view and manage macros."""

from __future__ import annotations

from typing import Optional

import typer

from zabbix_cli.app import Example
from zabbix_cli.app import app
from zabbix_cli.commands.common.args import get_limit_option
from zabbix_cli.config.constants import OutputFormat
from zabbix_cli.exceptions import ZabbixCLIError
from zabbix_cli.exceptions import ZabbixNotFoundError
from zabbix_cli.output.console import exit_err
from zabbix_cli.output.render import render_result

HELP_PANEL_USER = "Macro (User)"
HELP_PANEL_GLOBAL = "Macro (Global)"


def fmt_macro_name(macro: str) -> str:
    """Format macro name for use in a query."""
    macro = macro.strip()
    if not macro:
        # TODO: More specific exception class
        raise ZabbixCLIError("Macro name cannot be empty.")
    if not macro.isupper():
        macro = macro.upper()
    if not macro.startswith("{"):
        macro = "{" + macro
    if not macro.endswith("}"):
        macro = macro + "}"
    if not macro[1] == "$":  # NOTE: refactor could break this
        macro = "{$" + macro[1:]
    if macro == "{$}":
        raise ZabbixCLIError(f"Invalid macro name {macro!r}")
    return macro


@app.command("define_global_macro", rich_help_panel=HELP_PANEL_GLOBAL)
def define_global_macro(
    ctx: typer.Context,
    name: str = typer.Argument(help="Name of the macro", show_default=False),
    value: str = typer.Argument(help="Value of the macro", show_default=False),
) -> None:
    """Create a global macro."""
    from zabbix_cli.commands.results.macro import GlobalMacroResult
    from zabbix_cli.models import Result

    name = fmt_macro_name(name)

    try:
        macro = app.state.client.get_global_macro(macro_name=name)
    except ZabbixNotFoundError:
        pass
    else:
        exit_err(f"Macro {name!r} already exists with value {macro.value!r}")

    macro_id = app.state.client.create_global_macro(macro=name, value=value)
    render_result(
        Result(
            message=f"Created macro {name!r} with ID {macro_id}.",
            result=GlobalMacroResult(macro=name, globalmacroid=macro_id, value=value),
        ),
    )


@app.command("show_global_macros", rich_help_panel=HELP_PANEL_GLOBAL)
def show_global_macros(ctx: typer.Context) -> None:
    """Show all global macros."""
    from zabbix_cli.commands.results.macro import GlobalMacroResult
    from zabbix_cli.models import AggregateResult

    macros = app.state.client.get_global_macros()
    render_result(
        AggregateResult(
            result=[
                GlobalMacroResult(
                    macro=m.macro, globalmacroid=m.globalmacroid, value=m.value
                )
                for m in macros
            ]
        )
    )


@app.command(
    name="define_host_usermacro",
    rich_help_panel=HELP_PANEL_USER,
    examples=[
        Example(
            "Create a macro named {$SNMP_COMMUNITY} for a host",
            "define_host_usermacro foo.example.com '{$SNMP_COMMUNITY}' public",
        ),
        Example(
            "Create a macro named {$SITE_URL} for a host (automatic name conversion)",
            "define_host_usermacro foo.example.com site_url https://example.com",
        ),
    ],
)
def define_host_usermacro(
    # NOTE: should this use old style args?
    hostname: str = typer.Argument(
        help="Host to define macro for.", show_default=False
    ),
    macro_name: str = typer.Argument(
        help=(
            "Name of macro. "
            "Names will be converted to the Zabbix format, "
            "i.e. [value]site_url[/] becomes [value]{$SITE_URL}[/]."
        ),
        show_default=False,
    ),
    macro_value: str = typer.Argument(
        help="Default value of macro.", show_default=False
    ),
) -> None:
    """Create or update a host usermacro."""
    from zabbix_cli.models import Result

    host = app.state.client.get_host(hostname)
    macro_name = fmt_macro_name(macro_name)

    # Determine if we should create or update macro
    try:
        macro = app.state.client.get_macro(host=host, macro_name=macro_name)
    except ZabbixNotFoundError:
        macro_id = app.state.client.create_macro(
            host=host, macro=macro_name, value=macro_value
        )
        action = "Created"
    else:
        macro_id = app.state.client.update_macro(
            macroid=macro.hostmacroid, value=macro_value
        )
        action = "Updated"

    render_result(
        Result(
            message=f"{action} macro {macro_name!r} with ID {macro_id} for host {hostname!r}."
        )
    )


@app.command(name="show_host_usermacros", rich_help_panel=HELP_PANEL_USER, hidden=False)
def show_host_usermacros(
    hostname_or_id: str = typer.Argument(
        help="Hostname or ID to show macros for",
        show_default=False,
    ),
) -> None:
    """Show all macros defined for a host."""
    from zabbix_cli.commands.results.macro import ShowHostUserMacrosResult
    from zabbix_cli.models import AggregateResult

    # By getting the macros via the host, we also ensure the host exists.
    host = app.state.client.get_host(hostname_or_id, select_macros=True)

    render_result(
        AggregateResult(
            result=[
                ShowHostUserMacrosResult.from_result(macro)
                # Sort macros by name when rendering
                for macro in sorted(host.macros, key=lambda m: m.macro)
            ]
        )
    )


@app.command(
    name="show_usermacro_host_list", rich_help_panel=HELP_PANEL_USER, hidden=False
)
def show_usermacro_host_list(
    usermacro: str = typer.Argument(
        help=(
            "Name of macro to find hosts with. "
            "Macro names are automatically formatted, e.g. [value]site_url[/] becomes [value]{$SITE_URL}[/]."
        ),
        show_default=False,
    ),
    limit: Optional[int] = get_limit_option(),
) -> None:
    """Find all hosts with a user macro of the given name.

    Renders a list of the complete macro object and its hosts in JSON mode.
    """
    from zabbix_cli.commands.results.macro import MacroHostListV2
    from zabbix_cli.commands.results.macro import MacroHostListV3
    from zabbix_cli.models import AggregateResult

    usermacro = fmt_macro_name(usermacro)
    macros = app.state.client.get_macros(
        macro_name=usermacro, select_hosts=True, limit=limit
    )
    macros = [macro for macro in macros if macro.hosts]

    # This is a place where we need to differentiate between legacy and
    # new JSON modes instead of sharing a single model and
    # letting the render function figure it out.
    # The V2 command only renders a single host, but the whole point of this
    # command is to list _all_ hosts with the given macro, so we want to render
    # the macro and a list of EVERY host with that macro.
    if (
        app.state.config.app.output.format == OutputFormat.JSON
        and app.state.config.app.legacy_json_format
    ):
        render_result(
            AggregateResult(result=[MacroHostListV2(macro=macro) for macro in macros])
        )
    else:
        if not macros:
            exit_err(f"Macro {usermacro!r} not found.")
        render_result(
            AggregateResult(result=[MacroHostListV3(macro=macro) for macro in macros])
        )


@app.command(
    "show_usermacro_template_list",
    rich_help_panel=HELP_PANEL_USER,
    examples=[
        Example(
            "Show all templates with a user macro named {$SNMP_COMMUNITY}",
            "show_usermacro_template_list SNMP_COMMUNITY",
        )
    ],
)
def show_usermacro_template_list(
    ctx: typer.Context,
    macro_name: str = typer.Argument(
        help="Name of the macro to find templates with. Automatically formatted.",
        show_default=False,
    ),
    limit: Optional[int] = get_limit_option(),
) -> None:
    """Find all templates with a user macro of the given name."""
    import itertools

    from zabbix_cli.commands.results.macro import ShowUsermacroTemplateListResult
    from zabbix_cli.models import AggregateResult

    macro_name = fmt_macro_name(macro_name)
    macros = app.state.client.get_macros(
        macro_name=macro_name, select_templates=True, limit=limit
    )
    macros = [macro for macro in macros if macro.templates]

    results = itertools.chain.from_iterable(
        [
            [
                ShowUsermacroTemplateListResult(
                    macro=macro.macro,
                    value=macro.value,
                    templateid=template.templateid,
                    template=template.host,
                )
                for template in macro.templates
            ]
            for macro in macros
        ]
    )

    render_result(
        AggregateResult(
            result=list(results),
        )
    )