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,452 @@
|
|
|
1
|
+
"""A/AAAA-related subcommands for the host command.
|
|
2
|
+
|
|
3
|
+
Commands implemented:
|
|
4
|
+
- a_add
|
|
5
|
+
- a_change
|
|
6
|
+
- a_move
|
|
7
|
+
- a_remove
|
|
8
|
+
- a_show
|
|
9
|
+
- aaaa_add
|
|
10
|
+
- aaaa_change
|
|
11
|
+
- aaaa_move
|
|
12
|
+
- aaaa_remove
|
|
13
|
+
- aaaa_show
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
|
|
20
|
+
from mreg_cli.api.models import Host, HostList, IPAddress, MACAddressField, Network, NetworkOrIP
|
|
21
|
+
from mreg_cli.commands.host import registry as command_registry
|
|
22
|
+
from mreg_cli.exceptions import (
|
|
23
|
+
DeleteError,
|
|
24
|
+
EntityAlreadyExists,
|
|
25
|
+
EntityNotFound,
|
|
26
|
+
ForceMissing,
|
|
27
|
+
InputFailure,
|
|
28
|
+
)
|
|
29
|
+
from mreg_cli.outputmanager import OutputManager
|
|
30
|
+
from mreg_cli.types import Flag, IP_AddressT, IP_Version
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _bail_if_ip_in_use_and_not_force(ip: IP_AddressT) -> None:
|
|
34
|
+
"""Check if an IP is in use and bail if it is.
|
|
35
|
+
|
|
36
|
+
:param ip: The IP address to check.
|
|
37
|
+
"""
|
|
38
|
+
hosts_using_ip = HostList.get_by_ip(ip)
|
|
39
|
+
if hosts_using_ip:
|
|
40
|
+
hostnames = ", ".join(hosts_using_ip.hostnames())
|
|
41
|
+
raise ForceMissing(f"IP {ip} in use by {hostnames}, must force.")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _ip_change(args: argparse.Namespace, ipversion: IP_Version) -> None:
|
|
45
|
+
"""Change A record. If <name> is an alias the cname host is used.
|
|
46
|
+
|
|
47
|
+
:param args: argparse.Namespace (name, old, new, force)
|
|
48
|
+
"""
|
|
49
|
+
if args.old == args.new:
|
|
50
|
+
raise EntityAlreadyExists("New and old IP are equal")
|
|
51
|
+
|
|
52
|
+
old_ip = NetworkOrIP(ip_or_network=args.old).as_ip()
|
|
53
|
+
|
|
54
|
+
new_ip = NetworkOrIP(ip_or_network=args.new)
|
|
55
|
+
if new_ip.is_network():
|
|
56
|
+
network = Network.get_by_network_or_raise(str(new_ip.ip_or_network))
|
|
57
|
+
new_ip = network.get_first_available_ip()
|
|
58
|
+
else:
|
|
59
|
+
new_ip = new_ip.as_ip()
|
|
60
|
+
|
|
61
|
+
if old_ip.version != ipversion:
|
|
62
|
+
raise InputFailure("Old IP version does not match the requested version")
|
|
63
|
+
|
|
64
|
+
if new_ip.version != ipversion:
|
|
65
|
+
raise InputFailure("New IP version does not match the requested version")
|
|
66
|
+
|
|
67
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
68
|
+
|
|
69
|
+
host_ip = host.get_ip(old_ip)
|
|
70
|
+
if not host_ip:
|
|
71
|
+
raise EntityNotFound(f"Host {host} does not have IP {old_ip}")
|
|
72
|
+
|
|
73
|
+
ip_obj = IPAddress.get(host_ip.id)
|
|
74
|
+
if not ip_obj:
|
|
75
|
+
raise EntityNotFound(f"IP {old_ip} not found")
|
|
76
|
+
|
|
77
|
+
if not args.force:
|
|
78
|
+
_bail_if_ip_in_use_and_not_force(new_ip)
|
|
79
|
+
|
|
80
|
+
ip_obj.patch(fields={"ipaddress": str(new_ip)})
|
|
81
|
+
|
|
82
|
+
OutputManager().add_ok(f"changed ip {args.old} to {new_ip} for {host}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _ip_move(args: argparse.Namespace, ipversion: IP_Version) -> None:
|
|
86
|
+
"""Move an IP from a host to another host. Will move also move the PTR, if any.
|
|
87
|
+
|
|
88
|
+
:param args: argparse.Namespace (ip, fromhost, tohost)
|
|
89
|
+
:param ipversion: 4 or 6
|
|
90
|
+
"""
|
|
91
|
+
ip = NetworkOrIP(ip_or_network=args.ip)
|
|
92
|
+
if ip.is_network():
|
|
93
|
+
raise InputFailure("IP cannot be a network")
|
|
94
|
+
if ip.as_ip().version != ipversion:
|
|
95
|
+
raise InputFailure(
|
|
96
|
+
f"IP version {ip.as_ip().version} does not match the requested version {ipversion}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
from_host = Host.get_by_any_means_or_raise(args.fromhost)
|
|
100
|
+
to_host = Host.get_by_any_means_or_raise(args.tohost)
|
|
101
|
+
|
|
102
|
+
host_ip = from_host.get_ip(ip.as_ip())
|
|
103
|
+
|
|
104
|
+
ptr = from_host.get_ptr_override(ip.as_ip())
|
|
105
|
+
if not host_ip and not ptr:
|
|
106
|
+
raise EntityNotFound(f"Host {from_host} has no IP or PTR with address {ip}")
|
|
107
|
+
|
|
108
|
+
msg = ""
|
|
109
|
+
if host_ip:
|
|
110
|
+
host_ip.patch(fields={"host": to_host.id})
|
|
111
|
+
msg = f"Moved ipaddress {args.ip}"
|
|
112
|
+
else:
|
|
113
|
+
msg += "No ipaddresses matched. "
|
|
114
|
+
|
|
115
|
+
if ptr:
|
|
116
|
+
ptr.patch(fields={"host": to_host.id})
|
|
117
|
+
msg += "Moved PTR override."
|
|
118
|
+
|
|
119
|
+
OutputManager().add_line(msg)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _ip_remove(args: argparse.Namespace, ipversion: IP_Version) -> None:
|
|
123
|
+
"""Remove A record from host. If <name> is an alias the cname host is used.
|
|
124
|
+
|
|
125
|
+
:param args: argparse.Namespace (name, ip)
|
|
126
|
+
"""
|
|
127
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
128
|
+
ip = NetworkOrIP(ip_or_network=args.ip)
|
|
129
|
+
if ip.is_network():
|
|
130
|
+
raise InputFailure("IP cannot be a network")
|
|
131
|
+
if ip.as_ip().version != ipversion:
|
|
132
|
+
raise InputFailure(
|
|
133
|
+
f"IP version {ip.as_ip().version} does not match the requested version {ipversion}"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
host_ip = host.get_ip(ip.as_ip())
|
|
137
|
+
if not host_ip:
|
|
138
|
+
raise EntityNotFound(f"Host {host} does not have IP {ip}")
|
|
139
|
+
|
|
140
|
+
if host_ip.delete():
|
|
141
|
+
OutputManager().add_ok(f"Removed ipaddress {args.ip} from {host}")
|
|
142
|
+
else:
|
|
143
|
+
raise DeleteError(f"Failed to remove ipaddress {args.ip} from {host}")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _add_ip(
|
|
147
|
+
args: argparse.Namespace,
|
|
148
|
+
ipversion: IP_Version = 4,
|
|
149
|
+
) -> Host:
|
|
150
|
+
"""Add a new IP address to a host.
|
|
151
|
+
|
|
152
|
+
:param host: Name of the host to add the IP to.
|
|
153
|
+
:param ip: The IP address to add.
|
|
154
|
+
:param macaddress: The MAC address to add.
|
|
155
|
+
:param force: Whether to force the addition.
|
|
156
|
+
|
|
157
|
+
:return: The updated host object.
|
|
158
|
+
"""
|
|
159
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
160
|
+
ip_or_net = NetworkOrIP(ip_or_network=args.ip)
|
|
161
|
+
|
|
162
|
+
if ipversion == 4 and (ip_or_net.is_ipv6() or ip_or_net.is_ipv6_network()):
|
|
163
|
+
raise InputFailure("Use aaaa_add for IPv6 addresses")
|
|
164
|
+
elif ipversion == 6 and (ip_or_net.is_ipv4() or ip_or_net.is_ipv4_network()):
|
|
165
|
+
raise InputFailure("Use a_add for IPv4 addresses")
|
|
166
|
+
|
|
167
|
+
ipaddr = None
|
|
168
|
+
network = None
|
|
169
|
+
if ip_or_net.is_network():
|
|
170
|
+
network = Network.get_by_network_or_raise(str(ip_or_net.ip_or_network))
|
|
171
|
+
ip = network.get_first_available_ip()
|
|
172
|
+
ipaddr = ip
|
|
173
|
+
else:
|
|
174
|
+
network = Network.get_by_ip(ip_or_net.as_ip())
|
|
175
|
+
ip = ip_or_net
|
|
176
|
+
ipaddr = ip.as_ip()
|
|
177
|
+
|
|
178
|
+
if not args.force and network and network.frozen:
|
|
179
|
+
raise ForceMissing(f"Network {network.network} is frozen, must force")
|
|
180
|
+
|
|
181
|
+
if not args.force and host.has_ip(ipaddr):
|
|
182
|
+
raise EntityAlreadyExists(f"Host {host} already has IP {ipaddr}")
|
|
183
|
+
|
|
184
|
+
if not args.force and len(host.ipaddresses) > 0:
|
|
185
|
+
raise ForceMissing(f"Host {host} already has one or more ip addresses, must force")
|
|
186
|
+
|
|
187
|
+
mac = None
|
|
188
|
+
if args.macaddress:
|
|
189
|
+
try:
|
|
190
|
+
mac = MACAddressField(address=args.macaddress)
|
|
191
|
+
except ValueError as e:
|
|
192
|
+
raise InputFailure(f"Invalid MAC address: {mac}") from e
|
|
193
|
+
|
|
194
|
+
if not args.force:
|
|
195
|
+
_bail_if_ip_in_use_and_not_force(ipaddr)
|
|
196
|
+
|
|
197
|
+
host = host.add_ip(ipaddr, mac) # returns the refetched host
|
|
198
|
+
OutputManager().add_ok(f"Added ipaddress {ipaddr} to {host}")
|
|
199
|
+
return host
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@command_registry.register_command(
|
|
203
|
+
prog="a_add",
|
|
204
|
+
description="Add an A record to host. If NAME is an alias the cname host is used.",
|
|
205
|
+
short_desc="Add A record.",
|
|
206
|
+
flags=[
|
|
207
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
208
|
+
Flag(
|
|
209
|
+
"ip",
|
|
210
|
+
description=(
|
|
211
|
+
"The IP of new A record. May also be a network, "
|
|
212
|
+
"in which case a random IP address from that network "
|
|
213
|
+
"is chosen."
|
|
214
|
+
),
|
|
215
|
+
metavar="IP/network",
|
|
216
|
+
),
|
|
217
|
+
Flag("-macaddress", description="Mac address", metavar="MACADDRESS"),
|
|
218
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
219
|
+
],
|
|
220
|
+
)
|
|
221
|
+
def a_add(args: argparse.Namespace) -> None:
|
|
222
|
+
"""Add an A record to host. If <name> is an alias the cname host is used.
|
|
223
|
+
|
|
224
|
+
:param args: argparse.Namespace (name, ip, force, macaddress)
|
|
225
|
+
"""
|
|
226
|
+
_add_ip(args, 4)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@command_registry.register_command(
|
|
230
|
+
prog="a_change",
|
|
231
|
+
description=(
|
|
232
|
+
"Change an A record for the target host. If NAME is an alias the cname host is used."
|
|
233
|
+
),
|
|
234
|
+
short_desc="Change A record.",
|
|
235
|
+
flags=[
|
|
236
|
+
Flag(
|
|
237
|
+
"name",
|
|
238
|
+
description="Name of the target host.",
|
|
239
|
+
short_desc="Host name.",
|
|
240
|
+
metavar="NAME",
|
|
241
|
+
),
|
|
242
|
+
Flag(
|
|
243
|
+
"-old",
|
|
244
|
+
description="The existing IP that should be changed.",
|
|
245
|
+
short_desc="IP to change.",
|
|
246
|
+
required=True,
|
|
247
|
+
metavar="IP",
|
|
248
|
+
),
|
|
249
|
+
Flag(
|
|
250
|
+
"-new",
|
|
251
|
+
description=(
|
|
252
|
+
"The new IP address. May also be a network, in which "
|
|
253
|
+
"case a random IP from that network is chosen."
|
|
254
|
+
),
|
|
255
|
+
short_desc="New IP.",
|
|
256
|
+
required=True,
|
|
257
|
+
metavar="IP/network",
|
|
258
|
+
),
|
|
259
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
260
|
+
],
|
|
261
|
+
)
|
|
262
|
+
def a_change(args: argparse.Namespace) -> None:
|
|
263
|
+
"""Change A record. If <name> is an alias the cname host is used.
|
|
264
|
+
|
|
265
|
+
:param args: argparse.Namespace (name, old, new, force)
|
|
266
|
+
"""
|
|
267
|
+
_ip_change(args, 4)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@command_registry.register_command(
|
|
271
|
+
prog="a_move",
|
|
272
|
+
description="Move A record from a host to another host",
|
|
273
|
+
short_desc="Move A record",
|
|
274
|
+
flags=[
|
|
275
|
+
Flag("-ip", description="IP to move", required=True, metavar="IP"),
|
|
276
|
+
Flag(
|
|
277
|
+
"-fromhost",
|
|
278
|
+
description="Name of source host",
|
|
279
|
+
required=True,
|
|
280
|
+
metavar="NAME",
|
|
281
|
+
),
|
|
282
|
+
Flag(
|
|
283
|
+
"-tohost",
|
|
284
|
+
description="Name of destination host",
|
|
285
|
+
required=True,
|
|
286
|
+
metavar="NAME",
|
|
287
|
+
),
|
|
288
|
+
],
|
|
289
|
+
)
|
|
290
|
+
def a_move(args: argparse.Namespace) -> None:
|
|
291
|
+
"""Move an IP from a host to another host. Will move also move the PTR, if any.
|
|
292
|
+
|
|
293
|
+
:param args: argparse.Namespace (ip, fromhost, tohost)
|
|
294
|
+
"""
|
|
295
|
+
_ip_move(args, 4)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@command_registry.register_command(
|
|
299
|
+
prog="a_remove",
|
|
300
|
+
description="Remove an A record from the target host.",
|
|
301
|
+
short_desc="Remove A record.",
|
|
302
|
+
flags=[
|
|
303
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
304
|
+
Flag("ip", description="IP to remove.", metavar="IP"),
|
|
305
|
+
],
|
|
306
|
+
)
|
|
307
|
+
def a_remove(args: argparse.Namespace) -> None:
|
|
308
|
+
"""Remove A record from host. If <name> is an alias the cname host is used.
|
|
309
|
+
|
|
310
|
+
:param args: argparse.Namespace (name, ip)
|
|
311
|
+
"""
|
|
312
|
+
_ip_remove(args, 4)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@command_registry.register_command(
|
|
316
|
+
prog="a_show",
|
|
317
|
+
description="Show hosts ipaddresses. If NAME is an alias the cname host is used.",
|
|
318
|
+
short_desc="Show ipaddresses.",
|
|
319
|
+
flags=[
|
|
320
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
321
|
+
],
|
|
322
|
+
)
|
|
323
|
+
def a_show(args: argparse.Namespace) -> None:
|
|
324
|
+
"""Show hosts ipaddresses. If <name> is an alias the cname host is used.
|
|
325
|
+
|
|
326
|
+
:param args: argparse.Namespace (name)
|
|
327
|
+
"""
|
|
328
|
+
Host.get_by_any_means_or_raise(args.name).output_ipaddresses(only=4)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@command_registry.register_command(
|
|
332
|
+
prog="aaaa_add",
|
|
333
|
+
description="Add an AAAA record to host. If NAME is an alias the cname host is used.",
|
|
334
|
+
short_desc="Add AAAA record.",
|
|
335
|
+
flags=[
|
|
336
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
337
|
+
Flag(
|
|
338
|
+
"ip",
|
|
339
|
+
description="The IPv6 to add to the target host.",
|
|
340
|
+
metavar="IPv6",
|
|
341
|
+
),
|
|
342
|
+
Flag("-macaddress", description="Mac address", metavar="MACADDRESS"),
|
|
343
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
344
|
+
],
|
|
345
|
+
)
|
|
346
|
+
def aaaa_add(args: argparse.Namespace) -> None:
|
|
347
|
+
"""Add an AAAA record to host. If <name> is an alias the cname host is used.
|
|
348
|
+
|
|
349
|
+
:param args: argparse.Namespace (name, ip, force, macaddress)
|
|
350
|
+
"""
|
|
351
|
+
_add_ip(args, 6)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@command_registry.register_command(
|
|
355
|
+
prog="aaaa_change",
|
|
356
|
+
description="Change AAAA record. If NAME is an alias the cname host is used.",
|
|
357
|
+
short_desc="Change AAAA record.",
|
|
358
|
+
flags=[
|
|
359
|
+
Flag(
|
|
360
|
+
"name",
|
|
361
|
+
description="Name of the target host.",
|
|
362
|
+
short_desc="Host name.",
|
|
363
|
+
metavar="NAME",
|
|
364
|
+
),
|
|
365
|
+
Flag(
|
|
366
|
+
"-old",
|
|
367
|
+
description="The existing IPv6 that should be changed.",
|
|
368
|
+
short_desc="IPv6 to change.",
|
|
369
|
+
required=True,
|
|
370
|
+
metavar="IPv6",
|
|
371
|
+
),
|
|
372
|
+
Flag(
|
|
373
|
+
"-new",
|
|
374
|
+
description="The new IPv6 address.",
|
|
375
|
+
short_desc="New IPv6.",
|
|
376
|
+
required=True,
|
|
377
|
+
metavar="IPv6",
|
|
378
|
+
),
|
|
379
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
380
|
+
],
|
|
381
|
+
)
|
|
382
|
+
def aaaa_change(args: argparse.Namespace) -> None:
|
|
383
|
+
"""Change AAAA record. If <name> is an alias the cname host is used.
|
|
384
|
+
|
|
385
|
+
:param args: argparse.Namespace (name, old, new, force)
|
|
386
|
+
"""
|
|
387
|
+
_ip_change(args, 6)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@command_registry.register_command(
|
|
391
|
+
prog="aaaa_move",
|
|
392
|
+
description="Move AAAA record from a host to another host",
|
|
393
|
+
short_desc="Move AAAA record",
|
|
394
|
+
flags=[
|
|
395
|
+
Flag("-ip", description="IP to move", required=True, metavar="IP"),
|
|
396
|
+
Flag(
|
|
397
|
+
"-fromhost",
|
|
398
|
+
description="Name of source host",
|
|
399
|
+
required=True,
|
|
400
|
+
metavar="NAME",
|
|
401
|
+
),
|
|
402
|
+
Flag(
|
|
403
|
+
"-tohost",
|
|
404
|
+
description="Name of destination host",
|
|
405
|
+
required=True,
|
|
406
|
+
metavar="NAME",
|
|
407
|
+
),
|
|
408
|
+
],
|
|
409
|
+
)
|
|
410
|
+
def aaaa_move(args: argparse.Namespace) -> None:
|
|
411
|
+
"""Move an IP from a host to another host. Will move also move the PTR, if any.
|
|
412
|
+
|
|
413
|
+
:param args: argparse.Namespace (ip, fromhost, tohost)
|
|
414
|
+
"""
|
|
415
|
+
_ip_move(args, 6)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@command_registry.register_command(
|
|
419
|
+
prog="aaaa_remove",
|
|
420
|
+
description="Remove AAAA record from host. If NAME is an alias the cname host is used.",
|
|
421
|
+
short_desc="Remove AAAA record.",
|
|
422
|
+
flags=[
|
|
423
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
424
|
+
Flag("ip", description="IPv6 to remove.", metavar="IPv6"),
|
|
425
|
+
],
|
|
426
|
+
)
|
|
427
|
+
def aaaa_remove(args: argparse.Namespace) -> None:
|
|
428
|
+
"""Remove AAAA record from host.
|
|
429
|
+
|
|
430
|
+
If <name> is an alias the cname host is used.
|
|
431
|
+
|
|
432
|
+
:param args: argparse.Namespace (name, ip)
|
|
433
|
+
"""
|
|
434
|
+
_ip_remove(args, 6)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@command_registry.register_command(
|
|
438
|
+
prog="aaaa_show",
|
|
439
|
+
description="Show hosts AAAA records. If NAME is an alias the cname host is used.",
|
|
440
|
+
short_desc="Show AAAA records.",
|
|
441
|
+
flags=[
|
|
442
|
+
Flag("name", description="Name of the target host.", metavar="NAME"),
|
|
443
|
+
],
|
|
444
|
+
)
|
|
445
|
+
def aaaa_show(args: argparse.Namespace) -> None:
|
|
446
|
+
"""Show hosts ipaddresses.
|
|
447
|
+
|
|
448
|
+
If <name> is an alias the cname host is used.
|
|
449
|
+
|
|
450
|
+
:param args: argparse.Namespace (name)
|
|
451
|
+
"""
|
|
452
|
+
Host.get_by_any_means_or_raise(args.name).output_ipaddresses(only=6)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Backnet subcommands for the host command.
|
|
2
|
+
|
|
3
|
+
Commands implemented:
|
|
4
|
+
|
|
5
|
+
- bacnetid_add
|
|
6
|
+
- bacnetid_remove
|
|
7
|
+
- bacnetid_list
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
|
|
14
|
+
from mreg_cli.api.models import BacnetID, Host
|
|
15
|
+
from mreg_cli.commands.host import registry as command_registry
|
|
16
|
+
from mreg_cli.exceptions import (
|
|
17
|
+
CreateError,
|
|
18
|
+
DeleteError,
|
|
19
|
+
EntityAlreadyExists,
|
|
20
|
+
EntityNotFound,
|
|
21
|
+
EntityOwnershipMismatch,
|
|
22
|
+
InputFailure,
|
|
23
|
+
)
|
|
24
|
+
from mreg_cli.outputmanager import OutputManager
|
|
25
|
+
from mreg_cli.types import Flag
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@command_registry.register_command(
|
|
29
|
+
prog="bacnetid_add",
|
|
30
|
+
description="Assign a BACnet ID to the host.",
|
|
31
|
+
short_desc="Add BACnet ID",
|
|
32
|
+
flags=[
|
|
33
|
+
Flag("name", description="Name of host.", metavar="NAME"),
|
|
34
|
+
Flag("-id", description=f"ID value (0-{BacnetID.MAX_ID()})", metavar="ID"),
|
|
35
|
+
],
|
|
36
|
+
)
|
|
37
|
+
def bacnetid_add(args: argparse.Namespace) -> None:
|
|
38
|
+
"""Assign a BACnet ID to the host.
|
|
39
|
+
|
|
40
|
+
:param args: argparse.Namespace (name, id)
|
|
41
|
+
"""
|
|
42
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
43
|
+
host_bacnet = host.bacnet()
|
|
44
|
+
if host_bacnet is not None:
|
|
45
|
+
raise EntityAlreadyExists(f"{host.name} already has BACnet ID {host_bacnet.id}.")
|
|
46
|
+
|
|
47
|
+
existing = BacnetID.get(args.id)
|
|
48
|
+
if existing:
|
|
49
|
+
raise EntityOwnershipMismatch(
|
|
50
|
+
f"BACnet ID {existing.id} is already in use by {existing.hostname}."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
BacnetID.create(params={"hostname": host.name.hostname, "id": args.id})
|
|
54
|
+
|
|
55
|
+
validator = BacnetID.get(args.id)
|
|
56
|
+
if validator and validator.hostname == host.name.hostname:
|
|
57
|
+
OutputManager().add_ok(f"Assigned BACnet ID {validator.id} to {validator.hostname}.")
|
|
58
|
+
else:
|
|
59
|
+
raise CreateError(f"Failed to assign BACnet ID {args.id} to {host.name.hostname}.")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@command_registry.register_command(
|
|
63
|
+
prog="bacnetid_remove",
|
|
64
|
+
description="Unassign the BACnet ID from the host.",
|
|
65
|
+
short_desc="Remove BACnet ID",
|
|
66
|
+
flags=[
|
|
67
|
+
Flag("name", description="Name of host.", metavar="NAME"),
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
def bacnetid_remove(args: argparse.Namespace) -> None:
|
|
71
|
+
"""Unassign the BACnet ID from the host.
|
|
72
|
+
|
|
73
|
+
:param args: argparse.Namespace (name)
|
|
74
|
+
"""
|
|
75
|
+
host = Host.get_by_any_means_or_raise(args.name)
|
|
76
|
+
host_bacnet = host.bacnet()
|
|
77
|
+
if host_bacnet is None:
|
|
78
|
+
raise EntityNotFound(f"{host.name} does not have a BACnet ID assigned.")
|
|
79
|
+
|
|
80
|
+
if host_bacnet.delete():
|
|
81
|
+
OutputManager().add_ok(f"Unassigned BACnet ID {host_bacnet.id} from {host.name}.")
|
|
82
|
+
else:
|
|
83
|
+
raise DeleteError(f"Failed to unassign BACnet ID {host_bacnet.id} from {host.name}.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@command_registry.register_command(
|
|
87
|
+
prog="bacnetid_list",
|
|
88
|
+
description="Find/list BACnet IDs and hostnames by ID.",
|
|
89
|
+
short_desc="List used BACnet IDs",
|
|
90
|
+
flags=[
|
|
91
|
+
Flag(
|
|
92
|
+
"-min",
|
|
93
|
+
description=f"Minimum ID value (0-{BacnetID.MAX_ID()})",
|
|
94
|
+
flag_type=int,
|
|
95
|
+
metavar="MIN",
|
|
96
|
+
),
|
|
97
|
+
Flag(
|
|
98
|
+
"-max",
|
|
99
|
+
description=f"Maximum ID value (0-{BacnetID.MAX_ID()})",
|
|
100
|
+
flag_type=int,
|
|
101
|
+
metavar="MAX",
|
|
102
|
+
),
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
def bacnetid_list(args: argparse.Namespace) -> None:
|
|
106
|
+
"""Find/list BACnet IDs and hostnames by ID.
|
|
107
|
+
|
|
108
|
+
:param args: argparse.Namespace (min, max)
|
|
109
|
+
"""
|
|
110
|
+
min_id = args.min if args.min is not None else 0
|
|
111
|
+
max_id = args.max if args.max is not None else BacnetID.MAX_ID()
|
|
112
|
+
|
|
113
|
+
if min_id < 0:
|
|
114
|
+
raise InputFailure("Minimum ID value cannot be less than 0.")
|
|
115
|
+
|
|
116
|
+
if min_id is not None and max_id is not None and min_id > max_id:
|
|
117
|
+
raise InputFailure("Minimum ID value cannot be greater than maximum ID value.")
|
|
118
|
+
|
|
119
|
+
if max_id is not None and max_id > BacnetID.MAX_ID():
|
|
120
|
+
raise InputFailure(f"The maximum ID value is {BacnetID.MAX_ID()}.")
|
|
121
|
+
|
|
122
|
+
bacnetids = BacnetID.get_in_range(min_id, max_id)
|
|
123
|
+
if not bacnetids:
|
|
124
|
+
raise EntityNotFound("No BACnet IDs found in the specified range.")
|
|
125
|
+
|
|
126
|
+
BacnetID.output_multiple(bacnetids)
|