fb-pdnstools 1.0.2__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.2.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.2.data/scripts/pdns-bulk-remove +0 -68
- fb_pdnstools-1.0.2.dist-info/METADATA +0 -40
- fb_pdnstools-1.0.2.dist-info/RECORD +0 -17
- fb_pdnstools-1.0.2.dist-info/top_level.txt +0 -1
- {fb_pdnstools-1.0.2.dist-info → fb_pdnstools-1.1.0.dist-info/licenses}/LICENSE +0 -0
fb_pdnstools/__init__.py
CHANGED
|
@@ -12,57 +12,99 @@ from __future__ import absolute_import
|
|
|
12
12
|
# Standard modules
|
|
13
13
|
import re
|
|
14
14
|
|
|
15
|
-
__version__ =
|
|
15
|
+
__version__ = "1.1.0"
|
|
16
16
|
|
|
17
17
|
# This library name will be used as a part of the user agent in HTTP(S) requests
|
|
18
|
-
LIBRARY_NAME =
|
|
18
|
+
LIBRARY_NAME = "fb-pdns-api-client"
|
|
19
19
|
|
|
20
20
|
VALID_RRSET_TYPES = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
"SOA",
|
|
22
|
+
"A",
|
|
23
|
+
"AAAA",
|
|
24
|
+
"AFSDB",
|
|
25
|
+
"APL",
|
|
26
|
+
"CAA",
|
|
27
|
+
"CDNSKEY",
|
|
28
|
+
"CDS",
|
|
29
|
+
"CERT",
|
|
30
|
+
"CNAME",
|
|
31
|
+
"DHCID",
|
|
32
|
+
"DLV",
|
|
33
|
+
"DNAME",
|
|
34
|
+
"DNSKEY",
|
|
35
|
+
"DS",
|
|
36
|
+
"HIP",
|
|
37
|
+
"HINFO",
|
|
38
|
+
"IPSECKEY",
|
|
39
|
+
"ISDN",
|
|
40
|
+
"KEY",
|
|
41
|
+
"KX",
|
|
42
|
+
"LOC",
|
|
43
|
+
"MB",
|
|
44
|
+
"MINFO",
|
|
45
|
+
"MX",
|
|
46
|
+
"NAPTR",
|
|
47
|
+
"NS",
|
|
48
|
+
"NSAP",
|
|
49
|
+
"NSEC",
|
|
50
|
+
"NSEC3",
|
|
51
|
+
"NSEC3PARAM",
|
|
52
|
+
"OPT",
|
|
53
|
+
"PTR",
|
|
54
|
+
"RP",
|
|
55
|
+
"RRSIG",
|
|
56
|
+
"SIG",
|
|
57
|
+
"SPF",
|
|
58
|
+
"SRV",
|
|
59
|
+
"SSHFP",
|
|
60
|
+
"TA",
|
|
61
|
+
"TKEY",
|
|
62
|
+
"TLSA",
|
|
63
|
+
"TSIG",
|
|
64
|
+
"TXT",
|
|
65
|
+
"URI",
|
|
66
|
+
"WKS",
|
|
67
|
+
"X25",
|
|
26
68
|
]
|
|
27
69
|
|
|
28
70
|
DEFAULT_PORT = 8081
|
|
29
71
|
DEFAULT_TIMEOUT = 20
|
|
30
|
-
DEFAULT_API_PREFIX =
|
|
72
|
+
DEFAULT_API_PREFIX = "/api/v1"
|
|
31
73
|
DEFAULT_USE_HTTPS = False
|
|
32
74
|
|
|
33
|
-
MAX_PORT_NUMBER = (2
|
|
75
|
+
MAX_PORT_NUMBER = (2**16) - 1
|
|
34
76
|
|
|
35
|
-
FQDN_REGEX = re.compile(r
|
|
77
|
+
FQDN_REGEX = re.compile(r"^((?!-)[-A-Z\d]{1,62}(?<!-)\.)+[A-Z]{1,62}\.?$", re.IGNORECASE)
|
|
36
78
|
|
|
37
79
|
|
|
38
80
|
# Own modules
|
|
39
|
-
from .errors import PDNSApiError
|
|
40
|
-
from .errors import PDNSApiNotAuthorizedError
|
|
41
|
-
from .errors import PDNSApiNotFoundError
|
|
42
|
-
from .errors import PDNSApiRateLimitExceededError
|
|
43
|
-
from .errors import PDNSApiRequestError
|
|
44
|
-
from .errors import PDNSApiTimeoutError
|
|
45
|
-
from .errors import PDNSApiValidationError
|
|
46
|
-
from .errors import PDNSNoRecordsToRemove
|
|
47
|
-
from .errors import PowerDNSHandlerError
|
|
48
|
-
from .errors import PowerDNSRecordError
|
|
49
|
-
from .errors import PowerDNSRecordSetError
|
|
50
|
-
from .errors import PowerDNSWrongRecordTypeError
|
|
51
|
-
from .errors import PowerDNSWrongSoaDataError
|
|
52
|
-
from .errors import PowerDNSZoneError
|
|
53
|
-
from .record import PowerDNSRecord
|
|
54
|
-
from .record import PowerDNSRecordList
|
|
55
|
-
from .record import PowerDNSRecordSet
|
|
56
|
-
from .record import PowerDNSRecordSetComment
|
|
57
|
-
from .record import PowerDNSRecordSetList
|
|
58
|
-
from .record import PowerDnsSOAData
|
|
59
|
-
from .server import PowerDNSServer
|
|
60
|
-
from .zone import PowerDNSZone
|
|
61
|
-
from .zone import PowerDNSZoneDict
|
|
81
|
+
from .errors import PDNSApiError # noqa: F401
|
|
82
|
+
from .errors import PDNSApiNotAuthorizedError # noqa: F401
|
|
83
|
+
from .errors import PDNSApiNotFoundError # noqa: F401
|
|
84
|
+
from .errors import PDNSApiRateLimitExceededError # noqa: F401
|
|
85
|
+
from .errors import PDNSApiRequestError # noqa: F401
|
|
86
|
+
from .errors import PDNSApiTimeoutError # noqa: F401
|
|
87
|
+
from .errors import PDNSApiValidationError # noqa: F401
|
|
88
|
+
from .errors import PDNSNoRecordsToRemove # noqa: F401
|
|
89
|
+
from .errors import PowerDNSHandlerError # noqa: F401
|
|
90
|
+
from .errors import PowerDNSRecordError # noqa: F401
|
|
91
|
+
from .errors import PowerDNSRecordSetError # noqa: F401
|
|
92
|
+
from .errors import PowerDNSWrongRecordTypeError # noqa: F401
|
|
93
|
+
from .errors import PowerDNSWrongSoaDataError # noqa: F401
|
|
94
|
+
from .errors import PowerDNSZoneError # noqa: F401
|
|
95
|
+
from .record import PowerDNSRecord # noqa: F401
|
|
96
|
+
from .record import PowerDNSRecordList # noqa: F401
|
|
97
|
+
from .record import PowerDNSRecordSet # noqa: F401
|
|
98
|
+
from .record import PowerDNSRecordSetComment # noqa: F401
|
|
99
|
+
from .record import PowerDNSRecordSetList # noqa: F401
|
|
100
|
+
from .record import PowerDnsSOAData # noqa: F401
|
|
101
|
+
from .server import PowerDNSServer # noqa: F401
|
|
102
|
+
from .zone import PowerDNSZone # noqa: F401
|
|
103
|
+
from .zone import PowerDNSZoneDict # noqa: F401
|
|
62
104
|
|
|
63
105
|
|
|
64
106
|
# =============================================================================
|
|
65
|
-
if __name__ ==
|
|
107
|
+
if __name__ == "__main__":
|
|
66
108
|
|
|
67
109
|
pass
|
|
68
110
|
|
fb_pdnstools/base_handler.py
CHANGED
|
@@ -18,6 +18,7 @@ import os
|
|
|
18
18
|
import re
|
|
19
19
|
import socket
|
|
20
20
|
from abc import ABCMeta
|
|
21
|
+
|
|
21
22
|
try:
|
|
22
23
|
from collections.abc import MutableMapping
|
|
23
24
|
except ImportError:
|
|
@@ -57,7 +58,7 @@ from .errors import PowerDNSHandlerError
|
|
|
57
58
|
from .xlate import XLATOR
|
|
58
59
|
|
|
59
60
|
|
|
60
|
-
__version__ =
|
|
61
|
+
__version__ = "1.0.0"
|
|
61
62
|
LOG = logging.getLogger(__name__)
|
|
62
63
|
|
|
63
64
|
LOGLEVEL_REQUESTS_SET = False
|
|
@@ -78,17 +79,25 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
78
79
|
|
|
79
80
|
default_port = DEFAULT_PORT
|
|
80
81
|
default_timeout = DEFAULT_TIMEOUT
|
|
81
|
-
default_api_servername =
|
|
82
|
+
default_api_servername = "localhost"
|
|
82
83
|
|
|
83
84
|
loglevel_requests_set = False
|
|
84
85
|
|
|
85
|
-
re_request_id = re.compile(r
|
|
86
|
+
re_request_id = re.compile(r"/requests/([-a-f0-9]+)/", re.IGNORECASE)
|
|
86
87
|
|
|
87
88
|
# -------------------------------------------------------------------------
|
|
88
89
|
def __init__(
|
|
89
|
-
self,
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
self,
|
|
91
|
+
version=__version__,
|
|
92
|
+
master_server=None,
|
|
93
|
+
port=DEFAULT_PORT,
|
|
94
|
+
key=None,
|
|
95
|
+
use_https=DEFAULT_USE_HTTPS,
|
|
96
|
+
timeout=None,
|
|
97
|
+
path_prefix=DEFAULT_API_PREFIX,
|
|
98
|
+
*args,
|
|
99
|
+
**kwargs,
|
|
100
|
+
):
|
|
92
101
|
"""Initialize a BasePowerDNSHandler object."""
|
|
93
102
|
self._master_server = master_server
|
|
94
103
|
self._port = self.default_port
|
|
@@ -96,12 +105,12 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
96
105
|
self._use_https = False
|
|
97
106
|
self._path_prefix = path_prefix
|
|
98
107
|
self._timeout = self.default_timeout
|
|
99
|
-
self._user_agent =
|
|
108
|
+
self._user_agent = "{}/{}".format(LIBRARY_NAME, __version__)
|
|
100
109
|
self._api_servername = self.default_api_servername
|
|
101
110
|
self._mocked = False
|
|
102
111
|
self.mocking_paths = []
|
|
103
112
|
|
|
104
|
-
super(BasePowerDNSHandler, self).__init__(
|
|
113
|
+
super(BasePowerDNSHandler, self).__init__(*args, **kwargs, version=version)
|
|
105
114
|
|
|
106
115
|
self.use_https = use_https
|
|
107
116
|
self.port = port
|
|
@@ -110,19 +119,20 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
110
119
|
global LOGLEVEL_REQUESTS_SET
|
|
111
120
|
|
|
112
121
|
if not LOGLEVEL_REQUESTS_SET:
|
|
113
|
-
msg = _(
|
|
114
|
-
m=
|
|
122
|
+
msg = _("Setting loglevel of the {m} module to {ll}.").format(
|
|
123
|
+
m="requests", ll="WARNING"
|
|
124
|
+
)
|
|
115
125
|
LOG.debug(msg)
|
|
116
|
-
logging.getLogger(
|
|
126
|
+
logging.getLogger("requests").setLevel(logging.WARNING)
|
|
117
127
|
LOGLEVEL_REQUESTS_SET = True
|
|
118
128
|
|
|
119
|
-
if
|
|
120
|
-
self.initialized = kwargs[
|
|
129
|
+
if "initialized" in kwargs:
|
|
130
|
+
self.initialized = kwargs["initialized"]
|
|
121
131
|
|
|
122
132
|
# -----------------------------------------------------------
|
|
123
133
|
@property
|
|
124
134
|
def master_server(self):
|
|
125
|
-
"""
|
|
135
|
+
"""Return the hostname or address of the PowerDNS master server."""
|
|
126
136
|
return self._master_server
|
|
127
137
|
|
|
128
138
|
@master_server.setter
|
|
@@ -132,7 +142,7 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
132
142
|
return
|
|
133
143
|
|
|
134
144
|
val = str(value).strip().lower()
|
|
135
|
-
if val ==
|
|
145
|
+
if val == "":
|
|
136
146
|
self._master_server = None
|
|
137
147
|
else:
|
|
138
148
|
self._master_server = val
|
|
@@ -140,7 +150,7 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
140
150
|
# -----------------------------------------------------------
|
|
141
151
|
@property
|
|
142
152
|
def port(self):
|
|
143
|
-
"""
|
|
153
|
+
"""Return the TCP port number of the PowerDNS API."""
|
|
144
154
|
return self._port
|
|
145
155
|
|
|
146
156
|
@port.setter
|
|
@@ -150,8 +160,9 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
150
160
|
return
|
|
151
161
|
val = int(value)
|
|
152
162
|
err_msg = _(
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
"Invalid port number {port!r} for the PowerDNS API, must be greater than zero "
|
|
164
|
+
"and less than {max}."
|
|
165
|
+
).format(port=value, max=(MAX_PORT_NUMBER + 1))
|
|
155
166
|
if val <= 0 or val >= MAX_PORT_NUMBER:
|
|
156
167
|
raise ValueError(err_msg)
|
|
157
168
|
self._port = val
|
|
@@ -159,7 +170,7 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
159
170
|
# -----------------------------------------------------------
|
|
160
171
|
@property
|
|
161
172
|
def key(self):
|
|
162
|
-
"""
|
|
173
|
+
"""Return the key used to authenticate against the PowerDNS API."""
|
|
163
174
|
return self._key
|
|
164
175
|
|
|
165
176
|
@key.setter
|
|
@@ -169,7 +180,7 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
169
180
|
return
|
|
170
181
|
|
|
171
182
|
val = str(value)
|
|
172
|
-
if val ==
|
|
183
|
+
if val == "":
|
|
173
184
|
self._key = None
|
|
174
185
|
else:
|
|
175
186
|
self._key = val
|
|
@@ -199,7 +210,7 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
199
210
|
# -----------------------------------------------------------
|
|
200
211
|
@property
|
|
201
212
|
def path_prefix(self):
|
|
202
|
-
"""
|
|
213
|
+
"""Return the hostname or address of the PowerDNS master server."""
|
|
203
214
|
return self._path_prefix
|
|
204
215
|
|
|
205
216
|
@path_prefix.setter
|
|
@@ -209,18 +220,18 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
209
220
|
return
|
|
210
221
|
|
|
211
222
|
val = str(value).strip()
|
|
212
|
-
if val ==
|
|
223
|
+
if val == "":
|
|
213
224
|
self._path_prefix = None
|
|
214
225
|
else:
|
|
215
226
|
if not os.path.isabs(val):
|
|
216
|
-
msg = _(
|
|
227
|
+
msg = _("The path prefix {!r} must be an absolute path.").format(value)
|
|
217
228
|
raise ValueError(msg)
|
|
218
229
|
self._path_prefix = val
|
|
219
230
|
|
|
220
231
|
# -----------------------------------------------------------
|
|
221
232
|
@property
|
|
222
233
|
def timeout(self):
|
|
223
|
-
"""
|
|
234
|
+
"""Return the timeout in seconds for requesting the PowerDNS API."""
|
|
224
235
|
return self._timeout
|
|
225
236
|
|
|
226
237
|
@timeout.setter
|
|
@@ -230,8 +241,9 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
230
241
|
return
|
|
231
242
|
val = int(value)
|
|
232
243
|
err_msg = _(
|
|
233
|
-
|
|
234
|
-
|
|
244
|
+
"Invalid timeout {!r} for requesting the PowerDNS API, must be greater than zero and "
|
|
245
|
+
"less or equal to 3600."
|
|
246
|
+
)
|
|
235
247
|
if val <= 0 or val > 3600:
|
|
236
248
|
msg = err_msg.format(value)
|
|
237
249
|
raise ValueError(msg)
|
|
@@ -240,25 +252,25 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
240
252
|
# -----------------------------------------------------------
|
|
241
253
|
@property
|
|
242
254
|
def user_agent(self):
|
|
243
|
-
"""
|
|
255
|
+
"""Return the name of the user agent used in API calls."""
|
|
244
256
|
return self._user_agent
|
|
245
257
|
|
|
246
258
|
@user_agent.setter
|
|
247
259
|
def user_agent(self, value):
|
|
248
|
-
if value is None or str(value).strip() ==
|
|
249
|
-
raise PowerDNSHandlerError(_(
|
|
260
|
+
if value is None or str(value).strip() == "":
|
|
261
|
+
raise PowerDNSHandlerError(_("Invalid user agent {!r} given.").format(value))
|
|
250
262
|
self._user_agent = str(value).strip()
|
|
251
263
|
|
|
252
264
|
# -----------------------------------------------------------
|
|
253
265
|
@property
|
|
254
266
|
def api_servername(self):
|
|
255
|
-
"""
|
|
267
|
+
"""Return the (virtual) name of the PowerDNS server used in API calls."""
|
|
256
268
|
return self._api_servername
|
|
257
269
|
|
|
258
270
|
@api_servername.setter
|
|
259
271
|
def api_servername(self, value):
|
|
260
|
-
if value is None or str(value).strip() ==
|
|
261
|
-
raise PowerDNSHandlerError(_(
|
|
272
|
+
if value is None or str(value).strip() == "":
|
|
273
|
+
raise PowerDNSHandlerError(_("Invalid API server name {!r} given.").format(value))
|
|
262
274
|
self._api_servername = str(value).strip()
|
|
263
275
|
|
|
264
276
|
# -------------------------------------------------------------------------
|
|
@@ -273,23 +285,23 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
273
285
|
@rtype: dict
|
|
274
286
|
"""
|
|
275
287
|
res = super(BasePowerDNSHandler, self).as_dict(short=short)
|
|
276
|
-
res[
|
|
277
|
-
res[
|
|
278
|
-
res[
|
|
279
|
-
res[
|
|
280
|
-
res[
|
|
281
|
-
res[
|
|
282
|
-
res[
|
|
283
|
-
res[
|
|
284
|
-
res[
|
|
285
|
-
res[
|
|
286
|
-
res[
|
|
287
|
-
res[
|
|
288
|
+
res["default_port"] = self.default_port
|
|
289
|
+
res["default_timeout"] = self.default_timeout
|
|
290
|
+
res["default_api_servername"] = self.default_api_servername
|
|
291
|
+
res["master_server"] = self.master_server
|
|
292
|
+
res["port"] = self.port
|
|
293
|
+
res["mocked"] = self.mocked
|
|
294
|
+
res["use_https"] = self.use_https
|
|
295
|
+
res["path_prefix"] = self.path_prefix
|
|
296
|
+
res["timeout"] = self.timeout
|
|
297
|
+
res["user_agent"] = self.user_agent
|
|
298
|
+
res["api_servername"] = self.api_servername
|
|
299
|
+
res["key"] = None
|
|
288
300
|
if self.key:
|
|
289
301
|
if self.verbose > 4:
|
|
290
|
-
res[
|
|
302
|
+
res["key"] = self.key
|
|
291
303
|
else:
|
|
292
|
-
res[
|
|
304
|
+
res["key"] = "*******"
|
|
293
305
|
|
|
294
306
|
return res
|
|
295
307
|
|
|
@@ -297,35 +309,36 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
297
309
|
@classmethod
|
|
298
310
|
def _request_id(cls, headers):
|
|
299
311
|
|
|
300
|
-
if
|
|
312
|
+
if "location" not in headers:
|
|
301
313
|
return None
|
|
302
314
|
|
|
303
|
-
loc = headers[
|
|
315
|
+
loc = headers["location"]
|
|
304
316
|
match = cls.re_request_id.search(loc)
|
|
305
317
|
if match:
|
|
306
318
|
return match.group(1)
|
|
307
319
|
else:
|
|
308
320
|
msg = _("Failed to extract request ID from response header 'location': {!r}").format(
|
|
309
|
-
loc
|
|
321
|
+
loc
|
|
322
|
+
)
|
|
310
323
|
raise PowerDNSHandlerError(msg)
|
|
311
324
|
|
|
312
325
|
# -------------------------------------------------------------------------
|
|
313
326
|
def _build_url(self, path, no_prefix=False):
|
|
314
327
|
|
|
315
328
|
if not os.path.isabs(path):
|
|
316
|
-
msg = _(
|
|
329
|
+
msg = _("The path {!r} must be an absolute path.").format(path)
|
|
317
330
|
raise ValueError(msg)
|
|
318
331
|
|
|
319
|
-
url =
|
|
332
|
+
url = "http://{}".format(self.master_server)
|
|
320
333
|
if self.mocked:
|
|
321
|
-
url =
|
|
334
|
+
url = "mock://{}".format(self.master_server)
|
|
322
335
|
elif self.use_https:
|
|
323
|
-
url =
|
|
336
|
+
url = "https://{}".format(self.master_server)
|
|
324
337
|
if self.port != 443:
|
|
325
|
-
url +=
|
|
338
|
+
url += ":{}".format(self.port)
|
|
326
339
|
else:
|
|
327
340
|
if self.port != 80:
|
|
328
|
-
url +=
|
|
341
|
+
url += ":{}".format(self.port)
|
|
329
342
|
|
|
330
343
|
if self.path_prefix and not no_prefix:
|
|
331
344
|
url += self.path_prefix
|
|
@@ -333,45 +346,45 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
333
346
|
url += path
|
|
334
347
|
|
|
335
348
|
if self.verbose > 1:
|
|
336
|
-
LOG.debug(_(
|
|
349
|
+
LOG.debug(_("Used URL: {!r}").format(url))
|
|
337
350
|
return url
|
|
338
351
|
|
|
339
352
|
# -------------------------------------------------------------------------
|
|
340
|
-
def perform_request(
|
|
341
|
-
self, path, no_prefix=False, method=
|
|
342
|
-
|
|
353
|
+
def perform_request( # noqa: C901
|
|
354
|
+
self, path, no_prefix=False, method="GET", data=None, headers=None, may_simulate=False
|
|
355
|
+
):
|
|
343
356
|
"""Perform the underlying API request."""
|
|
344
357
|
if headers is None:
|
|
345
358
|
headers = {}
|
|
346
359
|
if self.key:
|
|
347
|
-
headers[
|
|
360
|
+
headers["X-API-Key"] = self.key
|
|
348
361
|
|
|
349
362
|
url = self._build_url(path, no_prefix=no_prefix)
|
|
350
363
|
if self.verbose > 1:
|
|
351
|
-
LOG.debug(_(
|
|
364
|
+
LOG.debug(_("Request method: {!r}").format(method))
|
|
352
365
|
if data and self.verbose > 1:
|
|
353
|
-
data_out =
|
|
366
|
+
data_out = "{!r}".format(data)
|
|
354
367
|
try:
|
|
355
368
|
data_out = json.loads(data)
|
|
356
369
|
except ValueError:
|
|
357
370
|
pass
|
|
358
371
|
else:
|
|
359
372
|
data_out = pp(data_out)
|
|
360
|
-
LOG.debug(
|
|
373
|
+
LOG.debug("Data:\n{}".format(data_out))
|
|
361
374
|
if self.verbose > 2:
|
|
362
|
-
LOG.debug(
|
|
375
|
+
LOG.debug("RAW data:\n{}".format(data))
|
|
363
376
|
|
|
364
|
-
headers.update({
|
|
365
|
-
headers.update({
|
|
377
|
+
headers.update({"User-Agent": self.user_agent})
|
|
378
|
+
headers.update({"Content-Type": "application/json"})
|
|
366
379
|
if self.verbose > 1:
|
|
367
380
|
head_out = copy.copy(headers)
|
|
368
|
-
if
|
|
369
|
-
head_out[
|
|
370
|
-
LOG.debug(
|
|
381
|
+
if "X-API-Key" in head_out and self.verbose <= 4:
|
|
382
|
+
head_out["X-API-Key"] = "******"
|
|
383
|
+
LOG.debug("Headers:\n{}".format(pp(head_out)))
|
|
371
384
|
|
|
372
385
|
if may_simulate and self.simulate:
|
|
373
|
-
LOG.debug(_(
|
|
374
|
-
return
|
|
386
|
+
LOG.debug(_("Simulation mode, Request will not be sent."))
|
|
387
|
+
return ""
|
|
375
388
|
|
|
376
389
|
try:
|
|
377
390
|
|
|
@@ -379,36 +392,40 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
379
392
|
if self.mocked:
|
|
380
393
|
self.start_mocking(session)
|
|
381
394
|
response = session.request(
|
|
382
|
-
method, url, data=data, headers=headers, timeout=self.timeout
|
|
395
|
+
method, url, data=data, headers=headers, timeout=self.timeout
|
|
396
|
+
)
|
|
383
397
|
|
|
384
398
|
except RequestException as e:
|
|
385
399
|
raise PDNSRequestError(str(e), url, e.request, e.response)
|
|
386
400
|
|
|
387
401
|
except (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
402
|
+
socket.timeout,
|
|
403
|
+
urllib3.exceptions.ConnectTimeoutError,
|
|
404
|
+
urllib3.exceptions.MaxRetryError,
|
|
405
|
+
requests.exceptions.ConnectTimeout,
|
|
406
|
+
) as e:
|
|
407
|
+
msg = _("Got a {c} on connecting to {h!r}: {e}.").format(
|
|
408
|
+
c=e.__class__.__name__, h=self.master_server, e=e
|
|
409
|
+
)
|
|
393
410
|
raise PowerDNSHandlerError(msg)
|
|
394
411
|
|
|
395
412
|
try:
|
|
396
413
|
self._eval_response(url, response)
|
|
397
414
|
|
|
398
415
|
except ValueError:
|
|
399
|
-
raise PDNSApiError(_(
|
|
416
|
+
raise PDNSApiError(_("Failed to parse the response"), response.text)
|
|
400
417
|
|
|
401
418
|
if self.verbose > 3:
|
|
402
|
-
LOG.debug(
|
|
419
|
+
LOG.debug("RAW response: {!r}.".format(response.text))
|
|
403
420
|
if not response.text:
|
|
404
|
-
return
|
|
421
|
+
return ""
|
|
405
422
|
|
|
406
423
|
json_response = response.json()
|
|
407
424
|
if self.verbose > 3:
|
|
408
|
-
LOG.debug(
|
|
425
|
+
LOG.debug("JSON response:\n{}".format(pp(json_response)))
|
|
409
426
|
|
|
410
|
-
if
|
|
411
|
-
json_response[
|
|
427
|
+
if "location" in response.headers:
|
|
428
|
+
json_response["requestId"] = self._request_id(response.headers)
|
|
412
429
|
|
|
413
430
|
return json_response
|
|
414
431
|
|
|
@@ -420,8 +437,8 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
420
437
|
|
|
421
438
|
err = response.json()
|
|
422
439
|
code = response.status_code
|
|
423
|
-
msg = err[
|
|
424
|
-
LOG.debug(_(
|
|
440
|
+
msg = err["error"]
|
|
441
|
+
LOG.debug(_("Got an error response code {code}: {msg}").format(code=code, msg=msg))
|
|
425
442
|
if response.status_code == 401:
|
|
426
443
|
raise PDNSApiNotAuthorizedError(code, msg, url)
|
|
427
444
|
if response.status_code == 404:
|
|
@@ -436,7 +453,7 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
436
453
|
# -------------------------------------------------------------------------
|
|
437
454
|
def canon_name(self, name):
|
|
438
455
|
"""Canonize the DNS name, that means ensure a dot at the end of the name."""
|
|
439
|
-
ret = RE_DOT_AT_END.sub(
|
|
456
|
+
ret = RE_DOT_AT_END.sub(".", name, 1)
|
|
440
457
|
return ret
|
|
441
458
|
|
|
442
459
|
# -------------------------------------------------------------------------
|
|
@@ -457,12 +474,12 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
457
474
|
is_fqdn = False
|
|
458
475
|
except ValueError:
|
|
459
476
|
if self.verbose > 3:
|
|
460
|
-
LOG.debug(_(
|
|
477
|
+
LOG.debug(_("Name {!r} is not a valid IP address.").format(name))
|
|
461
478
|
is_fqdn = True
|
|
462
479
|
fqdn = name
|
|
463
480
|
|
|
464
|
-
if
|
|
465
|
-
LOG.error(_(
|
|
481
|
+
if ":" in fqdn:
|
|
482
|
+
LOG.error(_("Invalid FQDN {!r}.").format(fqdn))
|
|
466
483
|
return None
|
|
467
484
|
|
|
468
485
|
return self.canon_name(fqdn)
|
|
@@ -470,14 +487,14 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
470
487
|
# -------------------------------------------------------------------------
|
|
471
488
|
def decanon_name(self, name):
|
|
472
489
|
"""Decanonize the FQDN - removing possible dots at the end of the name."""
|
|
473
|
-
ret = RE_DOT_AT_END.sub(
|
|
490
|
+
ret = RE_DOT_AT_END.sub("", name)
|
|
474
491
|
return ret
|
|
475
492
|
|
|
476
493
|
# -------------------------------------------------------------------------
|
|
477
494
|
def verify_rrset_type(self, rtype, raise_on_error=True):
|
|
478
495
|
"""Verify, that the given name is a valid RRset type name."""
|
|
479
496
|
if not isinstance(rtype, six.string_types):
|
|
480
|
-
msg = _(
|
|
497
|
+
msg = _("A rrset type must be a string type, but is {!r} instead.").format(rtype)
|
|
481
498
|
if raise_on_error:
|
|
482
499
|
raise TypeError(msg)
|
|
483
500
|
LOG.error(msg)
|
|
@@ -485,14 +502,14 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
485
502
|
|
|
486
503
|
type_used = to_str(rtype).strip().upper()
|
|
487
504
|
if not type_used:
|
|
488
|
-
msg = _(
|
|
505
|
+
msg = _("Invalid, empty rrset type {!r} given.").format(rtype)
|
|
489
506
|
if raise_on_error:
|
|
490
507
|
raise ValueError(msg)
|
|
491
508
|
LOG.error(msg)
|
|
492
509
|
return None
|
|
493
510
|
|
|
494
511
|
if type_used not in VALID_RRSET_TYPES:
|
|
495
|
-
msg = _(
|
|
512
|
+
msg = _("Invalid rrset type {!r} given.").format(rtype)
|
|
496
513
|
if raise_on_error:
|
|
497
514
|
raise ValueError(msg)
|
|
498
515
|
LOG.error(msg)
|
|
@@ -506,34 +523,34 @@ class BasePowerDNSHandler(HandlingObject):
|
|
|
506
523
|
if not self.mocked:
|
|
507
524
|
return
|
|
508
525
|
|
|
509
|
-
LOG.debug(_(
|
|
526
|
+
LOG.debug(_("Preparing mocking ..."))
|
|
510
527
|
|
|
511
528
|
import requests_mock
|
|
512
529
|
|
|
513
530
|
adapter = requests_mock.Adapter()
|
|
514
|
-
session.mount(
|
|
531
|
+
session.mount("mock", adapter)
|
|
515
532
|
|
|
516
533
|
for path in self.mocking_paths:
|
|
517
534
|
|
|
518
535
|
if not isinstance(path, MutableMapping):
|
|
519
536
|
msg = _(
|
|
520
|
-
|
|
521
|
-
|
|
537
|
+
"Mocking path {p!r} is not a dictionary object, but a " "{c} object instead."
|
|
538
|
+
).format(p=path, c=path.__class__.__name__)
|
|
522
539
|
raise PowerDNSHandlerError(msg)
|
|
523
540
|
|
|
524
|
-
for key in (
|
|
541
|
+
for key in ("method", "url"):
|
|
525
542
|
if key not in path:
|
|
526
|
-
msg = _(
|
|
527
|
-
msg +=
|
|
543
|
+
msg = _("Mocking path has no {!r} key defined:").format(key)
|
|
544
|
+
msg += "\n" + pp(path)
|
|
528
545
|
raise PowerDNSHandlerError(msg)
|
|
529
546
|
|
|
530
547
|
if self.verbose > 2:
|
|
531
|
-
LOG.debug(_(
|
|
548
|
+
LOG.debug(_("Adding mocking path:") + "\n" + pp(path))
|
|
532
549
|
adapter.register_uri(**path)
|
|
533
550
|
|
|
534
551
|
|
|
535
552
|
# =============================================================================
|
|
536
|
-
if __name__ ==
|
|
553
|
+
if __name__ == "__main__":
|
|
537
554
|
|
|
538
555
|
pass
|
|
539
556
|
|