zboxapi 0.0.5__tar.gz → 0.0.6__tar.gz
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.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.6"
|
|
@@ -49,9 +49,10 @@ def get_hosts_lines(hosts_fo: IO) -> list[dict[str, str]]:
|
|
|
49
49
|
line = line.strip()
|
|
50
50
|
if not line or line.startswith("#"):
|
|
51
51
|
continue
|
|
52
|
-
line_ip, *
|
|
52
|
+
line_ip, *line_hostnames = line.split()
|
|
53
53
|
lines.extend(
|
|
54
|
-
{"ip": IPv4Address(line_ip), "
|
|
54
|
+
{"ip": IPv4Address(line_ip), "hostname": line_hostname}
|
|
55
|
+
for line_hostname in line_hostnames
|
|
55
56
|
)
|
|
56
57
|
return sorted(lines, key=sort_hosts_lines)
|
|
57
58
|
|
|
@@ -59,12 +60,13 @@ def get_hosts_lines(hosts_fo: IO) -> list[dict[str, str]]:
|
|
|
59
60
|
def filter_hosts_file(
|
|
60
61
|
lines: list[dict[str, str]],
|
|
61
62
|
ip: IPv4Address | None = None,
|
|
62
|
-
|
|
63
|
+
hostname: str | None = None,
|
|
63
64
|
):
|
|
64
65
|
return [
|
|
65
66
|
line
|
|
66
67
|
for line in lines
|
|
67
|
-
if (not ip or ip == line["ip"])
|
|
68
|
+
if (not ip or ip == line["ip"])
|
|
69
|
+
and (not hostname or hostname == line["hostname"])
|
|
68
70
|
]
|
|
69
71
|
|
|
70
72
|
|
|
@@ -72,7 +74,7 @@ def write_hosts_file(hosts_fo: IO, lines: list[dict[str, str]]):
|
|
|
72
74
|
lines.sort(key=sort_hosts_lines)
|
|
73
75
|
hosts_fo.seek(0)
|
|
74
76
|
hosts_fo.truncate()
|
|
75
|
-
hosts_fo.writelines([f"{line['ip']}\t{line['
|
|
77
|
+
hosts_fo.writelines([f"{line['ip']}\t{line['hostname']}\n" for line in lines])
|
|
76
78
|
|
|
77
79
|
|
|
78
80
|
def validate_api_key(api_key: Annotated[APIKey, Security(api_key_header)]):
|
|
@@ -101,11 +103,11 @@ def dnsmasq_sighup():
|
|
|
101
103
|
|
|
102
104
|
|
|
103
105
|
def sort_hosts_lines(item: dict):
|
|
104
|
-
"""Sort list by loopback first, ip second,
|
|
106
|
+
"""Sort list by loopback first, ip second, hostname third"""
|
|
105
107
|
return (
|
|
106
108
|
not item["ip"].is_loopback,
|
|
107
109
|
socket.inet_aton(str(item["ip"])),
|
|
108
|
-
item["
|
|
110
|
+
item["hostname"],
|
|
109
111
|
)
|
|
110
112
|
|
|
111
113
|
|
|
@@ -120,68 +122,57 @@ def simplify_operation_ids(api: FastAPI) -> None:
|
|
|
120
122
|
route.operation_id = f"{tag}_{route.name}"
|
|
121
123
|
|
|
122
124
|
|
|
123
|
-
def RecordNotFound(ip,
|
|
125
|
+
def RecordNotFound(ip, hostname):
|
|
124
126
|
return HTTPException(
|
|
125
127
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
126
|
-
detail=f"DNS record not found: ip={ip},
|
|
128
|
+
detail=f"DNS record not found: ip={ip}, hostname={hostname}",
|
|
127
129
|
)
|
|
128
130
|
|
|
129
131
|
|
|
130
|
-
def RecordAlreadyPresent(ip,
|
|
132
|
+
def RecordAlreadyPresent(ip, hostname):
|
|
131
133
|
return HTTPException(
|
|
132
134
|
status_code=status.HTTP_406_NOT_ACCEPTABLE,
|
|
133
|
-
detail=f"DNS record already present: ip={ip},
|
|
135
|
+
detail=f"DNS record already present: ip={ip}, hostname={hostname}",
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"""
|
|
141
|
-
|
|
142
|
-
if not 1 < len(value) < 253:
|
|
143
|
-
raise PydanticCustomError("value_error", "Invalid fqdn length")
|
|
144
|
-
|
|
145
|
-
# Remove trailing dot
|
|
146
|
-
if value[-1] == ".":
|
|
147
|
-
value = value[:-1]
|
|
148
|
-
|
|
149
|
-
# Split hostname into list of DNS labels
|
|
150
|
-
labels = value.split(".")
|
|
139
|
+
def validate_hostname(value: str):
|
|
140
|
+
if not 1 < len(value) < 64:
|
|
141
|
+
raise PydanticCustomError("value_error", "Invalid hostname length")
|
|
151
142
|
|
|
152
143
|
# Define pattern of DNS label
|
|
153
144
|
# Can begin and end with a number or letter only
|
|
154
145
|
# Can contain hyphens, a-z, A-Z, 0-9
|
|
155
146
|
# 1 - 63 chars allowed
|
|
156
|
-
|
|
147
|
+
hostname_re = re.compile(r"^[a-z0-9]([a-z-0-9-]{0,61}[a-z0-9])?$", re.IGNORECASE)
|
|
157
148
|
|
|
158
149
|
# Check that all labels match that pattern.
|
|
159
|
-
if not
|
|
160
|
-
raise PydanticCustomError("value_error", "Invalid
|
|
150
|
+
if not hostname_re.match(value):
|
|
151
|
+
raise PydanticCustomError("value_error", "Invalid hostname")
|
|
161
152
|
return value
|
|
162
153
|
|
|
163
154
|
|
|
164
|
-
|
|
155
|
+
HOSTNAME = Annotated[str, AfterValidator(validate_hostname)]
|
|
165
156
|
|
|
166
157
|
|
|
167
158
|
class DnsCreate(BaseModel):
|
|
168
159
|
ip: IPv4Address
|
|
169
|
-
|
|
160
|
+
hostname: HOSTNAME
|
|
170
161
|
|
|
171
162
|
|
|
172
163
|
class DnsDelete(BaseModel):
|
|
173
164
|
ip: IPv4Address
|
|
174
|
-
|
|
165
|
+
hostname: HOSTNAME
|
|
175
166
|
|
|
176
167
|
|
|
177
168
|
class DnsUpdate(BaseModel):
|
|
178
169
|
ip: IPv4Address
|
|
179
|
-
|
|
170
|
+
hostname: HOSTNAME
|
|
180
171
|
|
|
181
172
|
|
|
182
173
|
class DnsView(BaseModel):
|
|
183
174
|
ip: IPv4Address
|
|
184
|
-
|
|
175
|
+
hostname: str
|
|
185
176
|
|
|
186
177
|
|
|
187
178
|
dns_router = APIRouter(prefix="/dns", tags=["dns"])
|
|
@@ -194,16 +185,16 @@ def dns_get_all() -> list[DnsView]:
|
|
|
194
185
|
return hosts_lines
|
|
195
186
|
|
|
196
187
|
|
|
197
|
-
@dns_router.get("/{ip}/{
|
|
188
|
+
@dns_router.get("/{ip}/{hostname}")
|
|
198
189
|
def dns_get(
|
|
199
190
|
ip: IPv4Address,
|
|
200
|
-
|
|
191
|
+
hostname: HOSTNAME,
|
|
201
192
|
) -> DnsView:
|
|
202
193
|
with get_hosts_file_object() as hosts_fo:
|
|
203
194
|
hosts_lines = get_hosts_lines(hosts_fo)
|
|
204
|
-
hosts_lines = filter_hosts_file(hosts_lines, ip,
|
|
195
|
+
hosts_lines = filter_hosts_file(hosts_lines, ip, hostname)
|
|
205
196
|
if not hosts_lines:
|
|
206
|
-
raise RecordNotFound(ip,
|
|
197
|
+
raise RecordNotFound(ip, hostname)
|
|
207
198
|
return hosts_lines[0]
|
|
208
199
|
|
|
209
200
|
|
|
@@ -213,42 +204,42 @@ def dns_add(
|
|
|
213
204
|
) -> list[DnsView]:
|
|
214
205
|
with get_hosts_file_object() as hosts_fo:
|
|
215
206
|
hosts_lines = get_hosts_lines(hosts_fo)
|
|
216
|
-
if filter_hosts_file(hosts_lines, dns_in.ip, dns_in.
|
|
217
|
-
raise RecordAlreadyPresent(dns_in.ip, dns_in.
|
|
218
|
-
hosts_lines.append({"ip": dns_in.ip, "
|
|
207
|
+
if filter_hosts_file(hosts_lines, dns_in.ip, dns_in.hostname):
|
|
208
|
+
raise RecordAlreadyPresent(dns_in.ip, dns_in.hostname)
|
|
209
|
+
hosts_lines.append({"ip": dns_in.ip, "hostname": dns_in.hostname})
|
|
219
210
|
write_hosts_file(hosts_fo, hosts_lines)
|
|
220
211
|
dnsmasq_sighup()
|
|
221
212
|
return hosts_lines
|
|
222
213
|
|
|
223
214
|
|
|
224
|
-
@dns_router.put("/{ip}/{
|
|
215
|
+
@dns_router.put("/{ip}/{hostname}")
|
|
225
216
|
def dns_update(
|
|
226
217
|
ip: IPv4Address,
|
|
227
|
-
|
|
218
|
+
hostname: HOSTNAME,
|
|
228
219
|
dns_in: DnsUpdate,
|
|
229
220
|
) -> list[DnsView]:
|
|
230
221
|
with get_hosts_file_object() as hosts_fo:
|
|
231
222
|
hosts_lines = get_hosts_lines(hosts_fo)
|
|
232
|
-
key = {"ip": ip, "
|
|
223
|
+
key = {"ip": ip, "hostname": hostname}
|
|
233
224
|
if key not in hosts_lines:
|
|
234
|
-
raise RecordNotFound(ip,
|
|
225
|
+
raise RecordNotFound(ip, hostname)
|
|
235
226
|
ix = hosts_lines.index(key)
|
|
236
|
-
hosts_lines[ix] = {"ip": dns_in.ip, "
|
|
227
|
+
hosts_lines[ix] = {"ip": dns_in.ip, "hostname": dns_in.hostname}
|
|
237
228
|
write_hosts_file(hosts_fo, hosts_lines)
|
|
238
229
|
dnsmasq_sighup()
|
|
239
230
|
return hosts_lines
|
|
240
231
|
|
|
241
232
|
|
|
242
|
-
@dns_router.delete("/{ip}/{
|
|
233
|
+
@dns_router.delete("/{ip}/{hostname}")
|
|
243
234
|
def dns_delete(
|
|
244
235
|
ip: IPv4Address,
|
|
245
|
-
|
|
236
|
+
hostname: HOSTNAME,
|
|
246
237
|
) -> list[DnsView]:
|
|
247
238
|
with get_hosts_file_object() as hosts_fo:
|
|
248
239
|
hosts_lines = get_hosts_lines(hosts_fo)
|
|
249
|
-
key = {"ip": ip, "
|
|
240
|
+
key = {"ip": ip, "hostname": hostname}
|
|
250
241
|
if key not in hosts_lines:
|
|
251
|
-
raise RecordNotFound(ip,
|
|
242
|
+
raise RecordNotFound(ip, hostname)
|
|
252
243
|
hosts_lines.remove(key)
|
|
253
244
|
write_hosts_file(hosts_fo, hosts_lines)
|
|
254
245
|
dnsmasq_sighup()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.5"
|
|
File without changes
|