fb-pdnstools 1.0.0__py3-none-any.whl → 1.1.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.
- fb_pdnstools/__init__.py +76 -34
- fb_pdnstools/base_handler.py +119 -102
- fb_pdnstools/bulk_rm_app.py +242 -152
- fb_pdnstools/bulk_rm_cfg.py +33 -19
- fb_pdnstools/common.py +13 -13
- fb_pdnstools/errors.py +15 -15
- fb_pdnstools/record.py +407 -241
- fb_pdnstools/server.py +81 -44
- fb_pdnstools/xlate.py +57 -36
- fb_pdnstools/zone.py +600 -369
- fb_pdnstools-1.1.0.data/data/share/locale/de/LC_MESSAGES/fb_pdnstools.mo +0 -0
- fb_pdnstools-1.1.0.data/data/share/locale/en/LC_MESSAGES/fb_pdnstools.mo +0 -0
- fb_pdnstools-1.1.0.dist-info/METADATA +53 -0
- fb_pdnstools-1.1.0.dist-info/RECORD +17 -0
- {fb_pdnstools-1.0.0.dist-info → fb_pdnstools-1.1.0.dist-info}/WHEEL +1 -2
- fb_pdnstools-1.1.0.dist-info/entry_points.txt +3 -0
- fb_pdnstools/local_version.py +0 -17
- fb_pdnstools-1.0.0.data/scripts/pdns-bulk-remove +0 -68
- fb_pdnstools-1.0.0.dist-info/METADATA +0 -40
- fb_pdnstools-1.0.0.dist-info/RECORD +0 -17
- fb_pdnstools-1.0.0.dist-info/top_level.txt +0 -1
- {fb_pdnstools-1.0.0.dist-info → fb_pdnstools-1.1.0.dist-info/licenses}/LICENSE +0 -0
fb_pdnstools/server.py
CHANGED
|
@@ -26,7 +26,7 @@ from .errors import PDNSApiNotFoundError, PDNSApiValidationError
|
|
|
26
26
|
from .xlate import XLATOR
|
|
27
27
|
from .zone import PowerDNSZone, PowerDNSZoneDict
|
|
28
28
|
|
|
29
|
-
__version__ =
|
|
29
|
+
__version__ = "1.0.0"
|
|
30
30
|
LOG = logging.getLogger(__name__)
|
|
31
31
|
|
|
32
32
|
_ = XLATOR.gettext
|
|
@@ -39,20 +39,40 @@ class PowerDNSServer(BasePowerDNSHandler):
|
|
|
39
39
|
|
|
40
40
|
# -------------------------------------------------------------------------
|
|
41
41
|
def __init__(
|
|
42
|
-
self,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
self,
|
|
43
|
+
appname=None,
|
|
44
|
+
verbose=0,
|
|
45
|
+
version=__version__,
|
|
46
|
+
base_dir=None,
|
|
47
|
+
master_server=None,
|
|
48
|
+
port=DEFAULT_PORT,
|
|
49
|
+
key=None,
|
|
50
|
+
use_https=False,
|
|
51
|
+
path_prefix=DEFAULT_API_PREFIX,
|
|
52
|
+
simulate=None,
|
|
53
|
+
force=None,
|
|
54
|
+
terminal_has_colors=False,
|
|
55
|
+
initialized=False,
|
|
56
|
+
):
|
|
46
57
|
"""Initialize a PowerDNSServer record."""
|
|
47
58
|
self._api_servername = self.default_api_servername
|
|
48
|
-
self._api_server_version =
|
|
59
|
+
self._api_server_version = "unknown"
|
|
49
60
|
self.zones = None
|
|
50
61
|
|
|
51
62
|
super(PowerDNSServer, self).__init__(
|
|
52
|
-
appname=appname,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
appname=appname,
|
|
64
|
+
verbose=verbose,
|
|
65
|
+
version=version,
|
|
66
|
+
base_dir=base_dir,
|
|
67
|
+
master_server=master_server,
|
|
68
|
+
port=port,
|
|
69
|
+
key=key,
|
|
70
|
+
use_https=use_https,
|
|
71
|
+
path_prefix=path_prefix,
|
|
72
|
+
simulate=simulate,
|
|
73
|
+
force=force,
|
|
74
|
+
terminal_has_colors=terminal_has_colors,
|
|
75
|
+
initialized=False,
|
|
56
76
|
)
|
|
57
77
|
|
|
58
78
|
self.initialized = initialized
|
|
@@ -70,8 +90,9 @@ class PowerDNSServer(BasePowerDNSHandler):
|
|
|
70
90
|
self._simulate = to_bool(value)
|
|
71
91
|
|
|
72
92
|
if self.initialized:
|
|
73
|
-
LOG.debug(
|
|
74
|
-
self.simulate)
|
|
93
|
+
LOG.debug(
|
|
94
|
+
_("Setting simulate of all subsequent objects to {!r} ...").format(self.simulate)
|
|
95
|
+
)
|
|
75
96
|
|
|
76
97
|
if self.zones:
|
|
77
98
|
for zone_name in self.zones:
|
|
@@ -89,60 +110,70 @@ class PowerDNSServer(BasePowerDNSHandler):
|
|
|
89
110
|
@rtype: dict
|
|
90
111
|
"""
|
|
91
112
|
res = super(PowerDNSServer, self).as_dict(short=short)
|
|
92
|
-
res[
|
|
113
|
+
res["api_server_version"] = self.api_server_version
|
|
93
114
|
|
|
94
115
|
return res
|
|
95
116
|
|
|
96
117
|
# -------------------------------------------------------------------------
|
|
97
118
|
def get_api_server_version(self):
|
|
98
119
|
"""Retreive from PowerDNS API their server version."""
|
|
99
|
-
path =
|
|
120
|
+
path = "/servers/{}".format(self.api_servername)
|
|
100
121
|
try:
|
|
101
122
|
json_response = self.perform_request(path)
|
|
102
123
|
except (PDNSApiNotFoundError, PDNSApiValidationError):
|
|
103
|
-
LOG.error(_(
|
|
124
|
+
LOG.error(_("Could not found server info."))
|
|
104
125
|
return None
|
|
105
126
|
if self.verbose > 2:
|
|
106
|
-
LOG.debug(_(
|
|
127
|
+
LOG.debug(_("Got a response:") + "\n" + pp(json_response))
|
|
107
128
|
|
|
108
|
-
if
|
|
109
|
-
self._api_server_version = to_str(json_response[
|
|
110
|
-
LOG.info(_(
|
|
129
|
+
if "version" in json_response:
|
|
130
|
+
self._api_server_version = to_str(json_response["version"])
|
|
131
|
+
LOG.info(_("PowerDNS server version {!r}.").format(self.api_server_version))
|
|
111
132
|
return self.api_server_version
|
|
112
|
-
LOG.error(_(
|
|
133
|
+
LOG.error(_("Did not found version info in server info:") + "\n" + pp(json_response))
|
|
113
134
|
return None
|
|
114
135
|
|
|
115
136
|
# -------------------------------------------------------------------------
|
|
116
137
|
def get_api_zones(self):
|
|
117
138
|
"""Pull from PowerDNS API a list of all zones and return them as a PowerDNSZoneDict."""
|
|
118
|
-
LOG.debug(_(
|
|
139
|
+
LOG.debug(_("Trying to get all zones from PDNS API ..."))
|
|
119
140
|
|
|
120
|
-
path =
|
|
141
|
+
path = "/servers/{}/zones".format(self.api_servername)
|
|
121
142
|
json_response = self.perform_request(path)
|
|
122
143
|
if self.verbose > 3:
|
|
123
|
-
LOG.debug(_(
|
|
144
|
+
LOG.debug(_("Got a response:") + "\n" + pp(json_response))
|
|
124
145
|
|
|
125
146
|
self.zones = PowerDNSZoneDict()
|
|
126
147
|
|
|
127
148
|
for data in json_response:
|
|
128
149
|
zone = PowerDNSZone.init_from_dict(
|
|
129
|
-
data,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
150
|
+
data,
|
|
151
|
+
appname=self.appname,
|
|
152
|
+
verbose=self.verbose,
|
|
153
|
+
base_dir=self.base_dir,
|
|
154
|
+
master_server=self.master_server,
|
|
155
|
+
port=self.port,
|
|
156
|
+
key=self.key,
|
|
157
|
+
use_https=self.use_https,
|
|
158
|
+
timeout=self.timeout,
|
|
159
|
+
path_prefix=self.path_prefix,
|
|
160
|
+
simulate=self.simulate,
|
|
161
|
+
force=self.force,
|
|
162
|
+
initialized=True,
|
|
163
|
+
)
|
|
133
164
|
self.zones.append(zone)
|
|
134
165
|
if self.verbose > 3:
|
|
135
|
-
print(
|
|
166
|
+
print("{!r}".format(zone))
|
|
136
167
|
|
|
137
168
|
if self.verbose > 1:
|
|
138
|
-
msg = ngettext(
|
|
169
|
+
msg = ngettext("Found a zone.", "Found {n} zones.", len(self.zones))
|
|
139
170
|
LOG.debug(msg.format(n=len(self.zones)))
|
|
140
171
|
|
|
141
172
|
if self.verbose > 2:
|
|
142
173
|
if self.verbose > 3:
|
|
143
|
-
LOG.debug(_(
|
|
174
|
+
LOG.debug(_("Zones:") + "\n" + pp(self.zones.as_list()))
|
|
144
175
|
else:
|
|
145
|
-
LOG.debug(_(
|
|
176
|
+
LOG.debug(_("Zones:") + "\n" + pp(list(self.zones.keys())))
|
|
146
177
|
|
|
147
178
|
return self.zones
|
|
148
179
|
|
|
@@ -157,20 +188,23 @@ class PowerDNSServer(BasePowerDNSHandler):
|
|
|
157
188
|
return None
|
|
158
189
|
|
|
159
190
|
if self.verbose > 2:
|
|
160
|
-
LOG.debug(
|
|
161
|
-
|
|
191
|
+
LOG.debug(
|
|
192
|
+
_("Searching an appropriate zone for item {i!r} - FQDN {f!r} ...").format(
|
|
193
|
+
i=item, f=fqdn
|
|
194
|
+
)
|
|
195
|
+
)
|
|
162
196
|
|
|
163
197
|
for zone_name in reversed(self.zones.keys()):
|
|
164
|
-
pattern = r
|
|
198
|
+
pattern = r"\." + re.escape(zone_name) + "$"
|
|
165
199
|
if self.verbose > 3:
|
|
166
|
-
LOG.debug(_(
|
|
200
|
+
LOG.debug(_("Search pattern: {}").format(pattern))
|
|
167
201
|
if re.search(pattern, fqdn):
|
|
168
202
|
return zone_name
|
|
169
203
|
zone = self.zones[zone_name]
|
|
170
204
|
if zone_name != zone.name_unicode:
|
|
171
|
-
pattern = r
|
|
205
|
+
pattern = r"\." + re.escape(zone.name_unicode) + "$"
|
|
172
206
|
if self.verbose > 3:
|
|
173
|
-
LOG.debug(_(
|
|
207
|
+
LOG.debug(_("Search pattern Unicode: {}").format(pattern))
|
|
174
208
|
if re.search(pattern, fqdn):
|
|
175
209
|
return zone_name
|
|
176
210
|
|
|
@@ -187,22 +221,25 @@ class PowerDNSServer(BasePowerDNSHandler):
|
|
|
187
221
|
return []
|
|
188
222
|
|
|
189
223
|
if self.verbose > 2:
|
|
190
|
-
LOG.debug(
|
|
191
|
-
|
|
224
|
+
LOG.debug(
|
|
225
|
+
_("Searching all appropriate zones for item {i!r} - FQDN {f!r} ...").format(
|
|
226
|
+
i=item, f=fqdn
|
|
227
|
+
)
|
|
228
|
+
)
|
|
192
229
|
zones = []
|
|
193
230
|
|
|
194
231
|
for zone_name in self.zones.keys():
|
|
195
|
-
pattern = r
|
|
232
|
+
pattern = r"\." + re.escape(zone_name) + "$"
|
|
196
233
|
if self.verbose > 3:
|
|
197
|
-
LOG.debug(_(
|
|
234
|
+
LOG.debug(_("Search pattern: {}").format(pattern))
|
|
198
235
|
if re.search(pattern, fqdn):
|
|
199
236
|
zones.append(zone_name)
|
|
200
237
|
continue
|
|
201
238
|
zone = self.zones[zone_name]
|
|
202
239
|
if zone_name != zone.name_unicode:
|
|
203
|
-
pattern = r
|
|
240
|
+
pattern = r"\." + re.escape(zone.name_unicode) + "$"
|
|
204
241
|
if self.verbose > 3:
|
|
205
|
-
LOG.debug(_(
|
|
242
|
+
LOG.debug(_("Search pattern Unicode: {}").format(pattern))
|
|
206
243
|
if re.search(pattern, fqdn):
|
|
207
244
|
zones.append(zone_name)
|
|
208
245
|
|
|
@@ -210,7 +247,7 @@ class PowerDNSServer(BasePowerDNSHandler):
|
|
|
210
247
|
|
|
211
248
|
|
|
212
249
|
# =============================================================================
|
|
213
|
-
if __name__ ==
|
|
250
|
+
if __name__ == "__main__":
|
|
214
251
|
|
|
215
252
|
pass
|
|
216
253
|
|
fb_pdnstools/xlate.py
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
"""
|
|
4
4
|
@summary: The module for i18n.
|
|
5
5
|
|
|
6
|
-
It provides translation object, usable from all other
|
|
7
|
-
modules in this package.
|
|
6
|
+
It provides translation object, usable from all other modules in this package.
|
|
8
7
|
@author: Frank Brehm
|
|
9
8
|
@contact: frank@brehm-online.com
|
|
10
9
|
@copyright: © 2024 by Frank Brehm, Berlin
|
|
@@ -16,68 +15,78 @@ import copy
|
|
|
16
15
|
import gettext
|
|
17
16
|
import logging
|
|
18
17
|
import sys
|
|
19
|
-
|
|
20
|
-
from pathlib import Path
|
|
21
|
-
except ImportError:
|
|
22
|
-
from pathlib2 import Path
|
|
23
|
-
try:
|
|
24
|
-
from packaging.version import Version
|
|
25
|
-
except ImportError:
|
|
26
|
-
from distutils.version import LooseVersion as Version
|
|
18
|
+
from pathlib import Path
|
|
27
19
|
|
|
28
20
|
# Third party modules
|
|
29
21
|
import babel
|
|
30
22
|
import babel.lists
|
|
31
23
|
from babel.support import Translations
|
|
32
24
|
|
|
33
|
-
|
|
25
|
+
try:
|
|
26
|
+
from semver import Version
|
|
27
|
+
except ImportError:
|
|
28
|
+
from semver import VersionInfo as Version
|
|
29
|
+
|
|
30
|
+
DOMAIN = "fb_pdnstools"
|
|
34
31
|
|
|
35
32
|
LOG = logging.getLogger(__name__)
|
|
36
33
|
|
|
37
|
-
__version__ =
|
|
34
|
+
__version__ = "1.0.0"
|
|
38
35
|
|
|
39
36
|
__me__ = Path(__file__).resolve()
|
|
40
37
|
__module_dir__ = __me__.parent
|
|
41
38
|
__lib_dir__ = __module_dir__.parent
|
|
42
39
|
__base_dir__ = __lib_dir__.parent
|
|
43
|
-
|
|
40
|
+
|
|
41
|
+
LOCALE_DIR = __base_dir__ / "locale"
|
|
42
|
+
|
|
44
43
|
if LOCALE_DIR.is_dir():
|
|
44
|
+
# Not installed, in development workdir
|
|
45
45
|
LOCALE_DIR = str(LOCALE_DIR)
|
|
46
46
|
else:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
# Somehow installed
|
|
48
|
+
LOCALE_DIR = __module_dir__.joinpath("locale")
|
|
49
|
+
if sys.prefix == sys.base_prefix:
|
|
50
|
+
# installed as a package
|
|
51
|
+
LOCALE_DIR = sys.prefix + "/share/locale"
|
|
50
52
|
else:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
# Obviously in a virtual environment
|
|
54
|
+
__base_dir__ = Path(sys.prefix)
|
|
55
|
+
LOCALE_DIR = __base_dir__ / "share" / "locale"
|
|
56
|
+
if LOCALE_DIR.is_dir():
|
|
57
|
+
LOCALE_DIR = str(LOCALE_DIR.resolve())
|
|
58
|
+
else:
|
|
59
|
+
LOCALE_DIR = __module_dir__ / "locale"
|
|
60
|
+
if LOCALE_DIR.is_dir():
|
|
61
|
+
LOCALE_DIR = str(LOCALE_DIR)
|
|
62
|
+
else:
|
|
63
|
+
LOCALE_DIR = str(__base_dir__ / sys.prefix / "share" / "locale")
|
|
64
|
+
|
|
65
|
+
DEFAULT_LOCALE_DEF = "en_US"
|
|
54
66
|
DEFAULT_LOCALE = babel.core.default_locale()
|
|
55
67
|
if not DEFAULT_LOCALE:
|
|
56
68
|
DEFAULT_LOCALE = DEFAULT_LOCALE_DEF
|
|
57
69
|
|
|
58
|
-
__mo_file__ = gettext.find(DOMAIN,
|
|
70
|
+
__mo_file__ = gettext.find(DOMAIN, LOCALE_DIR)
|
|
59
71
|
if __mo_file__:
|
|
60
72
|
try:
|
|
61
|
-
with open(__mo_file__,
|
|
73
|
+
with open(__mo_file__, "rb") as F:
|
|
62
74
|
XLATOR = Translations(F, DOMAIN)
|
|
63
75
|
except IOError:
|
|
64
76
|
XLATOR = gettext.NullTranslations()
|
|
65
77
|
else:
|
|
66
78
|
XLATOR = gettext.NullTranslations()
|
|
67
79
|
|
|
68
|
-
CUR_BABEL_VERSION = Version(babel.__version__)
|
|
69
|
-
NEWER_BABEL_VERSION = Version(
|
|
80
|
+
CUR_BABEL_VERSION = Version.parse(babel.__version__)
|
|
81
|
+
NEWER_BABEL_VERSION = Version.parse("2.6.0")
|
|
70
82
|
|
|
71
|
-
SUPPORTED_LANGS = (
|
|
72
|
-
'de',
|
|
73
|
-
'en'
|
|
74
|
-
)
|
|
83
|
+
SUPPORTED_LANGS = ("de", "en")
|
|
75
84
|
|
|
76
85
|
_ = XLATOR.gettext
|
|
77
86
|
|
|
78
87
|
|
|
79
88
|
# =============================================================================
|
|
80
|
-
def format_list(lst, do_repr=False, style=
|
|
89
|
+
def format_list(lst, do_repr=False, style="standard", locale=DEFAULT_LOCALE):
|
|
81
90
|
"""
|
|
82
91
|
Format the items in `lst` as a list.
|
|
83
92
|
|
|
@@ -85,13 +94,13 @@ def format_list(lst, do_repr=False, style='standard', locale=DEFAULT_LOCALE):
|
|
|
85
94
|
:param locale: the locale
|
|
86
95
|
"""
|
|
87
96
|
if not lst:
|
|
88
|
-
return
|
|
97
|
+
return ""
|
|
89
98
|
|
|
90
99
|
my_list = copy.copy(lst)
|
|
91
100
|
if do_repr:
|
|
92
101
|
my_list = []
|
|
93
102
|
for item in lst:
|
|
94
|
-
my_list.append(
|
|
103
|
+
my_list.append("{!r}".format(item))
|
|
95
104
|
|
|
96
105
|
if CUR_BABEL_VERSION < NEWER_BABEL_VERSION:
|
|
97
106
|
return babel.lists.format_list(my_list, locale=locale)
|
|
@@ -100,13 +109,25 @@ def format_list(lst, do_repr=False, style='standard', locale=DEFAULT_LOCALE):
|
|
|
100
109
|
|
|
101
110
|
# =============================================================================
|
|
102
111
|
|
|
103
|
-
if __name__ ==
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
|
|
114
|
+
out_list = []
|
|
115
|
+
out_list.append([_("Module directory:"), str(__module_dir__)])
|
|
116
|
+
out_list.append([_("Lib directory:"), str(__lib_dir__)])
|
|
117
|
+
out_list.append([_("Base directory:"), str(__base_dir__)])
|
|
118
|
+
out_list.append([_("Locale directory:"), LOCALE_DIR])
|
|
119
|
+
out_list.append([_("Locale domain:"), DOMAIN])
|
|
120
|
+
out_list.append([_("Found .mo-file:"), __mo_file__])
|
|
121
|
+
|
|
122
|
+
max_len = 1
|
|
123
|
+
for pair in out_list:
|
|
124
|
+
if len(pair[0]) > max_len:
|
|
125
|
+
max_len = len(pair[0])
|
|
126
|
+
|
|
127
|
+
template = "{{label:<{}}} {{val!r}}".format(max_len)
|
|
128
|
+
for pair in out_list:
|
|
129
|
+
print(template.format(label=pair[0], val=pair[1]))
|
|
104
130
|
|
|
105
|
-
print(_('Module directory: {!r}').format(__module_dir__))
|
|
106
|
-
print(_('Base directory: {!r}').format(__base_dir__))
|
|
107
|
-
print(_('Locale directory: {!r}').format(LOCALE_DIR))
|
|
108
|
-
print(_('Locale domain: {!r}').format(DOMAIN))
|
|
109
|
-
print(_('Found .mo-file: {!r}').format(__mo_file__))
|
|
110
131
|
|
|
111
132
|
# =============================================================================
|
|
112
133
|
|