File: macro.py

package info (click to toggle)
zabbix-cli 3.6.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,980 kB
  • sloc: python: 19,920; makefile: 5
file content (390 lines) | stat: -rw-r--r-- 12,377 bytes parent folder | download
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
"""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_HOST = "Macro (Host)"
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(
    "define_host_macro",
    rich_help_panel=HELP_PANEL_HOST,
    examples=[
        Example(
            "Create a macro named {$SNMP_COMMUNITY} for a host",
            "define_host_macro foo.example.com '{$SNMP_COMMUNITY}' public",
        ),
        Example(
            "Create a macro named {$SITE_URL} for a host (automatic name conversion)",
            "define_host_macro foo.example.com site_url https://example.com",
        ),
    ],
    help="Define a host macro.",
)
@app.command(
    # old name for backwards compatibility
    "define_host_usermacro",
    hidden=True,
    deprecated=True,
    rich_help_panel=HELP_PANEL_HOST,
    help="DEPRECATED: Use add_template_to_group instead.",
)
def define_host_macro(
    # 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_host_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_macros",
    rich_help_panel=HELP_PANEL_HOST,
    help="Show all macros defined for a host.",
)
@app.command(
    name="show_host_usermacros",
    rich_help_panel=HELP_PANEL_HOST,
    hidden=True,
    deprecated=True,
    help="DEPRECATED: Use show_host_macros instead.",
)
def show_host_macros(
    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_macro_hosts",
    rich_help_panel=HELP_PANEL_HOST,
)
@app.command(
    name="show_usermacro_host_list",
    rich_help_panel=HELP_PANEL_HOST,
    hidden=True,
    deprecated=True,
    help="DEPRECATED: Use show_macro_hosts instead.",
)
def show_macro_hosts(
    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(
    "define_template_macro",
    rich_help_panel=HELP_PANEL_HOST,
    examples=[
        Example(
            "Create a macro named {$SNMP_COMMUNITY} with the value 'public' for a template",
            "define_template_macro Mytemplate '{$SNMP_COMMUNITY}' public",
        ),
        Example(
            "Create a macro named {$SITE_URL} with an URL value for a template (automatic name conversion)",
            "define_template_macro Mytemplate site_url https://example.com",
        ),
    ],
    help="Define a template macro.",
)
def define_template_macro(
    template_name: str = typer.Argument(
        help="Name of template 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 template macro."""
    from zabbix_cli.models import Result

    template = app.state.client.get_template(template_name)
    macro_name = fmt_macro_name(macro_name)

    # Determine if we should create or update macro
    try:
        macro = app.state.client.get_macro(template=template, macro_name=macro_name)
    except ZabbixNotFoundError:
        macro_id = app.state.client.create_template_macro(
            template=template, 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 template {template}."
        )
    )


@app.command(
    name="show_template_macros",
    rich_help_panel=HELP_PANEL_HOST,
    help="Show all macros defined for a template.",
)
def show_template_macros(
    template_name_or_id: str = typer.Argument(
        help="Template name 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 template, we also ensure the template exists.
    template = app.state.client.get_template(template_name_or_id, select_macros=True)

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


@app.command(
    "show_macro_templates",
    rich_help_panel=HELP_PANEL_HOST,
    examples=[
        Example(
            "Show all templates with a user macro named {$SNMP_COMMUNITY}",
            "show_macro_templates SNMP_COMMUNITY",
        )
    ],
)
@app.command(
    "show_usermacro_template_list",
    rich_help_panel=HELP_PANEL_HOST,
    hidden=True,
    deprecated=True,
    help="DEPRECATED: Use show_macro_templates instead.",
)
def show_macro_templates(
    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 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),
        )
    )