mreg-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mreg_cli/__about__.py +7 -0
- mreg_cli/__init__.py +5 -0
- mreg_cli/__main__.py +8 -0
- mreg_cli/_version.py +16 -0
- mreg_cli/api/__init__.py +7 -0
- mreg_cli/api/abstracts.py +486 -0
- mreg_cli/api/endpoints.py +130 -0
- mreg_cli/api/fields.py +104 -0
- mreg_cli/api/history.py +152 -0
- mreg_cli/api/models.py +3416 -0
- mreg_cli/cli.py +382 -0
- mreg_cli/commands/__init__.py +1 -0
- mreg_cli/commands/base.py +62 -0
- mreg_cli/commands/dhcp.py +136 -0
- mreg_cli/commands/group.py +326 -0
- mreg_cli/commands/help.py +77 -0
- mreg_cli/commands/host.py +54 -0
- mreg_cli/commands/host_submodules/__init__.py +28 -0
- mreg_cli/commands/host_submodules/a_aaaa.py +452 -0
- mreg_cli/commands/host_submodules/bacnet.py +126 -0
- mreg_cli/commands/host_submodules/cname.py +166 -0
- mreg_cli/commands/host_submodules/core.py +507 -0
- mreg_cli/commands/host_submodules/rr.py +973 -0
- mreg_cli/commands/label.py +146 -0
- mreg_cli/commands/logging.py +112 -0
- mreg_cli/commands/network.py +516 -0
- mreg_cli/commands/permission.py +202 -0
- mreg_cli/commands/policy.py +519 -0
- mreg_cli/commands/recording.py +59 -0
- mreg_cli/commands/registry.py +56 -0
- mreg_cli/commands/root.py +58 -0
- mreg_cli/commands/zone.py +288 -0
- mreg_cli/config.py +253 -0
- mreg_cli/errorbuilder.py +193 -0
- mreg_cli/exceptions.py +237 -0
- mreg_cli/help_formatter.py +38 -0
- mreg_cli/main.py +238 -0
- mreg_cli/outputmanager.py +466 -0
- mreg_cli/py.typed +0 -0
- mreg_cli/tags.txt +55 -0
- mreg_cli/tokenfile.py +89 -0
- mreg_cli/types.py +160 -0
- mreg_cli/utilities/__init__.py +5 -0
- mreg_cli/utilities/api.py +595 -0
- mreg_cli/utilities/shared.py +65 -0
- mreg_cli/utilities/validators.py +19 -0
- mreg_cli-1.0.0.dist-info/AUTHORS +12 -0
- mreg_cli-1.0.0.dist-info/LICENSE +674 -0
- mreg_cli-1.0.0.dist-info/METADATA +1079 -0
- mreg_cli-1.0.0.dist-info/RECORD +53 -0
- mreg_cli-1.0.0.dist-info/WHEEL +5 -0
- mreg_cli-1.0.0.dist-info/entry_points.txt +2 -0
- mreg_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,973 @@
|
|
|
1
|
+
"""Resource record related subcommands for the host command.
|
|
2
|
+
|
|
3
|
+
Commands implemented:
|
|
4
|
+
- hinfo_add
|
|
5
|
+
- hinfo_remove
|
|
6
|
+
- hinfo_show
|
|
7
|
+
- loc_add
|
|
8
|
+
- loc_remove
|
|
9
|
+
- loc_show
|
|
10
|
+
- mx_add
|
|
11
|
+
- mx_remove
|
|
12
|
+
- mx_show
|
|
13
|
+
- naptr_add
|
|
14
|
+
- naptr_remove
|
|
15
|
+
- naptr_show
|
|
16
|
+
- ptr_change
|
|
17
|
+
- ptr_remove
|
|
18
|
+
- ptr_add
|
|
19
|
+
- ptr_show
|
|
20
|
+
- srv_add
|
|
21
|
+
- srv_remove
|
|
22
|
+
- srv_show
|
|
23
|
+
- mx_add
|
|
24
|
+
- mx_remove
|
|
25
|
+
- mx_show
|
|
26
|
+
- txt_add
|
|
27
|
+
- txt_remove
|
|
28
|
+
- txt_show
|
|
29
|
+
- sshfp_add
|
|
30
|
+
- sshfp_remove
|
|
31
|
+
- sshfp_show
|
|
32
|
+
- ttl_set
|
|
33
|
+
- ttl_show
|
|
34
|
+
- cname_add
|
|
35
|
+
- cname_remove
|
|
36
|
+
- cname_replace
|
|
37
|
+
- cname_show
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
import argparse
|
|
43
|
+
|
|
44
|
+
from mreg_cli.api.models import (
|
|
45
|
+
MX,
|
|
46
|
+
NAPTR,
|
|
47
|
+
SSHFP,
|
|
48
|
+
TXT,
|
|
49
|
+
ForwardZone,
|
|
50
|
+
HInfo,
|
|
51
|
+
Host,
|
|
52
|
+
HostT,
|
|
53
|
+
Location,
|
|
54
|
+
Network,
|
|
55
|
+
NetworkOrIP,
|
|
56
|
+
PTR_override,
|
|
57
|
+
Srv,
|
|
58
|
+
)
|
|
59
|
+
from mreg_cli.commands.host import registry as command_registry
|
|
60
|
+
from mreg_cli.exceptions import (
|
|
61
|
+
CreateError,
|
|
62
|
+
DeleteError,
|
|
63
|
+
EntityAlreadyExists,
|
|
64
|
+
EntityNotFound,
|
|
65
|
+
ForceMissing,
|
|
66
|
+
InputFailure,
|
|
67
|
+
PatchError,
|
|
68
|
+
)
|
|
69
|
+
from mreg_cli.outputmanager import OutputManager
|
|
70
|
+
from mreg_cli.types import Flag, QueryParams
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@command_registry.register_command(
|
|
74
|
+
prog="hinfo_add",
|
|
75
|
+
description="Add HINFO for host. If NAME is an alias the cname host is updated.",
|
|
76
|
+
short_desc="Set HINFO.",
|
|
77
|
+
flags=[
|
|
78
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
79
|
+
Flag("cpu", description="CPU/hardware", metavar="CPU"),
|
|
80
|
+
Flag("os", description="Operating system", metavar="OS"),
|
|
81
|
+
],
|
|
82
|
+
)
|
|
83
|
+
def hinfo_add(args: argparse.Namespace) -> None:
|
|
84
|
+
"""Add hinfo for host.
|
|
85
|
+
|
|
86
|
+
If <name> is an alias the cname host is updated.
|
|
87
|
+
|
|
88
|
+
:param args: argparse.Namespace (name, cpu, os)
|
|
89
|
+
"""
|
|
90
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
91
|
+
if host.hinfo:
|
|
92
|
+
raise EntityAlreadyExists(f"{host} already has hinfo set.")
|
|
93
|
+
|
|
94
|
+
HInfo.create({"host": host.id, "cpu": args.cpu, "os": args.os})
|
|
95
|
+
host = host.refetch()
|
|
96
|
+
|
|
97
|
+
if host.hinfo and host.hinfo.cpu == args.cpu and host.hinfo.os == args.os:
|
|
98
|
+
OutputManager().add_ok(f"Added HINFO record for {host.name.hostname}.")
|
|
99
|
+
else:
|
|
100
|
+
raise CreateError(f"Failed to add correct HINFO for {host}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@command_registry.register_command(
|
|
104
|
+
prog="hinfo_remove",
|
|
105
|
+
description="Remove hinfo for host. If NAME is an alias the cname host is updated.",
|
|
106
|
+
short_desc="Remove HINFO.",
|
|
107
|
+
flags=[
|
|
108
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
109
|
+
],
|
|
110
|
+
)
|
|
111
|
+
def hinfo_remove(args: argparse.Namespace) -> None:
|
|
112
|
+
"""Remove hinfo for host.
|
|
113
|
+
|
|
114
|
+
If <name> is an alias the cname host is updated.
|
|
115
|
+
|
|
116
|
+
:param args: argparse.Namespace (name)
|
|
117
|
+
"""
|
|
118
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
119
|
+
if not host.hinfo:
|
|
120
|
+
raise EntityNotFound(f"{host} already has no hinfo set.")
|
|
121
|
+
|
|
122
|
+
hinfo = HInfo.get_by_field("host", host.id)
|
|
123
|
+
if hinfo and hinfo.delete():
|
|
124
|
+
OutputManager().add_ok(f"Removed HINFO record for {host.name.hostname}.")
|
|
125
|
+
else:
|
|
126
|
+
raise DeleteError(f"Failed to remove HINFO for {host}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@command_registry.register_command(
|
|
130
|
+
prog="hinfo_show",
|
|
131
|
+
description="Show hinfo for host. If NAME is an alias the cname hosts hinfo is shown.",
|
|
132
|
+
short_desc="Show HINFO.",
|
|
133
|
+
flags=[
|
|
134
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
135
|
+
],
|
|
136
|
+
)
|
|
137
|
+
def hinfo_show(args: argparse.Namespace) -> None:
|
|
138
|
+
"""Show hinfo for host.
|
|
139
|
+
|
|
140
|
+
If <name> is an alias the cname hosts hinfo is shown.
|
|
141
|
+
|
|
142
|
+
:param args: argparse.Namespace (name)
|
|
143
|
+
"""
|
|
144
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
145
|
+
if not host.hinfo:
|
|
146
|
+
OutputManager().add_line(f"No hinfo for {host.name.hostname}")
|
|
147
|
+
|
|
148
|
+
hinfo = HInfo.get_by_field("host", host.id)
|
|
149
|
+
if hinfo:
|
|
150
|
+
hinfo.output()
|
|
151
|
+
else:
|
|
152
|
+
OutputManager().add_line(f"No hinfo for {host.name.hostname}")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@command_registry.register_command(
|
|
156
|
+
prog="loc_remove",
|
|
157
|
+
description="Remove location from host. If NAME is an alias the cname host is updated.",
|
|
158
|
+
short_desc="Remove LOC record.",
|
|
159
|
+
flags=[
|
|
160
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
161
|
+
],
|
|
162
|
+
)
|
|
163
|
+
def loc_remove(args: argparse.Namespace) -> None:
|
|
164
|
+
"""Remove location from host.
|
|
165
|
+
|
|
166
|
+
If <name> is an alias the cname host is updated.
|
|
167
|
+
|
|
168
|
+
:param args: argparse.Namespace (name)
|
|
169
|
+
"""
|
|
170
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
171
|
+
if not host.loc:
|
|
172
|
+
raise EntityNotFound(f"{host} already has no loc set.")
|
|
173
|
+
|
|
174
|
+
if host.loc.delete():
|
|
175
|
+
OutputManager().add_ok(f"Removed LOC for {host.name.hostname}.")
|
|
176
|
+
else:
|
|
177
|
+
raise DeleteError(f"Failed to remove LOC for {host}")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@command_registry.register_command(
|
|
181
|
+
prog="loc_add",
|
|
182
|
+
description="Set location of host. If NAME is an alias the cname host is updated.",
|
|
183
|
+
short_desc="Set LOC record.",
|
|
184
|
+
flags=[
|
|
185
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
186
|
+
Flag("loc", description="New LOC.", metavar="LOC"),
|
|
187
|
+
],
|
|
188
|
+
)
|
|
189
|
+
def loc_add(args: argparse.Namespace) -> None:
|
|
190
|
+
"""Set location of host.
|
|
191
|
+
|
|
192
|
+
If <name> is an alias the cname host is updated.
|
|
193
|
+
|
|
194
|
+
:param args: argparse.Namespace (name, loc)
|
|
195
|
+
"""
|
|
196
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
197
|
+
|
|
198
|
+
if host.loc:
|
|
199
|
+
raise EntityAlreadyExists(f"{host} already has loc set.")
|
|
200
|
+
|
|
201
|
+
Location.create({"host": host.id, "loc": args.loc})
|
|
202
|
+
host = host.refetch()
|
|
203
|
+
|
|
204
|
+
if host.loc and host.loc.loc == args.loc:
|
|
205
|
+
OutputManager().add_ok(f"Added LOC record for {host.name.hostname}.")
|
|
206
|
+
else:
|
|
207
|
+
CreateError(f"Failed to set LOC for {host}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@command_registry.register_command(
|
|
211
|
+
prog="loc_show",
|
|
212
|
+
description="Show location of host. If NAME is an alias the cname hosts LOC is shown.",
|
|
213
|
+
short_desc="Show LOC record.",
|
|
214
|
+
flags=[
|
|
215
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
216
|
+
],
|
|
217
|
+
)
|
|
218
|
+
def loc_show(args: argparse.Namespace) -> None:
|
|
219
|
+
"""Show location of host.
|
|
220
|
+
|
|
221
|
+
If <name> is an alias the cname hosts LOC is shown.
|
|
222
|
+
|
|
223
|
+
:param args: argparse.Namespace (name)
|
|
224
|
+
"""
|
|
225
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
226
|
+
if not host.loc:
|
|
227
|
+
raise EntityNotFound(f"No loc for {host.name.hostname}")
|
|
228
|
+
|
|
229
|
+
host.loc.output()
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@command_registry.register_command(
|
|
233
|
+
prog="mx_add",
|
|
234
|
+
description="Add a MX record to host.",
|
|
235
|
+
short_desc="Add MX record.",
|
|
236
|
+
flags=[
|
|
237
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
238
|
+
Flag("priority", description="Priority", flag_type=int, metavar="PRIORITY"),
|
|
239
|
+
Flag("mx", description="Mail Server", metavar="MX"),
|
|
240
|
+
],
|
|
241
|
+
)
|
|
242
|
+
def mx_add(args: argparse.Namespace) -> None:
|
|
243
|
+
"""Add a mx record to host.
|
|
244
|
+
|
|
245
|
+
<text> must be enclosed in double quotes if it contains more than one word.
|
|
246
|
+
|
|
247
|
+
:param args: argparse.Namespace (name, priority, mx)
|
|
248
|
+
"""
|
|
249
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
250
|
+
if host.has_mx_with_priority(args.priority, args.mx):
|
|
251
|
+
raise EntityAlreadyExists(f"{host} already has that MX defined.")
|
|
252
|
+
|
|
253
|
+
MX.create({"host": host.id, "priority": args.priority, "mx": args.mx})
|
|
254
|
+
OutputManager().add_ok(f"Added MX record to {host.name.hostname}.")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@command_registry.register_command(
|
|
258
|
+
prog="mx_remove",
|
|
259
|
+
description="Remove MX record for host.",
|
|
260
|
+
short_desc="Remove MX record.",
|
|
261
|
+
flags=[
|
|
262
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
263
|
+
Flag("priority", description="Priority", flag_type=int, metavar="PRIORITY"),
|
|
264
|
+
Flag("mx", description="Mail Server", metavar="TEXT"),
|
|
265
|
+
],
|
|
266
|
+
)
|
|
267
|
+
def mx_remove(args: argparse.Namespace) -> None:
|
|
268
|
+
"""Remove MX record for host.
|
|
269
|
+
|
|
270
|
+
:param args: argparse.Namespace (name, priority, mx)
|
|
271
|
+
"""
|
|
272
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
273
|
+
mx = MX.get_by_all(host.id, args.mx, args.priority)
|
|
274
|
+
if not mx:
|
|
275
|
+
raise EntityNotFound(
|
|
276
|
+
f"{host} has no MX record with priority {args.priority} and mail exhange {args.mx}"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if mx.delete():
|
|
280
|
+
OutputManager().add_ok(f"deleted MX from {host.name.hostname}.")
|
|
281
|
+
else:
|
|
282
|
+
raise DeleteError(f"Failed to remove MX for {host}")
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@command_registry.register_command(
|
|
286
|
+
prog="mx_show",
|
|
287
|
+
description="Show all MX records for host.",
|
|
288
|
+
short_desc="Show MX records.",
|
|
289
|
+
flags=[
|
|
290
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
291
|
+
],
|
|
292
|
+
)
|
|
293
|
+
def mx_show(args: argparse.Namespace) -> None:
|
|
294
|
+
"""Show all MX records for host.
|
|
295
|
+
|
|
296
|
+
:param args: argparse.Namespace (name)
|
|
297
|
+
"""
|
|
298
|
+
MX.output_multiple(Host.get_by_any_means_or_raise(args.name).mxs)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@command_registry.register_command(
|
|
302
|
+
prog="naptr_add",
|
|
303
|
+
description="Add a NAPTR record to host.",
|
|
304
|
+
short_desc="Add NAPTR record.",
|
|
305
|
+
flags=[
|
|
306
|
+
Flag(
|
|
307
|
+
"-name",
|
|
308
|
+
description="Name of the target host.",
|
|
309
|
+
required=True,
|
|
310
|
+
metavar="NAME",
|
|
311
|
+
),
|
|
312
|
+
Flag(
|
|
313
|
+
"-preference",
|
|
314
|
+
description="NAPTR preference.",
|
|
315
|
+
flag_type=int,
|
|
316
|
+
required=True,
|
|
317
|
+
metavar="PREFERENCE",
|
|
318
|
+
),
|
|
319
|
+
Flag(
|
|
320
|
+
"-order",
|
|
321
|
+
description="NAPTR order.",
|
|
322
|
+
flag_type=int,
|
|
323
|
+
required=True,
|
|
324
|
+
metavar="ORDER",
|
|
325
|
+
),
|
|
326
|
+
Flag("-flag", description="NAPTR flag.", required=True, metavar="FLAG"),
|
|
327
|
+
Flag("-service", description="NAPTR service.", required=True, metavar="SERVICE"),
|
|
328
|
+
Flag("-regex", description="NAPTR regexp.", required=True, metavar="REGEXP"),
|
|
329
|
+
Flag(
|
|
330
|
+
"-replacement",
|
|
331
|
+
description="NAPTR replacement.",
|
|
332
|
+
required=True,
|
|
333
|
+
metavar="REPLACEMENT",
|
|
334
|
+
),
|
|
335
|
+
],
|
|
336
|
+
)
|
|
337
|
+
def naptr_add(args: argparse.Namespace) -> None:
|
|
338
|
+
"""Add a NAPTR record to host.
|
|
339
|
+
|
|
340
|
+
:param args: argparse.Namespace (name, preference, order, flag, service, regex, replacement)
|
|
341
|
+
"""
|
|
342
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
343
|
+
params: QueryParams = {
|
|
344
|
+
"preference": args.preference,
|
|
345
|
+
"order": args.order,
|
|
346
|
+
"flag": args.flag,
|
|
347
|
+
"service": args.service,
|
|
348
|
+
"regex": args.regex,
|
|
349
|
+
"replacement": args.replacement,
|
|
350
|
+
"host": host.id,
|
|
351
|
+
}
|
|
352
|
+
existing_naptr = NAPTR.get_by_query_unique(params)
|
|
353
|
+
if existing_naptr:
|
|
354
|
+
raise EntityAlreadyExists(f"{host} already has that NAPTR defined.")
|
|
355
|
+
NAPTR.create(params=params)
|
|
356
|
+
OutputManager().add_ok(f"Added NAPTR record to {host.name.hostname}.")
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@command_registry.register_command(
|
|
360
|
+
prog="naptr_remove",
|
|
361
|
+
description="Remove matching NAPTR records from a host.",
|
|
362
|
+
short_desc="Remove NAPTR record.",
|
|
363
|
+
flags=[
|
|
364
|
+
Flag(
|
|
365
|
+
"-name",
|
|
366
|
+
description="Name of the target host.",
|
|
367
|
+
required=True,
|
|
368
|
+
metavar="NAME",
|
|
369
|
+
),
|
|
370
|
+
Flag(
|
|
371
|
+
"-preference",
|
|
372
|
+
description="NAPTR preference.",
|
|
373
|
+
flag_type=int,
|
|
374
|
+
required=True,
|
|
375
|
+
metavar="PREFERENCE",
|
|
376
|
+
),
|
|
377
|
+
Flag(
|
|
378
|
+
"-order",
|
|
379
|
+
description="NAPTR order.",
|
|
380
|
+
flag_type=int,
|
|
381
|
+
required=True,
|
|
382
|
+
metavar="ORDER",
|
|
383
|
+
),
|
|
384
|
+
Flag("-flag", description="NAPTR flag.", required=True, metavar="FLAG"),
|
|
385
|
+
Flag("-service", description="NAPTR service.", required=True, metavar="SERVICE"),
|
|
386
|
+
Flag("-regex", description="NAPTR regexp.", required=True, metavar="REGEXP"),
|
|
387
|
+
Flag(
|
|
388
|
+
"-replacement",
|
|
389
|
+
description="NAPTR replacement.",
|
|
390
|
+
required=True,
|
|
391
|
+
metavar="REPLACEMENT",
|
|
392
|
+
),
|
|
393
|
+
Flag("-force", action="store_true", description="Force deletion for multiple records."),
|
|
394
|
+
],
|
|
395
|
+
)
|
|
396
|
+
def naptr_remove(args: argparse.Namespace) -> None:
|
|
397
|
+
"""Remove NAPTR matching records from host.
|
|
398
|
+
|
|
399
|
+
:param args: argparse.Namespace (name, preference, order, flag, service, regex, replacement)
|
|
400
|
+
"""
|
|
401
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
402
|
+
naptrs = host.naptrs()
|
|
403
|
+
|
|
404
|
+
to_delete: list[NAPTR] = []
|
|
405
|
+
|
|
406
|
+
for naptr in naptrs:
|
|
407
|
+
for attribute in ("preference", "order", "flag", "service", "regex", "replacement"):
|
|
408
|
+
if getattr(args, attribute) and getattr(naptr, attribute) != getattr(args, attribute):
|
|
409
|
+
break
|
|
410
|
+
|
|
411
|
+
to_delete.append(naptr)
|
|
412
|
+
|
|
413
|
+
if not to_delete:
|
|
414
|
+
raise EntityNotFound(f"No matching NAPTR record found for {host}")
|
|
415
|
+
|
|
416
|
+
if len(to_delete) > 1 and not args.force:
|
|
417
|
+
OutputManager().add_line("Found multiple matching NAPTR records:")
|
|
418
|
+
NAPTR.output_multiple(to_delete)
|
|
419
|
+
raise ForceMissing("Use --force to delete all matching records.")
|
|
420
|
+
|
|
421
|
+
# This should ideally be done in a transaction, but the API doesn't support it.
|
|
422
|
+
# Right now we may end up in a situation where some records are deleted and some are not.
|
|
423
|
+
for naptr in to_delete:
|
|
424
|
+
if naptr.delete():
|
|
425
|
+
OutputManager().add_ok(f"Deleted NAPTR record from {host.name.hostname}.")
|
|
426
|
+
else:
|
|
427
|
+
raise DeleteError(f"Failed to remove NAPTR for {host}")
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
@command_registry.register_command(
|
|
431
|
+
prog="naptr_show",
|
|
432
|
+
description="Show all NAPTR records for host.",
|
|
433
|
+
short_desc="Show NAPTR records.",
|
|
434
|
+
flags=[
|
|
435
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
436
|
+
],
|
|
437
|
+
)
|
|
438
|
+
def naptr_show(args: argparse.Namespace) -> None:
|
|
439
|
+
"""Show all NAPTR records for host.
|
|
440
|
+
|
|
441
|
+
:param args: argparse.Namespace (name)
|
|
442
|
+
"""
|
|
443
|
+
NAPTR.output_multiple(Host.get_by_any_means_or_raise(args.name).naptrs())
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
@command_registry.register_command(
|
|
447
|
+
prog="ptr_change",
|
|
448
|
+
description="Move PTR record from OLD to NEW.",
|
|
449
|
+
short_desc="Move PTR record.",
|
|
450
|
+
flags=[
|
|
451
|
+
Flag(
|
|
452
|
+
"-ip",
|
|
453
|
+
description="IP of PTR record. May be IPv4 or IPv6.",
|
|
454
|
+
short_desc="IP of PTR record.",
|
|
455
|
+
required=True,
|
|
456
|
+
metavar="IP",
|
|
457
|
+
),
|
|
458
|
+
Flag("-old", description="Name of old host.", required=True, metavar="NAME"),
|
|
459
|
+
Flag("-new", description="Name of new host.", required=True, metavar="NAME"),
|
|
460
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
461
|
+
],
|
|
462
|
+
)
|
|
463
|
+
def ptr_change(args: argparse.Namespace) -> None:
|
|
464
|
+
"""Move PTR record from <old-name> to <new-name>.
|
|
465
|
+
|
|
466
|
+
:param args: argparse.Namespace (ip, old, new, force)
|
|
467
|
+
"""
|
|
468
|
+
old_host = Host.get_by_any_means_or_raise(args.old)
|
|
469
|
+
new_host = Host.get_by_any_means_or_raise(args.new)
|
|
470
|
+
|
|
471
|
+
if new_host.ptr_overrides:
|
|
472
|
+
raise InputFailure(f"{new_host} already has a PTR record.")
|
|
473
|
+
|
|
474
|
+
if not old_host.ptr_overrides:
|
|
475
|
+
raise EntityNotFound(f"No PTR records for {old_host}")
|
|
476
|
+
|
|
477
|
+
ip = NetworkOrIP(ip_or_network=args.ip)
|
|
478
|
+
if not ip.is_ip():
|
|
479
|
+
raise InputFailure(f"{args.ip} is not a valid IP")
|
|
480
|
+
|
|
481
|
+
ip = ip.as_ip()
|
|
482
|
+
ptr_override = old_host.get_ptr_override(ip)
|
|
483
|
+
if not ptr_override:
|
|
484
|
+
raise EntityNotFound(f"No PTR record for {old_host} with IP {ip}")
|
|
485
|
+
|
|
486
|
+
data = {"host": new_host.id}
|
|
487
|
+
if not ptr_override.patch(data):
|
|
488
|
+
raise PatchError(f"Failed to move PTR record from {old_host} to {new_host}")
|
|
489
|
+
else:
|
|
490
|
+
OutputManager().add_ok(
|
|
491
|
+
f"Moved PTR record {ip} from {old_host.name.hostname} to {new_host.name.hostname}."
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
@command_registry.register_command(
|
|
496
|
+
prog="ptr_remove",
|
|
497
|
+
description="Remove PTR record from host.",
|
|
498
|
+
short_desc="Remove PTR record.",
|
|
499
|
+
flags=[
|
|
500
|
+
Flag("ip", description="IP of PTR record. May be IPv4 or IPv6.", metavar="IP"),
|
|
501
|
+
Flag("name", description="Name of host.", metavar="NAME"),
|
|
502
|
+
],
|
|
503
|
+
)
|
|
504
|
+
def ptr_remove(args: argparse.Namespace) -> None:
|
|
505
|
+
"""Remove PTR record from host.
|
|
506
|
+
|
|
507
|
+
:param args: argparse.Namespace (ip, name)
|
|
508
|
+
"""
|
|
509
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
510
|
+
ip = NetworkOrIP(ip_or_network=args.ip)
|
|
511
|
+
if not ip.is_ip():
|
|
512
|
+
raise InputFailure(f"{args.ip} is not a valid IP")
|
|
513
|
+
|
|
514
|
+
ip = ip.as_ip()
|
|
515
|
+
ptr_override = host.get_ptr_override(ip)
|
|
516
|
+
if not ptr_override:
|
|
517
|
+
raise EntityNotFound(f"No PTR record for {host} with IP {ip}")
|
|
518
|
+
|
|
519
|
+
if ptr_override.delete():
|
|
520
|
+
OutputManager().add_ok(f"Removed PTR record {ip} from {host.name.hostname}.")
|
|
521
|
+
else:
|
|
522
|
+
raise DeleteError(f"Failed to remove PTR record from {host}")
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@command_registry.register_command(
|
|
526
|
+
prog="ptr_add",
|
|
527
|
+
description="Create a PTR record for host.",
|
|
528
|
+
short_desc="Add PTR record.",
|
|
529
|
+
flags=[
|
|
530
|
+
Flag("ip", description="IP of PTR record. May be IPv4 or IPv6.", metavar="IP"),
|
|
531
|
+
Flag("name", description="Name of host.", metavar="NAME"),
|
|
532
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
533
|
+
],
|
|
534
|
+
)
|
|
535
|
+
def ptr_add(args: argparse.Namespace) -> None:
|
|
536
|
+
"""Create a PTR record for host.
|
|
537
|
+
|
|
538
|
+
:param args: argparse.Namespace (ip, name, force)
|
|
539
|
+
"""
|
|
540
|
+
ip = NetworkOrIP(ip_or_network=args.ip)
|
|
541
|
+
if not ip.is_ip():
|
|
542
|
+
raise InputFailure(f"{args.ip} is not a valid IP")
|
|
543
|
+
ip = ip.as_ip()
|
|
544
|
+
|
|
545
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
546
|
+
existing_ptrs = PTR_override.get_list_by_field("ipaddress", str(ip))
|
|
547
|
+
if existing_ptrs:
|
|
548
|
+
raise EntityAlreadyExists(f"{ip} already exists in a PTR record.")
|
|
549
|
+
|
|
550
|
+
if host.zone is None and not args.force:
|
|
551
|
+
raise ForceMissing(f"{host} isn't in a zone controlled by MREG, must force")
|
|
552
|
+
|
|
553
|
+
network = Network.get_by_ip(ip)
|
|
554
|
+
if not network:
|
|
555
|
+
raise EntityNotFound(f"{ip} isn't in a network controlled by MREG")
|
|
556
|
+
|
|
557
|
+
if network.is_reserved_ip(ip) and not args.force:
|
|
558
|
+
raise ForceMissing("Address is reserved. Requires force")
|
|
559
|
+
|
|
560
|
+
PTR_override.create({"host": host.id, "ipaddress": str(ip)})
|
|
561
|
+
OutputManager().add_ok(f"Added PTR record {ip} to {host.name.hostname}.")
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
@command_registry.register_command(
|
|
565
|
+
prog="ptr_show",
|
|
566
|
+
description="Show PTR record matching given ip (empty input shows all PTR records).",
|
|
567
|
+
short_desc="Show PTR record.",
|
|
568
|
+
flags=[
|
|
569
|
+
Flag("ip", description="IP of PTR record. May be IPv4 or IPv6.", metavar="IP"),
|
|
570
|
+
],
|
|
571
|
+
)
|
|
572
|
+
def ptr_show(args: argparse.Namespace) -> None:
|
|
573
|
+
"""Show PTR record matching given ip.
|
|
574
|
+
|
|
575
|
+
:param args: argparse.Namespace (ip)
|
|
576
|
+
"""
|
|
577
|
+
ip = NetworkOrIP(ip_or_network=args.ip)
|
|
578
|
+
if not ip.is_ip():
|
|
579
|
+
raise InputFailure(f"{args.ip} is not a valid IP")
|
|
580
|
+
|
|
581
|
+
host = Host.get_by_any_means_or_raise(str(ip), inform_as_ptr=False)
|
|
582
|
+
if not host.ptr_overrides:
|
|
583
|
+
OutputManager().add_line(f"No PTR records for {host.name.hostname}")
|
|
584
|
+
|
|
585
|
+
for ptr in host.ptr_overrides:
|
|
586
|
+
if ip.as_ip() == ptr.ipaddress:
|
|
587
|
+
ptr.output()
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
@command_registry.register_command(
|
|
591
|
+
prog="srv_add",
|
|
592
|
+
description="Add SRV record.",
|
|
593
|
+
short_desc="Add SRV record.",
|
|
594
|
+
flags=[
|
|
595
|
+
Flag("-name", description="SRV service.", required=True, metavar="SERVICE"),
|
|
596
|
+
Flag("-priority", description="SRV priority.", required=True, metavar="PRIORITY"),
|
|
597
|
+
Flag("-weight", description="SRV weight.", required=True, metavar="WEIGHT"),
|
|
598
|
+
Flag("-port", description="SRV port.", required=True, metavar="PORT"),
|
|
599
|
+
Flag("-host", description="Host target name.", required=True, metavar="NAME"),
|
|
600
|
+
Flag("-ttl", description="TTL value", metavar="TTL"),
|
|
601
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
602
|
+
],
|
|
603
|
+
)
|
|
604
|
+
def srv_add(args: argparse.Namespace) -> None:
|
|
605
|
+
"""Add SRV record.
|
|
606
|
+
|
|
607
|
+
:param args: argparse.Namespace (name, priority, weight, port, host, ttl, force)
|
|
608
|
+
"""
|
|
609
|
+
sname = HostT(hostname=args.name)
|
|
610
|
+
host = Host.get_by_any_means_or_raise(args.host)
|
|
611
|
+
|
|
612
|
+
szone = ForwardZone.get_from_hostname(sname)
|
|
613
|
+
if not szone:
|
|
614
|
+
raise EntityNotFound(f"{sname} isn't in a zone controlled by MREG")
|
|
615
|
+
|
|
616
|
+
hzone = ForwardZone.get_from_hostname(host.name)
|
|
617
|
+
if not hzone:
|
|
618
|
+
raise EntityNotFound(f"{host} isn't in a zone controlled by MREG")
|
|
619
|
+
|
|
620
|
+
data: QueryParams = {
|
|
621
|
+
"name": sname.hostname,
|
|
622
|
+
"priority": args.priority,
|
|
623
|
+
"weight": args.weight,
|
|
624
|
+
"port": args.port,
|
|
625
|
+
"host": host.id,
|
|
626
|
+
"ttl": args.ttl,
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
existing_srv = Srv.get_by_query_unique(data)
|
|
630
|
+
if existing_srv:
|
|
631
|
+
raise EntityAlreadyExists(f"{sname} already has that SRV defined.")
|
|
632
|
+
Srv.create(data)
|
|
633
|
+
OutputManager().add_ok(f"Added SRV record {sname} with target {host}.")
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
@command_registry.register_command(
|
|
637
|
+
prog="srv_remove",
|
|
638
|
+
description="Remove SRV record.",
|
|
639
|
+
short_desc="Remove SRV record.",
|
|
640
|
+
flags=[
|
|
641
|
+
Flag("-name", description="SRV service.", required=True, metavar="SERVICE"),
|
|
642
|
+
Flag(
|
|
643
|
+
"-priority",
|
|
644
|
+
description="SRV priority.",
|
|
645
|
+
flag_type=int,
|
|
646
|
+
required=True,
|
|
647
|
+
metavar="PRIORITY",
|
|
648
|
+
),
|
|
649
|
+
Flag(
|
|
650
|
+
"-weight",
|
|
651
|
+
description="SRV weight.",
|
|
652
|
+
flag_type=int,
|
|
653
|
+
required=True,
|
|
654
|
+
metavar="WEIGHT",
|
|
655
|
+
),
|
|
656
|
+
Flag(
|
|
657
|
+
"-port",
|
|
658
|
+
description="SRV port.",
|
|
659
|
+
flag_type=int,
|
|
660
|
+
required=True,
|
|
661
|
+
metavar="PORT",
|
|
662
|
+
),
|
|
663
|
+
Flag("-host", description="Host target name.", required=True, metavar="NAME"),
|
|
664
|
+
],
|
|
665
|
+
)
|
|
666
|
+
def srv_remove(args: argparse.Namespace) -> None:
|
|
667
|
+
"""Remove SRV record.
|
|
668
|
+
|
|
669
|
+
:param args: argparse.Namespace (name, priority, weight, port, host)
|
|
670
|
+
"""
|
|
671
|
+
host = Host.get_by_any_means_or_raise(args.host)
|
|
672
|
+
sname = HostT(hostname=args.name)
|
|
673
|
+
|
|
674
|
+
data: QueryParams = {
|
|
675
|
+
"name": sname.hostname,
|
|
676
|
+
"host": host.id,
|
|
677
|
+
"priority": args.priority,
|
|
678
|
+
"port": args.port,
|
|
679
|
+
"weight": args.weight,
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
srv = Srv.get_by_query_unique(data)
|
|
683
|
+
if not srv:
|
|
684
|
+
raise EntityNotFound(
|
|
685
|
+
f"No SRV record for {sname} with target {host} matching the given values."
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
if srv.delete():
|
|
689
|
+
OutputManager().add_ok(f"Removed SRV record {sname} from {host.name.hostname}.")
|
|
690
|
+
else:
|
|
691
|
+
raise DeleteError(f"Failed to remove SRV for {sname}")
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
@command_registry.register_command(
|
|
695
|
+
prog="srv_show",
|
|
696
|
+
description="Show SRV records for the service.",
|
|
697
|
+
short_desc="Show SRV records.",
|
|
698
|
+
flags=[
|
|
699
|
+
Flag("service", description="Host target name.", metavar="SERVICE"),
|
|
700
|
+
],
|
|
701
|
+
)
|
|
702
|
+
def srv_show(args: argparse.Namespace) -> None:
|
|
703
|
+
"""Show SRV records for the service.
|
|
704
|
+
|
|
705
|
+
:param args: argparse.Namespace (service)
|
|
706
|
+
"""
|
|
707
|
+
sname = HostT(hostname=args.service)
|
|
708
|
+
srvs = Srv.get_list_by_field("name", sname.hostname)
|
|
709
|
+
|
|
710
|
+
if len(srvs) == 0:
|
|
711
|
+
raise EntityNotFound(f"No SRV records for {sname}")
|
|
712
|
+
|
|
713
|
+
Srv.output_multiple(srvs)
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
@command_registry.register_command(
|
|
717
|
+
prog="sshfp_add",
|
|
718
|
+
description="Add SSHFP record.",
|
|
719
|
+
short_desc="Add SSHFP record.",
|
|
720
|
+
flags=[
|
|
721
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
722
|
+
Flag("algorithm", description="SSH algorithm.", metavar="ALGORITHM"),
|
|
723
|
+
Flag("hash_type", description="Hash type.", metavar="HASH_TYPE"),
|
|
724
|
+
Flag("fingerprint", description="Hexadecimal fingerprint.", metavar="FINGERPRINT"),
|
|
725
|
+
],
|
|
726
|
+
)
|
|
727
|
+
def sshfp_add(args: argparse.Namespace) -> None:
|
|
728
|
+
"""Add SSHFP record.
|
|
729
|
+
|
|
730
|
+
:param args: argparse.Namespace (name, algorithm, hash_type, fingerprint)
|
|
731
|
+
"""
|
|
732
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
733
|
+
|
|
734
|
+
data: QueryParams = {
|
|
735
|
+
"algorithm": args.algorithm,
|
|
736
|
+
"hash_type": args.hash_type,
|
|
737
|
+
"fingerprint": args.fingerprint,
|
|
738
|
+
"host": host.id,
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
existing_sshfp = SSHFP.get_by_query_unique(data)
|
|
742
|
+
if existing_sshfp:
|
|
743
|
+
raise EntityAlreadyExists(f"{host} already has that SSHFP defined.")
|
|
744
|
+
|
|
745
|
+
SSHFP.create(data)
|
|
746
|
+
OutputManager().add_ok(f"Added SSHFP record for {host.name.hostname}.")
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
@command_registry.register_command(
|
|
750
|
+
prog="sshfp_remove",
|
|
751
|
+
description=(
|
|
752
|
+
"Remove SSHFP record with a given fingerprint from the host. "
|
|
753
|
+
"A missing fingerprint removes all SSHFP records for the host."
|
|
754
|
+
),
|
|
755
|
+
short_desc="Remove SSHFP record.",
|
|
756
|
+
flags=[
|
|
757
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
758
|
+
Flag(
|
|
759
|
+
"-fingerprint",
|
|
760
|
+
description="Hexadecimal fingerprint.",
|
|
761
|
+
metavar="FINGERPRINT",
|
|
762
|
+
),
|
|
763
|
+
],
|
|
764
|
+
)
|
|
765
|
+
def sshfp_remove(args: argparse.Namespace) -> None:
|
|
766
|
+
"""Remove SSHFP record from the host.
|
|
767
|
+
|
|
768
|
+
A missing fingerprint removes all SSHFP records for the host.
|
|
769
|
+
|
|
770
|
+
:param args: argparse.Namespace (name, fingerprint)
|
|
771
|
+
"""
|
|
772
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
773
|
+
sshfps = None
|
|
774
|
+
|
|
775
|
+
if args.fingerprint:
|
|
776
|
+
sshfps = [
|
|
777
|
+
SSHFP.get_by_query_unique_or_raise(
|
|
778
|
+
{"fingerprint": args.fingerprint, "host": host.id}
|
|
779
|
+
)
|
|
780
|
+
]
|
|
781
|
+
else:
|
|
782
|
+
sshfps = host.sshfps()
|
|
783
|
+
|
|
784
|
+
if not sshfps:
|
|
785
|
+
raise EntityNotFound(f"No matching SSHFP records for {host}")
|
|
786
|
+
else:
|
|
787
|
+
for sshfp in sshfps:
|
|
788
|
+
if not sshfp.delete():
|
|
789
|
+
raise DeleteError(f"Failed to remove SSHFP for {host}")
|
|
790
|
+
else:
|
|
791
|
+
fp = sshfp.fingerprint
|
|
792
|
+
OutputManager().add_ok(f"Removed SSHFP record with fingerprint {fp} for {host}.")
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
@command_registry.register_command(
|
|
796
|
+
prog="sshfp_show",
|
|
797
|
+
description="Show SSHFP records for the host.",
|
|
798
|
+
short_desc="Show SSHFP record.",
|
|
799
|
+
flags=[
|
|
800
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
801
|
+
],
|
|
802
|
+
)
|
|
803
|
+
def sshfp_show(args: argparse.Namespace) -> None:
|
|
804
|
+
"""Show SSHFP records for the host.
|
|
805
|
+
|
|
806
|
+
:param args: argparse.Namespace (name)
|
|
807
|
+
"""
|
|
808
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
809
|
+
sshfps = host.sshfps()
|
|
810
|
+
|
|
811
|
+
if not sshfps:
|
|
812
|
+
raise EntityNotFound(f"No SSHFP records for {host}")
|
|
813
|
+
|
|
814
|
+
SSHFP.output_multiple(sshfps)
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
@command_registry.register_command(
|
|
818
|
+
prog="ttl_remove",
|
|
819
|
+
description="Remove explicit TTL for host. If NAME is an alias the alias host is updated.",
|
|
820
|
+
short_desc="Remove TTL record.",
|
|
821
|
+
flags=[
|
|
822
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
823
|
+
],
|
|
824
|
+
)
|
|
825
|
+
def ttl_remove(args: argparse.Namespace) -> None:
|
|
826
|
+
"""Remove explicit TTL for host.
|
|
827
|
+
|
|
828
|
+
If <name> is an alias the alias host is updated.
|
|
829
|
+
|
|
830
|
+
:param args: argparse.Namespace (name)
|
|
831
|
+
"""
|
|
832
|
+
args.ttl = "default"
|
|
833
|
+
ttl_set(args)
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
@command_registry.register_command(
|
|
837
|
+
prog="ttl_set",
|
|
838
|
+
description=(
|
|
839
|
+
"Set ttl for host. Valid values are 300 <= TTL <= 68400 or "
|
|
840
|
+
'"default". If NAME is an alias the alias host is updated.'
|
|
841
|
+
),
|
|
842
|
+
short_desc="Set TTL record.",
|
|
843
|
+
flags=[
|
|
844
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
845
|
+
Flag("ttl", description="New TTL.", metavar="TTL"),
|
|
846
|
+
],
|
|
847
|
+
)
|
|
848
|
+
def ttl_set(args: argparse.Namespace) -> None:
|
|
849
|
+
"""Set ttl for name.
|
|
850
|
+
|
|
851
|
+
Valid values are 300 <= TTL <= 68400 or "default".
|
|
852
|
+
If <name> is an alias the alias host is updated.
|
|
853
|
+
|
|
854
|
+
:param args: argparse.Namespace (name, ttl)
|
|
855
|
+
"""
|
|
856
|
+
name: str = args.name
|
|
857
|
+
ttl: str = args.ttl
|
|
858
|
+
|
|
859
|
+
target = Host.get_by_any_means(name)
|
|
860
|
+
if not target:
|
|
861
|
+
target = Srv.get_by_field("name", name)
|
|
862
|
+
|
|
863
|
+
if not target:
|
|
864
|
+
raise EntityNotFound(f"No host or SRV record found for {name}")
|
|
865
|
+
|
|
866
|
+
result = target.set_ttl(ttl)
|
|
867
|
+
new_ttl = result.ttl or ttl # prefer the actual value if it exists
|
|
868
|
+
if result:
|
|
869
|
+
OutputManager().add_ok(f"Set TTL for {target} to {new_ttl}.")
|
|
870
|
+
else:
|
|
871
|
+
raise PatchError(f"Failed to set TTL for {target}")
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
@command_registry.register_command(
|
|
875
|
+
prog="ttl_show",
|
|
876
|
+
description="Show ttl for name.",
|
|
877
|
+
short_desc="Show TTL.",
|
|
878
|
+
flags=[
|
|
879
|
+
Flag("name", description="Name", metavar="NAME"),
|
|
880
|
+
],
|
|
881
|
+
)
|
|
882
|
+
def ttl_show(args: argparse.Namespace) -> None:
|
|
883
|
+
"""Show ttl for name.
|
|
884
|
+
|
|
885
|
+
If <name> is an alias the alias hosts TTL is shown.
|
|
886
|
+
|
|
887
|
+
:param args: argparse.Namespace (name)
|
|
888
|
+
"""
|
|
889
|
+
Host.get_by_any_means_or_raise(args.name).output_ttl()
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
@command_registry.register_command(
|
|
893
|
+
prog="txt_add",
|
|
894
|
+
description=(
|
|
895
|
+
"Add a txt record to host. TEXT must be enclosed in double "
|
|
896
|
+
"quotes if it contains more than one word."
|
|
897
|
+
),
|
|
898
|
+
short_desc="Add TXT record.",
|
|
899
|
+
flags=[
|
|
900
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
901
|
+
Flag(
|
|
902
|
+
"text",
|
|
903
|
+
description="TXT record text. Must be quoted if contains spaces.",
|
|
904
|
+
metavar="TEXT",
|
|
905
|
+
),
|
|
906
|
+
],
|
|
907
|
+
)
|
|
908
|
+
def txt_add(args: argparse.Namespace) -> None:
|
|
909
|
+
"""Add a txt record to host.
|
|
910
|
+
|
|
911
|
+
<text> must be enclosed in double quotes if it contains more than one word.
|
|
912
|
+
|
|
913
|
+
:param args: argparse.Namespace (name, text)
|
|
914
|
+
"""
|
|
915
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
916
|
+
if host.has_txt(args.text):
|
|
917
|
+
raise EntityAlreadyExists(f"{host} already has that TXT defined.")
|
|
918
|
+
|
|
919
|
+
TXT.create({"host": host.id, "txt": args.text})
|
|
920
|
+
OutputManager().add_ok(f"Added TXT record to {host}.")
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
@command_registry.register_command(
|
|
924
|
+
prog="txt_remove",
|
|
925
|
+
description=" Remove TXT record for host matching TEXT.",
|
|
926
|
+
short_desc="Remove TXT record.",
|
|
927
|
+
flags=[
|
|
928
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
929
|
+
Flag(
|
|
930
|
+
"text",
|
|
931
|
+
description="TXT record text. Must be quoted if contains spaces.",
|
|
932
|
+
metavar="TEXT",
|
|
933
|
+
),
|
|
934
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
935
|
+
],
|
|
936
|
+
)
|
|
937
|
+
def txt_remove(args: argparse.Namespace) -> None:
|
|
938
|
+
"""Remove TXT record for host with <text>.
|
|
939
|
+
|
|
940
|
+
:param args: argparse.Namespace (name, text)
|
|
941
|
+
"""
|
|
942
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
943
|
+
txt = TXT.get_by_query_unique({"host": host.id, "txt": args.text})
|
|
944
|
+
|
|
945
|
+
if not txt:
|
|
946
|
+
raise EntityNotFound(f"{host} has no TXT record matching '{args.text}'")
|
|
947
|
+
|
|
948
|
+
if txt.delete():
|
|
949
|
+
OutputManager().add_ok(f"Removed TXT record '{args.text}' from {host}.")
|
|
950
|
+
else:
|
|
951
|
+
raise DeleteError(f"Failed to remove TXT with '{args.text}' for {host}")
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
@command_registry.register_command(
|
|
955
|
+
prog="txt_show",
|
|
956
|
+
description="Show all TXT records for host.",
|
|
957
|
+
short_desc="Show TXT records.",
|
|
958
|
+
flags=[
|
|
959
|
+
Flag("name", description="Host target name.", metavar="NAME"),
|
|
960
|
+
],
|
|
961
|
+
)
|
|
962
|
+
def txt_show(args: argparse.Namespace) -> None:
|
|
963
|
+
"""Show all TXT records for host.
|
|
964
|
+
|
|
965
|
+
:param args: argparse.Namespace (name)
|
|
966
|
+
"""
|
|
967
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
968
|
+
txts = host.txts
|
|
969
|
+
|
|
970
|
+
if not txts:
|
|
971
|
+
raise EntityNotFound(f"No TXT records for {host}")
|
|
972
|
+
|
|
973
|
+
TXT.output_multiple(txts, padding=5)
|