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,288 @@
|
|
|
1
|
+
"""Zone commands for mreg_cli."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from mreg_cli.api.models import Zone
|
|
9
|
+
from mreg_cli.commands.base import BaseCommand
|
|
10
|
+
from mreg_cli.commands.registry import CommandRegistry
|
|
11
|
+
from mreg_cli.exceptions import DeleteError, InputFailure
|
|
12
|
+
from mreg_cli.outputmanager import OutputManager
|
|
13
|
+
from mreg_cli.types import Flag
|
|
14
|
+
|
|
15
|
+
command_registry = CommandRegistry()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ZoneCommands(BaseCommand):
|
|
19
|
+
"""Zone commands for the CLI."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, cli: Any) -> None:
|
|
22
|
+
"""Initialize the zone commands."""
|
|
23
|
+
super().__init__(cli, command_registry, "zone", "Manage zones.", "Manage zones")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@command_registry.register_command(
|
|
27
|
+
prog="create",
|
|
28
|
+
description="Create new zone.",
|
|
29
|
+
short_desc="Create new zone.",
|
|
30
|
+
flags=[
|
|
31
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
32
|
+
Flag("email", description="Contact email.", metavar="EMAIL"),
|
|
33
|
+
Flag("ns", description="Nameservers of the zone.", nargs="+", metavar="NS"),
|
|
34
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
35
|
+
],
|
|
36
|
+
)
|
|
37
|
+
def create(args: argparse.Namespace) -> None:
|
|
38
|
+
"""Create a new zone.
|
|
39
|
+
|
|
40
|
+
:param args: argparse.Namespace (ns, force, zone, email)
|
|
41
|
+
"""
|
|
42
|
+
Zone.create_zone(args.zone, args.email, args.ns, args.force)
|
|
43
|
+
OutputManager().add_ok(f"Created zone {args.zone}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@command_registry.register_command(
|
|
47
|
+
prog="delegation_create",
|
|
48
|
+
description=(
|
|
49
|
+
"Create new zone delegation. Requires force if ns or zone doesn't exist in MREG."
|
|
50
|
+
),
|
|
51
|
+
short_desc="Create new zone delegation.",
|
|
52
|
+
flags=[
|
|
53
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
54
|
+
Flag("delegation", description="Delegation", metavar="DELEGATION"),
|
|
55
|
+
Flag("ns", description="Nameservers for the delegation.", nargs="+", metavar="NS"),
|
|
56
|
+
Flag("-comment", description="Comment with a description", metavar="COMMENT"),
|
|
57
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
58
|
+
],
|
|
59
|
+
)
|
|
60
|
+
def delegation_create(args: argparse.Namespace) -> None:
|
|
61
|
+
"""Create a new zone delegation.
|
|
62
|
+
|
|
63
|
+
:param args: argparse.Namespace (ns, force, zone, delegation, comment)
|
|
64
|
+
"""
|
|
65
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
66
|
+
zone.create_delegation(args.delegation, args.ns, args.comment, args.force)
|
|
67
|
+
OutputManager().add_ok(f"Created zone delegation {args.delegation}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@command_registry.register_command(
|
|
71
|
+
prog="delete",
|
|
72
|
+
description="Delete a zone",
|
|
73
|
+
short_desc="Delete a zone",
|
|
74
|
+
flags=[
|
|
75
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
76
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
def zone_delete(args: argparse.Namespace) -> None:
|
|
80
|
+
"""Delete a zone.
|
|
81
|
+
|
|
82
|
+
:param args: argparse.Namespace (zone, force)
|
|
83
|
+
"""
|
|
84
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
85
|
+
if zone.delete_zone(args.force):
|
|
86
|
+
OutputManager().add_ok(f"Deleted zone {zone.name}")
|
|
87
|
+
else:
|
|
88
|
+
raise DeleteError(f"Unable to delete zone {zone.name}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@command_registry.register_command(
|
|
92
|
+
prog="delegation_delete",
|
|
93
|
+
description="Delete a zone delegation",
|
|
94
|
+
short_desc="Delete a zone delegation",
|
|
95
|
+
flags=[
|
|
96
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
97
|
+
Flag("delegation", description="Delegation", metavar="DELEGATION"),
|
|
98
|
+
],
|
|
99
|
+
)
|
|
100
|
+
def delegation_delete(args: argparse.Namespace) -> None:
|
|
101
|
+
"""Delete a zone delegation.
|
|
102
|
+
|
|
103
|
+
:param args: argparse.Namespace (zone, delegation)
|
|
104
|
+
"""
|
|
105
|
+
# NOTE: how to handle delegation that has delegation?
|
|
106
|
+
# i.e. uio.no -> pederhan.uio.no -> sub.pederhan.uio.no
|
|
107
|
+
# and we try to delete pederhan.uio.no?
|
|
108
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
109
|
+
if zone.delete_delegation(args.delegation):
|
|
110
|
+
OutputManager().add_ok(f"Removed zone delegation {args.delegation}")
|
|
111
|
+
else:
|
|
112
|
+
raise DeleteError(f"Unable to delete delegation {args.delegation}")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@command_registry.register_command(
|
|
116
|
+
prog="info",
|
|
117
|
+
description="Delete a zone",
|
|
118
|
+
short_desc="Delete a zone",
|
|
119
|
+
flags=[
|
|
120
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
121
|
+
],
|
|
122
|
+
)
|
|
123
|
+
def info(args: argparse.Namespace) -> None:
|
|
124
|
+
"""Show SOA info for a existing zone.
|
|
125
|
+
|
|
126
|
+
:param args: argparse.Namespace (zone)
|
|
127
|
+
"""
|
|
128
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
129
|
+
zone.output()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@command_registry.register_command(
|
|
133
|
+
prog="list",
|
|
134
|
+
description="List zones",
|
|
135
|
+
short_desc="List zones",
|
|
136
|
+
flags=[
|
|
137
|
+
Flag(
|
|
138
|
+
"-forward",
|
|
139
|
+
action="store_true",
|
|
140
|
+
short_desc="List all forward zones",
|
|
141
|
+
description="List all forward zones",
|
|
142
|
+
),
|
|
143
|
+
Flag(
|
|
144
|
+
"-reverse",
|
|
145
|
+
action="store_true",
|
|
146
|
+
short_desc="List all reverse zones",
|
|
147
|
+
description="List all reverse zones",
|
|
148
|
+
),
|
|
149
|
+
],
|
|
150
|
+
)
|
|
151
|
+
def zone_list(args: argparse.Namespace) -> None:
|
|
152
|
+
"""List all zones.
|
|
153
|
+
|
|
154
|
+
:param args: argparse.Namespace (forward, reverse)
|
|
155
|
+
"""
|
|
156
|
+
if not (args.forward or args.reverse):
|
|
157
|
+
raise InputFailure("Add either -forward or -reverse as argument")
|
|
158
|
+
Zone.output_zones(args.forward, args.reverse)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@command_registry.register_command(
|
|
162
|
+
prog="delegation_list",
|
|
163
|
+
description="List a zone's delegations",
|
|
164
|
+
short_desc="List a zone's delegations",
|
|
165
|
+
flags=[
|
|
166
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
167
|
+
],
|
|
168
|
+
)
|
|
169
|
+
def zone_delegation_list(args: argparse.Namespace) -> None:
|
|
170
|
+
"""List a zone's delegations.
|
|
171
|
+
|
|
172
|
+
:param args: argparse.Namespace (zone)
|
|
173
|
+
"""
|
|
174
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
175
|
+
zone.output_delegations()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@command_registry.register_command(
|
|
179
|
+
prog="delegation_comment_set",
|
|
180
|
+
description="Set a comment for zone delegation",
|
|
181
|
+
short_desc="Set a comment for zone delegation",
|
|
182
|
+
flags=[
|
|
183
|
+
Flag("zone", description="Zone name", metavar="ZONE"),
|
|
184
|
+
Flag("delegation", description="Delegation", metavar="DELEGATION"),
|
|
185
|
+
Flag("comment", description="Comment", metavar="COMMENT"),
|
|
186
|
+
],
|
|
187
|
+
)
|
|
188
|
+
def zone_delegation_comment_set(args: argparse.Namespace) -> None:
|
|
189
|
+
"""Set a delegation's comment.
|
|
190
|
+
|
|
191
|
+
:param args: argparse.Namespace (zone, delegation, comment)
|
|
192
|
+
"""
|
|
193
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
194
|
+
zone.set_delegation_comment(args.delegation, args.comment)
|
|
195
|
+
OutputManager().add_ok(f"Updated comment for {args.delegation}")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@command_registry.register_command(
|
|
199
|
+
prog="delegation_comment_remove",
|
|
200
|
+
description="Remove a comment for zone delegation",
|
|
201
|
+
short_desc="Remove a comment for zone delegation",
|
|
202
|
+
flags=[
|
|
203
|
+
Flag("zone", description="Zone name", metavar="ZONE"),
|
|
204
|
+
Flag("delegation", description="Delegation", metavar="DELEGATION"),
|
|
205
|
+
],
|
|
206
|
+
)
|
|
207
|
+
def zone_delegation_comment_remove(args: argparse.Namespace) -> None:
|
|
208
|
+
"""Set a delegation's comment.
|
|
209
|
+
|
|
210
|
+
:param args: argparse.Namespace (zone, delegation)
|
|
211
|
+
"""
|
|
212
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
213
|
+
zone.set_delegation_comment(args.delegation, "")
|
|
214
|
+
OutputManager().add_ok(f"Removed comment for {args.delegation}")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@command_registry.register_command(
|
|
218
|
+
prog="set_ns",
|
|
219
|
+
description="Update nameservers for an existing zone.",
|
|
220
|
+
short_desc="Update nameservers for an existing zone.",
|
|
221
|
+
flags=[
|
|
222
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
223
|
+
Flag("ns", description="Nameservers of the zone.", nargs="+", metavar="NS"),
|
|
224
|
+
Flag("-force", action="store_true", description="Enable force."),
|
|
225
|
+
],
|
|
226
|
+
)
|
|
227
|
+
def set_ns(args: argparse.Namespace) -> None:
|
|
228
|
+
"""Update nameservers for an existing zone.
|
|
229
|
+
|
|
230
|
+
If multiple nameservers are provided, the first one is set as the primary nameserver.
|
|
231
|
+
|
|
232
|
+
:param args: argparse.Namespace (zone, ns, force)
|
|
233
|
+
"""
|
|
234
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
235
|
+
zone.update_nameservers(args.ns, args.force)
|
|
236
|
+
OutputManager().add_ok(f"Updated nameservers for {args.zone}")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@command_registry.register_command(
|
|
240
|
+
prog="set_soa",
|
|
241
|
+
description="Updated the SOA of a zone.",
|
|
242
|
+
short_desc="Updated the SOA of a zone.",
|
|
243
|
+
flags=[
|
|
244
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
245
|
+
Flag("-ns", description="Primary nameserver (SOA MNAME).", metavar="PRIMARY-NS"),
|
|
246
|
+
Flag("-email", description="Zone contact email.", metavar="EMAIL"),
|
|
247
|
+
Flag("-serialno", description="Serial number.", flag_type=int, metavar="SERIALNO"),
|
|
248
|
+
Flag("-refresh", description="Refresh time.", flag_type=int, metavar="REFRESH"),
|
|
249
|
+
Flag("-retry", description="Retry time.", flag_type=int, metavar="RETRY"),
|
|
250
|
+
Flag("-expire", description="Expire time.", flag_type=int, metavar="EXPIRE"),
|
|
251
|
+
Flag("-soa-ttl", description="SOA Time To Live", flag_type=int, metavar="TTL"),
|
|
252
|
+
],
|
|
253
|
+
)
|
|
254
|
+
def set_soa(args: argparse.Namespace) -> None:
|
|
255
|
+
"""Update the SOA of a zone.
|
|
256
|
+
|
|
257
|
+
:param args: argparse.Namespace (zone, ns, email, serialno, retry, expire, soa_ttl)
|
|
258
|
+
"""
|
|
259
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
260
|
+
zone.update_soa(
|
|
261
|
+
primary_ns=args.ns,
|
|
262
|
+
email=args.email,
|
|
263
|
+
serialno=args.serialno,
|
|
264
|
+
refresh=args.refresh,
|
|
265
|
+
retry=args.retry,
|
|
266
|
+
expire=args.expire,
|
|
267
|
+
soa_ttl=args.soa_ttl,
|
|
268
|
+
)
|
|
269
|
+
OutputManager().add_ok(f"Updated SOA for {args.zone}")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@command_registry.register_command(
|
|
273
|
+
prog="set_default_ttl",
|
|
274
|
+
description="Set the default TTL of a zone.",
|
|
275
|
+
short_desc="Set the default TTL of a zone.",
|
|
276
|
+
flags=[
|
|
277
|
+
Flag("zone", description="Zone name.", metavar="ZONE"),
|
|
278
|
+
Flag("ttl", description="Default Time To Live.", flag_type=int, metavar="TTL"),
|
|
279
|
+
],
|
|
280
|
+
)
|
|
281
|
+
def set_default_ttl(args: argparse.Namespace) -> None:
|
|
282
|
+
"""Update the default TTL of a zone.
|
|
283
|
+
|
|
284
|
+
:param args: argparse.Namespace (zone, ttl)
|
|
285
|
+
"""
|
|
286
|
+
zone = Zone.get_zone_or_raise(args.zone)
|
|
287
|
+
zone.set_default_ttl(args.ttl)
|
|
288
|
+
OutputManager().add_ok(f"Set default TTL for {args.zone}")
|
mreg_cli/config.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""Logging.
|
|
2
|
+
|
|
3
|
+
This module can be used to configure basic logging to stderr, with an optional
|
|
4
|
+
filter level.
|
|
5
|
+
|
|
6
|
+
When prompting for user input (verbosity, debug level), log levels can be
|
|
7
|
+
translated according to a mapping:
|
|
8
|
+
|
|
9
|
+
.. py:data:: LOGGING_VERBOSITY
|
|
10
|
+
|
|
11
|
+
0. :py:const:`logging.ERROR`
|
|
12
|
+
1. :py:const:`logging.WARNING`
|
|
13
|
+
2. :py:const:`logging.INFO`
|
|
14
|
+
3. :py:const:`logging.DEBUG`
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import configparser
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from typing import Any, overload
|
|
24
|
+
|
|
25
|
+
from mreg_cli.types import DefaultType, LogLevel
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# Config file locations
|
|
30
|
+
DEFAULT_CONFIG_PATH = tuple(
|
|
31
|
+
(
|
|
32
|
+
os.path.expanduser("~/.config/mreg-cli.conf"),
|
|
33
|
+
"/etc/mreg-cli.conf",
|
|
34
|
+
os.path.join(sys.prefix, "local", "share", "mreg-cli.conf"),
|
|
35
|
+
os.path.join(sys.prefix, "share", "mreg-cli.conf"),
|
|
36
|
+
# At last, look in ../data/ in case we're developing
|
|
37
|
+
os.path.join(os.path.dirname(__file__), "..", "data", "mreg-cli.conf"),
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
DEFAULT_LOG_FILE = os.path.expanduser("~/.mreg-cli.log")
|
|
42
|
+
|
|
43
|
+
# Default logging format
|
|
44
|
+
LOGGING_FORMAT = "%(asctime)s - %(levelname)-8s - %(name)s - %(message)s"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MregCliConfig:
|
|
48
|
+
"""Configuration class for the mreg-cli.
|
|
49
|
+
|
|
50
|
+
This is a singleton class that is used to store configuration information.
|
|
51
|
+
Configuration is loaded with the following priority:
|
|
52
|
+
1. Command line options
|
|
53
|
+
2. Environment variables (prefixed with 'MREG_')
|
|
54
|
+
3. Configuration file
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
_instance = None
|
|
58
|
+
_is_logging = False
|
|
59
|
+
_logfile = None
|
|
60
|
+
_config_cmd: dict[str, str]
|
|
61
|
+
_config_file: dict[str, str]
|
|
62
|
+
_config_env: dict[str, str]
|
|
63
|
+
|
|
64
|
+
def __new__(cls) -> MregCliConfig:
|
|
65
|
+
"""Create a new instance of the configuration class.
|
|
66
|
+
|
|
67
|
+
This ensures that only one instance of the configuration class is created.
|
|
68
|
+
"""
|
|
69
|
+
if cls._instance is None:
|
|
70
|
+
cls._instance = super().__new__(cls)
|
|
71
|
+
cls._instance._config_file = {}
|
|
72
|
+
cls._instance._config_env = cls._load_env_config()
|
|
73
|
+
cls._instance._config_cmd = {}
|
|
74
|
+
cls._instance.get_config()
|
|
75
|
+
|
|
76
|
+
return cls._instance
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _load_env_config() -> dict[str, str]:
|
|
80
|
+
"""Load environment variables into the configuration, filtering with 'MREGCLI_' prefix."""
|
|
81
|
+
env_prefix = "MREGCLI_"
|
|
82
|
+
return {
|
|
83
|
+
key[len(env_prefix) :].lower(): value
|
|
84
|
+
for key, value in os.environ.items()
|
|
85
|
+
if key.startswith(env_prefix)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@overload
|
|
89
|
+
def get(self, key: str) -> str | None: ...
|
|
90
|
+
|
|
91
|
+
@overload
|
|
92
|
+
def get(self, key: str, default: DefaultType = ...) -> str | DefaultType: ...
|
|
93
|
+
|
|
94
|
+
def get(self, key: str, default: DefaultType | None = None) -> str | DefaultType | None:
|
|
95
|
+
"""Get a configuration value with priority: cmdline, env, file.
|
|
96
|
+
|
|
97
|
+
:param str key: Configuration key.
|
|
98
|
+
:param default: Default value if key is not found.
|
|
99
|
+
|
|
100
|
+
:returns: Configuration value.
|
|
101
|
+
"""
|
|
102
|
+
return self._config_cmd.get(
|
|
103
|
+
key, self._config_env.get(key, self._config_file.get(key, default))
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def set_cmd_config(self, cmd_config: dict[str, Any]) -> None:
|
|
107
|
+
"""Set command line configuration options.
|
|
108
|
+
|
|
109
|
+
:param Dict[str, Any] cmd_config: Dictionary of command line configurations.
|
|
110
|
+
"""
|
|
111
|
+
self._config_cmd.update(cmd_config)
|
|
112
|
+
|
|
113
|
+
def get_config(self, reload: bool = False) -> None:
|
|
114
|
+
"""Load the configuration file into the class.
|
|
115
|
+
|
|
116
|
+
:param bool reload: Reload the configuration from the config file.
|
|
117
|
+
"""
|
|
118
|
+
if not self._config_file or reload:
|
|
119
|
+
configpath = self.get_config_file()
|
|
120
|
+
if configpath is not None:
|
|
121
|
+
cfgparser = configparser.ConfigParser()
|
|
122
|
+
cfgparser.read(configpath)
|
|
123
|
+
self._config_file = dict(cfgparser["mreg"].items())
|
|
124
|
+
|
|
125
|
+
def get_logging_level(self) -> str:
|
|
126
|
+
"""Get the current logging level by name.
|
|
127
|
+
|
|
128
|
+
:returns: The logging level by name.
|
|
129
|
+
"""
|
|
130
|
+
return logging.getLevelName(logging.getLogger().getEffectiveLevel())
|
|
131
|
+
|
|
132
|
+
def set_logging_level(self, level: LogLevel) -> None:
|
|
133
|
+
"""Set the logging level.
|
|
134
|
+
|
|
135
|
+
:param str level: The logging level to set (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
136
|
+
"""
|
|
137
|
+
logging.getLogger().setLevel(level)
|
|
138
|
+
|
|
139
|
+
def start_logging(
|
|
140
|
+
self, logfile: str = DEFAULT_LOG_FILE, level: str | LogLevel = "INFO"
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Enable and configure logging.
|
|
143
|
+
|
|
144
|
+
:param str logfile: Path to the logfile, defaults to DEFAULT_LOG_FILE.
|
|
145
|
+
:param str level: Logging level, defaults to 'INFO'.
|
|
146
|
+
"""
|
|
147
|
+
level = LogLevel(level)
|
|
148
|
+
if self._is_logging:
|
|
149
|
+
logging.shutdown()
|
|
150
|
+
self._is_logging = False
|
|
151
|
+
else:
|
|
152
|
+
logging.basicConfig(
|
|
153
|
+
filename=logfile,
|
|
154
|
+
level=logging.getLevelName(level),
|
|
155
|
+
format=LOGGING_FORMAT,
|
|
156
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
logging.getLogger().setLevel(level)
|
|
160
|
+
self._is_logging = True
|
|
161
|
+
self._logfile = logfile
|
|
162
|
+
|
|
163
|
+
def get_logging_status(self) -> bool:
|
|
164
|
+
"""Get the logging status.
|
|
165
|
+
|
|
166
|
+
:returns: True if logging is enabled, False otherwise.
|
|
167
|
+
"""
|
|
168
|
+
return self._is_logging
|
|
169
|
+
|
|
170
|
+
def print_logging_status(self) -> None:
|
|
171
|
+
"""Print the logging status."""
|
|
172
|
+
if self._is_logging:
|
|
173
|
+
print(f"Logging enabled to {self._logfile}")
|
|
174
|
+
else:
|
|
175
|
+
print("Logging is not enabled")
|
|
176
|
+
|
|
177
|
+
def stop_logging(self) -> None:
|
|
178
|
+
"""Stop logging."""
|
|
179
|
+
logging.shutdown()
|
|
180
|
+
self._is_logging = False
|
|
181
|
+
self._logfile = None
|
|
182
|
+
|
|
183
|
+
def get_config_file(self) -> str | None:
|
|
184
|
+
"""Get the first config file found in DEFAULT_CONFIG_PATH.
|
|
185
|
+
|
|
186
|
+
:returns: path to config file, or None if no config file was found
|
|
187
|
+
"""
|
|
188
|
+
for path in DEFAULT_CONFIG_PATH:
|
|
189
|
+
logger.debug("looking for config in %r", os.path.abspath(path))
|
|
190
|
+
if os.path.isfile(path):
|
|
191
|
+
logger.info("found config in %r", path)
|
|
192
|
+
return path
|
|
193
|
+
logger.debug("no config file found in config paths")
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
def get_default_domain(self):
|
|
197
|
+
"""Get the default domain from the application."""
|
|
198
|
+
return self.get("domain", "uio.no")
|
|
199
|
+
|
|
200
|
+
def get_default_logfile(self):
|
|
201
|
+
"""Get the default logfile from the application."""
|
|
202
|
+
return self.get("logfile", DEFAULT_LOG_FILE)
|
|
203
|
+
|
|
204
|
+
def get_location_tags(self) -> list[str]:
|
|
205
|
+
"""Get the location tags from the application."""
|
|
206
|
+
return self.get("location_tags", "").split(",")
|
|
207
|
+
|
|
208
|
+
def get_category_tags(self) -> list[str]:
|
|
209
|
+
"""Get the category tags from the application."""
|
|
210
|
+
return self.get("category_tags", "").split(",")
|
|
211
|
+
|
|
212
|
+
# We handle url by itself because it's a required config option,
|
|
213
|
+
# it cannot be none once options, env, and config file are parsed.
|
|
214
|
+
def get_url(self) -> str:
|
|
215
|
+
"""Get the default url from the application."""
|
|
216
|
+
url = self.get("url", self.get("default_url", "https://mreg.uio.no"))
|
|
217
|
+
if not url:
|
|
218
|
+
raise ValueError("No URL found in config, no defaults available!")
|
|
219
|
+
return url
|
|
220
|
+
|
|
221
|
+
def _calculate_column_width(self, data: dict[str, Any], min_width: int = 8) -> int:
|
|
222
|
+
"""Calculate the maximum column width, ensuring a minimum width.
|
|
223
|
+
|
|
224
|
+
:param data: Dictionary of data for the column.
|
|
225
|
+
:param min_width: Minimum width of the column.
|
|
226
|
+
:returns: Calculated column width.
|
|
227
|
+
"""
|
|
228
|
+
max_length = max((len(str(value)) for value in data.values()), default=0)
|
|
229
|
+
return max(max_length, min_width) + 2 # Adding 2 for padding
|
|
230
|
+
|
|
231
|
+
def print_config_table(self) -> None:
|
|
232
|
+
"""Pretty print the configuration options in a dynamic table format."""
|
|
233
|
+
all_keys = set(self._config_cmd) | set(self._config_env) | set(self._config_file)
|
|
234
|
+
key_width = max(max(len(key) for key in all_keys), 8) + 2
|
|
235
|
+
|
|
236
|
+
# Calculate column widths
|
|
237
|
+
cmd_width = self._calculate_column_width(self._config_cmd)
|
|
238
|
+
env_width = self._calculate_column_width(self._config_env)
|
|
239
|
+
file_width = self._calculate_column_width(self._config_file)
|
|
240
|
+
|
|
241
|
+
# Print the table header
|
|
242
|
+
print("Configuration Options:")
|
|
243
|
+
header_format = f"{{:<{key_width}}} {{:<{cmd_width}}} {{:<{env_width}}} {{:<{file_width}}}"
|
|
244
|
+
print(header_format.format("Key", "Active", "Envir", "File"))
|
|
245
|
+
print("-" * (key_width + cmd_width + env_width + file_width))
|
|
246
|
+
|
|
247
|
+
# Print each row
|
|
248
|
+
row_format = f"{{:<{key_width}}} {{:<{cmd_width}}} {{:<{env_width}}} {{:<{file_width}}}"
|
|
249
|
+
for key in sorted(all_keys):
|
|
250
|
+
cmd_line_val = str(self._config_cmd.get(key, "-"))
|
|
251
|
+
env_var_val = str(self._config_env.get(key, "-"))
|
|
252
|
+
config_file_val = str(self._config_file.get(key, "-"))
|
|
253
|
+
print(row_format.format(key, cmd_line_val, env_var_val, config_file_val))
|