pgbelt 0.6.2__py3-none-any.whl → 0.7.1__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.
- pgbelt/cmd/convenience.py +5 -7
- pgbelt/cmd/preflight.py +457 -64
- pgbelt/cmd/setup.py +26 -7
- pgbelt/cmd/status.py +36 -0
- pgbelt/cmd/sync.py +40 -15
- pgbelt/cmd/teardown.py +2 -2
- pgbelt/config/models.py +5 -1
- pgbelt/util/dump.py +9 -5
- pgbelt/util/pglogical.py +27 -14
- pgbelt/util/postgres.py +177 -43
- {pgbelt-0.6.2.dist-info → pgbelt-0.7.1.dist-info}/METADATA +5 -1
- pgbelt-0.7.1.dist-info/RECORD +27 -0
- {pgbelt-0.6.2.dist-info → pgbelt-0.7.1.dist-info}/WHEEL +1 -1
- pgbelt-0.6.2.dist-info/RECORD +0 -27
- {pgbelt-0.6.2.dist-info → pgbelt-0.7.1.dist-info}/LICENSE +0 -0
- {pgbelt-0.6.2.dist-info → pgbelt-0.7.1.dist-info}/entry_points.txt +0 -0
pgbelt/cmd/convenience.py
CHANGED
|
@@ -34,9 +34,7 @@ def src_dsn(
|
|
|
34
34
|
echo(
|
|
35
35
|
conf.src.owner_dsn
|
|
36
36
|
if owner
|
|
37
|
-
else conf.src.pglogical_dsn
|
|
38
|
-
if pglogical
|
|
39
|
-
else conf.src.root_dsn
|
|
37
|
+
else conf.src.pglogical_dsn if pglogical else conf.src.root_dsn
|
|
40
38
|
)
|
|
41
39
|
|
|
42
40
|
|
|
@@ -56,9 +54,7 @@ def dst_dsn(
|
|
|
56
54
|
echo(
|
|
57
55
|
conf.dst.owner_dsn
|
|
58
56
|
if owner
|
|
59
|
-
else conf.dst.pglogical_dsn
|
|
60
|
-
if pglogical
|
|
61
|
-
else conf.dst.root_dsn
|
|
57
|
+
else conf.dst.pglogical_dsn if pglogical else conf.dst.root_dsn
|
|
62
58
|
)
|
|
63
59
|
|
|
64
60
|
|
|
@@ -66,7 +62,9 @@ async def _check_pkeys(
|
|
|
66
62
|
conf: DbupgradeConfig, logger: Logger
|
|
67
63
|
) -> tuple[list[str], list[str]]:
|
|
68
64
|
async with create_pool(conf.src.root_uri, min_size=1) as pool:
|
|
69
|
-
pkey_tables, no_pkey_tables, _ = await analyze_table_pkeys(
|
|
65
|
+
pkey_tables, no_pkey_tables, _ = await analyze_table_pkeys(
|
|
66
|
+
pool, conf.schema_name, logger
|
|
67
|
+
)
|
|
70
68
|
return pkey_tables, no_pkey_tables
|
|
71
69
|
|
|
72
70
|
|
pgbelt/cmd/preflight.py
CHANGED
|
@@ -12,7 +12,46 @@ from typer import echo
|
|
|
12
12
|
from typer import style
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
def _summary_table(results: dict, compared_extensions: list[str] = None) -> list[list]:
|
|
16
|
+
"""
|
|
17
|
+
Takes a dict of precheck results for all databases and returns a summary table for echo.
|
|
18
|
+
|
|
19
|
+
The summary table alters slightly if the results are for a destination database.
|
|
20
|
+
|
|
21
|
+
results format:
|
|
22
|
+
[
|
|
23
|
+
{
|
|
24
|
+
"server_version": "9.6.20",
|
|
25
|
+
"max_replication_slots": "10",
|
|
26
|
+
"max_worker_processes": "10",
|
|
27
|
+
"max_wal_senders": "10",
|
|
28
|
+
"shared_preload_libraries": ["pg_stat_statements", ...],
|
|
29
|
+
"rds.logical_replication": "on",
|
|
30
|
+
"schema: "public",
|
|
31
|
+
"extensions": ["uuid-ossp", ...],
|
|
32
|
+
"users": { // See pgbelt.util.postgres.precheck_info results["users"] for more info.
|
|
33
|
+
"root": {
|
|
34
|
+
"rolname": "root",
|
|
35
|
+
"rolcanlogin": True,
|
|
36
|
+
"rolcreaterole": True,
|
|
37
|
+
"rolinherit": True,
|
|
38
|
+
"rolsuper": True,
|
|
39
|
+
"memberof": ["rds_superuser", ...]
|
|
40
|
+
},
|
|
41
|
+
"owner": {
|
|
42
|
+
"rolname": "owner",
|
|
43
|
+
"rolcanlogin": True,
|
|
44
|
+
"rolcreaterole": False,
|
|
45
|
+
"rolinherit": True,
|
|
46
|
+
"rolsuper": False,
|
|
47
|
+
"memberof": ["rds_superuser", ...]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
...
|
|
52
|
+
]
|
|
53
|
+
"""
|
|
54
|
+
|
|
16
55
|
summary_table = [
|
|
17
56
|
[
|
|
18
57
|
style("database", "yellow"),
|
|
@@ -20,11 +59,12 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
20
59
|
style("max_replication_slots", "yellow"),
|
|
21
60
|
style("max_worker_processes", "yellow"),
|
|
22
61
|
style("max_wal_senders", "yellow"),
|
|
23
|
-
style("
|
|
24
|
-
style("pglogical", "yellow"),
|
|
62
|
+
style("shared_preload_libraries", "yellow"),
|
|
25
63
|
style("rds.logical_replication", "yellow"),
|
|
26
64
|
style("root user ok", "yellow"),
|
|
27
65
|
style("owner user ok", "yellow"),
|
|
66
|
+
style("targeted schema", "yellow"),
|
|
67
|
+
style("extensions ok", "yellow"),
|
|
28
68
|
]
|
|
29
69
|
]
|
|
30
70
|
|
|
@@ -32,30 +72,49 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
32
72
|
|
|
33
73
|
for r in results:
|
|
34
74
|
root_ok = (
|
|
35
|
-
r["root"]["rolcanlogin"]
|
|
36
|
-
and r["root"]["rolcreaterole"]
|
|
37
|
-
and r["root"]["rolinherit"]
|
|
38
|
-
) and (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"installed"
|
|
42
|
-
if "pg_stat_statements" in r["shared_preload_libraries"]
|
|
43
|
-
else "not installed"
|
|
44
|
-
)
|
|
45
|
-
pglogical = (
|
|
46
|
-
"installed"
|
|
47
|
-
if "pglogical" in r["shared_preload_libraries"]
|
|
48
|
-
else "not installed"
|
|
75
|
+
r["users"]["root"]["rolcanlogin"]
|
|
76
|
+
and r["users"]["root"]["rolcreaterole"]
|
|
77
|
+
and r["users"]["root"]["rolinherit"]
|
|
78
|
+
) and (
|
|
79
|
+
"rds_superuser" in r["users"]["root"]["memberof"]
|
|
80
|
+
or r["users"]["root"]["rolsuper"]
|
|
49
81
|
)
|
|
82
|
+
|
|
83
|
+
# Interestingly enough, we can tell if this is being run for a destination database if the compared_extensions is not None.
|
|
84
|
+
# This is because it is only set when we are ensuring all source extensions are in the destination.
|
|
85
|
+
is_dest_db = compared_extensions is not None
|
|
86
|
+
|
|
87
|
+
# If this is a destination database, we need to check if the owner can create objects.
|
|
88
|
+
|
|
89
|
+
if is_dest_db:
|
|
90
|
+
owner_ok = (r["users"]["owner"]["rolcanlogin"]) and (
|
|
91
|
+
r["users"]["owner"]["can_create"]
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
owner_ok = r["users"]["owner"]["rolcanlogin"]
|
|
95
|
+
|
|
96
|
+
shared_preload_libraries = "ok"
|
|
97
|
+
missing = []
|
|
98
|
+
if "pg_stat_statements" not in r["shared_preload_libraries"]:
|
|
99
|
+
missing.append("pg_stat_statements")
|
|
100
|
+
if "pglogical" not in r["shared_preload_libraries"]:
|
|
101
|
+
missing.append("pglogical")
|
|
102
|
+
if missing:
|
|
103
|
+
shared_preload_libraries = ", ".join(missing) + " are missing!"
|
|
104
|
+
|
|
50
105
|
summary_table.append(
|
|
51
106
|
[
|
|
52
107
|
style(r["db"], "green"),
|
|
53
108
|
style(
|
|
54
109
|
r["server_version"],
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
110
|
+
(
|
|
111
|
+
"green"
|
|
112
|
+
if float(
|
|
113
|
+
r["server_version"].rsplit(" ", 1)[0].rsplit(".", 1)[0]
|
|
114
|
+
)
|
|
115
|
+
>= 9.6
|
|
116
|
+
else "red"
|
|
117
|
+
),
|
|
59
118
|
),
|
|
60
119
|
style(
|
|
61
120
|
r["max_replication_slots"],
|
|
@@ -70,26 +129,65 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
70
129
|
"green" if int(r["max_wal_senders"]) >= 10 else "red",
|
|
71
130
|
),
|
|
72
131
|
style(
|
|
73
|
-
|
|
74
|
-
"green" if
|
|
132
|
+
shared_preload_libraries,
|
|
133
|
+
"green" if shared_preload_libraries == "ok" else "red",
|
|
75
134
|
),
|
|
76
|
-
style(pglogical, "green" if pglogical == "installed" else "red"),
|
|
77
135
|
style(
|
|
78
136
|
r["rds.logical_replication"],
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
137
|
+
(
|
|
138
|
+
"green"
|
|
139
|
+
if r["rds.logical_replication"] in ["on", "Not Applicable"]
|
|
140
|
+
else "red"
|
|
141
|
+
),
|
|
82
142
|
),
|
|
83
143
|
style(root_ok, "green" if root_ok else "red"),
|
|
84
144
|
style(owner_ok, "green" if owner_ok else "red"),
|
|
145
|
+
style(r["schema"], "green"),
|
|
85
146
|
]
|
|
86
147
|
)
|
|
87
148
|
|
|
88
|
-
|
|
89
|
-
|
|
149
|
+
# If this is a destinatino DB, we are ensuring all source extensions are in the destination.
|
|
150
|
+
# If not, we don't want this column in the table.
|
|
151
|
+
if is_dest_db:
|
|
152
|
+
extensions_ok = all(
|
|
153
|
+
[e in r["extensions"] for e in compared_extensions]
|
|
154
|
+
) and all([e in compared_extensions for e in r["extensions"]])
|
|
155
|
+
summary_table[-1].append(
|
|
156
|
+
style(extensions_ok, "green" if extensions_ok else "red")
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return summary_table
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _users_table(users: dict, is_dest_db: bool = False) -> list[list]:
|
|
163
|
+
"""
|
|
164
|
+
Takes a dict of user info and returns a table of the users for echo.
|
|
165
|
+
|
|
166
|
+
The users table alters slightly if the results are for a destination database.
|
|
167
|
+
|
|
168
|
+
users format:
|
|
169
|
+
{
|
|
170
|
+
"root": {
|
|
171
|
+
"rolname": "root",
|
|
172
|
+
"rolcanlogin": True,
|
|
173
|
+
"rolcreaterole": True,
|
|
174
|
+
"rolinherit": True,
|
|
175
|
+
"rolsuper": True,
|
|
176
|
+
"memberof": ["rds_superuser", ...]
|
|
177
|
+
},
|
|
178
|
+
"owner": {
|
|
179
|
+
"rolname": "owner",
|
|
180
|
+
"rolcanlogin": True,
|
|
181
|
+
"rolcreaterole": False,
|
|
182
|
+
"rolinherit": True,
|
|
183
|
+
"rolsuper": False,
|
|
184
|
+
"memberof": ["rds_superuser", ...]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
See pgbelt.util.postgres.precheck_info results["users"] for more info..
|
|
189
|
+
"""
|
|
90
190
|
|
|
91
|
-
# If we ran only on one db print more detailed info
|
|
92
|
-
r = results[0]
|
|
93
191
|
users_table = [
|
|
94
192
|
[
|
|
95
193
|
style("user", "yellow"),
|
|
@@ -100,38 +198,69 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
100
198
|
]
|
|
101
199
|
]
|
|
102
200
|
|
|
201
|
+
if is_dest_db:
|
|
202
|
+
users_table[0].insert(
|
|
203
|
+
5, style("can create objects in targeted schema", "yellow")
|
|
204
|
+
)
|
|
205
|
+
|
|
103
206
|
root_in_superusers = (
|
|
104
|
-
"rds_superuser" in
|
|
105
|
-
) or (
|
|
207
|
+
"rds_superuser" in users["root"]["memberof"] and users["root"]["rolinherit"]
|
|
208
|
+
) or (users["root"]["rolsuper"])
|
|
106
209
|
|
|
107
210
|
users_table.append(
|
|
108
211
|
[
|
|
109
212
|
style("root", "green"),
|
|
110
|
-
style(
|
|
213
|
+
style(users["root"]["rolname"], "green"),
|
|
111
214
|
style(
|
|
112
|
-
|
|
215
|
+
users["root"]["rolcanlogin"],
|
|
216
|
+
"green" if users["root"]["rolcanlogin"] else "red",
|
|
113
217
|
),
|
|
114
218
|
style(
|
|
115
|
-
|
|
116
|
-
"green" if
|
|
219
|
+
users["root"]["rolcreaterole"],
|
|
220
|
+
"green" if users["root"]["rolcreaterole"] else "red",
|
|
117
221
|
),
|
|
118
222
|
style(root_in_superusers, "green" if root_in_superusers else "red"),
|
|
223
|
+
style("not required", "green"),
|
|
119
224
|
]
|
|
120
225
|
)
|
|
121
226
|
|
|
122
227
|
users_table.append(
|
|
123
228
|
[
|
|
124
229
|
style("owner", "green"),
|
|
125
|
-
style(
|
|
230
|
+
style(users["owner"]["rolname"], "green"),
|
|
126
231
|
style(
|
|
127
|
-
|
|
128
|
-
"green" if
|
|
232
|
+
users["owner"]["rolcanlogin"],
|
|
233
|
+
"green" if users["owner"]["rolcanlogin"] else "red",
|
|
129
234
|
),
|
|
130
235
|
style("not required", "green"),
|
|
131
236
|
style("not required", "green"),
|
|
237
|
+
style(
|
|
238
|
+
users["owner"]["can_create"],
|
|
239
|
+
"green" if users["owner"]["can_create"] else "red",
|
|
240
|
+
),
|
|
132
241
|
]
|
|
133
242
|
)
|
|
134
243
|
|
|
244
|
+
return users_table
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _tables_table(
|
|
248
|
+
tables: list[dict], pkeys: list[dict], owner_name: str, schema_name: str
|
|
249
|
+
) -> list[list]:
|
|
250
|
+
"""
|
|
251
|
+
Takes a list of table dicts and returns a table of the tables for echo.
|
|
252
|
+
|
|
253
|
+
tables format:
|
|
254
|
+
[
|
|
255
|
+
{
|
|
256
|
+
"Name": "table_name",
|
|
257
|
+
"Schema": "schema_name",
|
|
258
|
+
"Owner": "owner_name"
|
|
259
|
+
},
|
|
260
|
+
...
|
|
261
|
+
]
|
|
262
|
+
"""
|
|
263
|
+
|
|
135
264
|
tables_table = [
|
|
136
265
|
[
|
|
137
266
|
style("table name", "yellow"),
|
|
@@ -142,10 +271,10 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
142
271
|
]
|
|
143
272
|
]
|
|
144
273
|
|
|
145
|
-
for t in
|
|
146
|
-
can_replicate = t["Schema"] ==
|
|
274
|
+
for t in tables:
|
|
275
|
+
can_replicate = t["Schema"] == schema_name and t["Owner"] == owner_name
|
|
147
276
|
replication = (
|
|
148
|
-
("pglogical" if t["Name"] in
|
|
277
|
+
("pglogical" if t["Name"] in pkeys else "dump and load")
|
|
149
278
|
if can_replicate
|
|
150
279
|
else "unavailable"
|
|
151
280
|
)
|
|
@@ -154,11 +283,31 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
154
283
|
style(t["Name"], "green"),
|
|
155
284
|
style(can_replicate, "green" if can_replicate else "red"),
|
|
156
285
|
style(replication, "green" if can_replicate else "red"),
|
|
157
|
-
style(t["Schema"], "green" if t["Schema"] ==
|
|
158
|
-
style(t["Owner"], "green" if t["Owner"] ==
|
|
286
|
+
style(t["Schema"], "green" if t["Schema"] == schema_name else "red"),
|
|
287
|
+
style(t["Owner"], "green" if t["Owner"] == owner_name else "red"),
|
|
159
288
|
]
|
|
160
289
|
)
|
|
161
290
|
|
|
291
|
+
return tables_table
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _sequences_table(
|
|
295
|
+
sequences: list[dict], owner_name: str, schema_name: str
|
|
296
|
+
) -> list[list]:
|
|
297
|
+
"""
|
|
298
|
+
Takes a list of sequence dicts and returns a table of the sequences for echo.
|
|
299
|
+
|
|
300
|
+
sequences format:
|
|
301
|
+
[
|
|
302
|
+
{
|
|
303
|
+
"Name": "sequence_name",
|
|
304
|
+
"Schema": "schema_name",
|
|
305
|
+
"Owner": "owner_name"
|
|
306
|
+
},
|
|
307
|
+
...
|
|
308
|
+
]
|
|
309
|
+
"""
|
|
310
|
+
|
|
162
311
|
sequences_table = [
|
|
163
312
|
[
|
|
164
313
|
style("sequence name", "yellow"),
|
|
@@ -168,38 +317,251 @@ async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
|
168
317
|
]
|
|
169
318
|
]
|
|
170
319
|
|
|
171
|
-
for s in
|
|
172
|
-
can_replicate = s["Schema"] ==
|
|
320
|
+
for s in sequences:
|
|
321
|
+
can_replicate = s["Schema"] == schema_name and s["Owner"] == owner_name
|
|
173
322
|
sequences_table.append(
|
|
174
323
|
[
|
|
175
324
|
style(s["Name"], "green"),
|
|
176
325
|
style(can_replicate, "green" if can_replicate else "red"),
|
|
177
|
-
style(s["Schema"], "green" if s["Schema"] ==
|
|
178
|
-
style(s["Owner"], "green" if s["Owner"] ==
|
|
326
|
+
style(s["Schema"], "green" if s["Schema"] == schema_name else "red"),
|
|
327
|
+
style(s["Owner"], "green" if s["Owner"] == owner_name else "red"),
|
|
328
|
+
]
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return sequences_table
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _extensions_table(
|
|
335
|
+
source_extensions: list[str], destination_extensions: list[str]
|
|
336
|
+
) -> list[list]:
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
Takes a list of source and destination extensions and returns a table of the extensions for echo.
|
|
340
|
+
It will flag any extensions that are not in the destination database but are in the source database.
|
|
341
|
+
|
|
342
|
+
<source/destination>_extensions format:
|
|
343
|
+
[
|
|
344
|
+
"uuid-ossp",
|
|
345
|
+
...
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
extensions_table = [
|
|
351
|
+
[
|
|
352
|
+
style("extension in source DB", "yellow"),
|
|
353
|
+
style("is in destination", "yellow"),
|
|
354
|
+
]
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
for e in source_extensions:
|
|
358
|
+
extensions_table.append(
|
|
359
|
+
[
|
|
360
|
+
style(e["extname"], "green"),
|
|
361
|
+
style(
|
|
362
|
+
e in destination_extensions,
|
|
363
|
+
"green" if e in destination_extensions else "red",
|
|
364
|
+
),
|
|
179
365
|
]
|
|
180
366
|
)
|
|
181
367
|
|
|
182
|
-
|
|
183
|
-
|
|
368
|
+
return extensions_table
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
async def _print_prechecks(results: list[dict]) -> list[list]:
|
|
372
|
+
"""
|
|
373
|
+
Print out the results of the prechecks in a human readable format.
|
|
374
|
+
If there are multiple databases, only print the summary table.
|
|
375
|
+
If there is only one database, print the summary table and more detailed info.
|
|
376
|
+
|
|
377
|
+
results format:
|
|
378
|
+
[
|
|
379
|
+
{
|
|
380
|
+
"db": "db_name",
|
|
381
|
+
"src": {
|
|
382
|
+
"server_version": "9.6.20",
|
|
383
|
+
"max_replication_slots": "10",
|
|
384
|
+
"max_worker_processes": "10",
|
|
385
|
+
"max_wal_senders": "10",
|
|
386
|
+
"pg_stat_statements": "installed",
|
|
387
|
+
"pglogical": "installed",
|
|
388
|
+
"rds.logical_replication": "on",
|
|
389
|
+
"schema: "public",
|
|
390
|
+
"users": { // See pgbelt.util.postgres.precheck_info results["users"] for more info.
|
|
391
|
+
"root": {
|
|
392
|
+
"rolname": "root",
|
|
393
|
+
"rolcanlogin": True,
|
|
394
|
+
"rolcreaterole": True,
|
|
395
|
+
"rolinherit": True,
|
|
396
|
+
"rolsuper": True,
|
|
397
|
+
"memberof": ["rds_superuser", ...]
|
|
398
|
+
},
|
|
399
|
+
"owner": {
|
|
400
|
+
"rolname": "owner",
|
|
401
|
+
"rolcanlogin": True,
|
|
402
|
+
"rolcreaterole": False,
|
|
403
|
+
"rolinherit": True,
|
|
404
|
+
"rolsuper": False,
|
|
405
|
+
"memberof": ["rds_superuser", ...],
|
|
406
|
+
"can_create": True
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
"dst": {
|
|
411
|
+
"server_version": "9.6.20",
|
|
412
|
+
"max_replication_slots": "10",
|
|
413
|
+
"max_worker_processes": "10",
|
|
414
|
+
"max_wal_senders": "10",
|
|
415
|
+
"pg_stat_statements": "installed",
|
|
416
|
+
"pglogical": "installed",
|
|
417
|
+
"rds.logical_replication": "on",
|
|
418
|
+
"schema: "public",
|
|
419
|
+
"users": { // See pgbelt.util.postgres.precheck_info results["users"] for more info.
|
|
420
|
+
"root": {
|
|
421
|
+
"rolname": "root",
|
|
422
|
+
"rolcanlogin": True,
|
|
423
|
+
"rolcreaterole": True,
|
|
424
|
+
"rolinherit": True,
|
|
425
|
+
"rolsuper": True,
|
|
426
|
+
"memberof": ["rds_superuser", ...]
|
|
427
|
+
},
|
|
428
|
+
"owner": {
|
|
429
|
+
"rolname": "owner",
|
|
430
|
+
"rolcanlogin": True,
|
|
431
|
+
"rolcreaterole": False,
|
|
432
|
+
"rolinherit": True,
|
|
433
|
+
"rolsuper": False,
|
|
434
|
+
"memberof": ["rds_superuser", ...],
|
|
435
|
+
"can_create": True
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
...
|
|
441
|
+
]
|
|
442
|
+
"""
|
|
443
|
+
|
|
444
|
+
src_summaries = []
|
|
445
|
+
dst_summaries = []
|
|
446
|
+
for r in results:
|
|
447
|
+
src_summaries.append(r["src"])
|
|
448
|
+
dst_summaries.append(r["dst"])
|
|
449
|
+
|
|
450
|
+
src_summary_table = _summary_table(src_summaries)
|
|
451
|
+
dst_summary_table = _summary_table(
|
|
452
|
+
dst_summaries, compared_extensions=r["src"]["extensions"]
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
if len(results) != 1:
|
|
456
|
+
|
|
457
|
+
# For mulitple databases, we only print the summary table.
|
|
458
|
+
|
|
459
|
+
src_multi_display_string = (
|
|
460
|
+
style("\nSource DB Configuration Summary", "blue")
|
|
461
|
+
+ "\n"
|
|
462
|
+
+ tabulate(src_summary_table, headers="firstrow")
|
|
463
|
+
)
|
|
464
|
+
echo(src_multi_display_string)
|
|
465
|
+
dst_multi_display_string = (
|
|
466
|
+
style("\nDestination DB Configuration Summary", "blue")
|
|
467
|
+
+ "\n"
|
|
468
|
+
+ tabulate(dst_summary_table, headers="firstrow")
|
|
469
|
+
)
|
|
470
|
+
echo(dst_multi_display_string)
|
|
471
|
+
|
|
472
|
+
return src_multi_display_string, dst_multi_display_string
|
|
473
|
+
|
|
474
|
+
# If we ran only on one db print more detailed info
|
|
475
|
+
r = results[0]
|
|
476
|
+
|
|
477
|
+
# TODO: We should confirm the named schema exists in the database and alert the user if it does not (red in column if not found).
|
|
478
|
+
|
|
479
|
+
# Source DB Tables
|
|
480
|
+
|
|
481
|
+
src_users_table = _users_table(r["src"]["users"])
|
|
482
|
+
src_tables_table = _tables_table(
|
|
483
|
+
r["src"]["tables"],
|
|
484
|
+
r["src"]["pkeys"],
|
|
485
|
+
r["src"]["users"]["owner"]["rolname"],
|
|
486
|
+
r["src"]["schema"],
|
|
487
|
+
)
|
|
488
|
+
src_sequences_table = _sequences_table(
|
|
489
|
+
r["src"]["sequences"], r["src"]["users"]["owner"]["rolname"], r["src"]["schema"]
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
if len(src_tables_table) == 1:
|
|
493
|
+
src_tables_table = [
|
|
494
|
+
[
|
|
495
|
+
style(
|
|
496
|
+
"ALERT: Not able to find tables to replicate, check your config's 'schema_name'",
|
|
497
|
+
"red",
|
|
498
|
+
)
|
|
499
|
+
]
|
|
500
|
+
]
|
|
501
|
+
|
|
502
|
+
if len(src_sequences_table) == 1:
|
|
503
|
+
src_sequences_table = [
|
|
504
|
+
[
|
|
505
|
+
style(
|
|
506
|
+
"ALERT: Not able to find sequences to replicate, check your config's 'schema_name'",
|
|
507
|
+
"red",
|
|
508
|
+
)
|
|
509
|
+
]
|
|
510
|
+
]
|
|
511
|
+
|
|
512
|
+
source_display_string = (
|
|
513
|
+
style("\nSource DB Configuration Summary", "blue")
|
|
514
|
+
+ "\n"
|
|
184
515
|
+ "\n"
|
|
185
|
-
+ tabulate(
|
|
516
|
+
+ tabulate(src_summary_table, headers="firstrow")
|
|
186
517
|
+ "\n"
|
|
187
518
|
+ style("\nRequired Users Summary", "yellow")
|
|
188
519
|
+ "\n"
|
|
189
|
-
+ tabulate(
|
|
520
|
+
+ tabulate(src_users_table, headers="firstrow")
|
|
190
521
|
+ "\n"
|
|
191
522
|
+ style("\nTable Compatibility Summary", "yellow")
|
|
192
523
|
+ "\n"
|
|
193
|
-
+ tabulate(
|
|
524
|
+
+ tabulate(
|
|
525
|
+
src_tables_table, headers="firstrow" if len(src_tables_table) > 1 else ""
|
|
526
|
+
)
|
|
194
527
|
+ "\n"
|
|
195
528
|
+ style("\nSequence Compatibility Summary", "yellow")
|
|
196
529
|
+ "\n"
|
|
197
|
-
+ tabulate(
|
|
530
|
+
+ tabulate(
|
|
531
|
+
src_sequences_table,
|
|
532
|
+
headers="firstrow" if len(src_sequences_table) > 1 else "",
|
|
533
|
+
)
|
|
198
534
|
)
|
|
199
535
|
|
|
200
|
-
echo(
|
|
536
|
+
echo(source_display_string)
|
|
201
537
|
|
|
202
|
-
|
|
538
|
+
echo("\n" + "=" * 80)
|
|
539
|
+
|
|
540
|
+
# Destination DB Tables
|
|
541
|
+
|
|
542
|
+
dst_users_table = _users_table(r["dst"]["users"], is_dest_db=True)
|
|
543
|
+
extenstions_table = _extensions_table(
|
|
544
|
+
r["src"]["extensions"], r["dst"]["extensions"]
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
destination_display_string = (
|
|
548
|
+
style("\nDestination DB Configuration Summary", "blue")
|
|
549
|
+
+ "\n"
|
|
550
|
+
+ "\n"
|
|
551
|
+
+ tabulate(dst_summary_table, headers="firstrow")
|
|
552
|
+
+ "\n"
|
|
553
|
+
+ style("\nExtension Matchup Summary", "yellow")
|
|
554
|
+
+ "\n"
|
|
555
|
+
+ tabulate(extenstions_table, headers="firstrow")
|
|
556
|
+
+ "\n"
|
|
557
|
+
+ style("\nRequired Users Summary", "yellow")
|
|
558
|
+
+ "\n"
|
|
559
|
+
+ tabulate(dst_users_table, headers="firstrow")
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
echo(destination_display_string)
|
|
563
|
+
|
|
564
|
+
return src_summary_table, dst_summary_table
|
|
203
565
|
|
|
204
566
|
|
|
205
567
|
@run_with_configs(skip_dst=True, results_callback=_print_prechecks)
|
|
@@ -214,22 +576,53 @@ async def precheck(config_future: Awaitable[DbupgradeConfig]) -> dict:
|
|
|
214
576
|
table and sequence in the database can be replicated.
|
|
215
577
|
If a row contains any red that sequence or table can not be replicated.
|
|
216
578
|
"""
|
|
579
|
+
|
|
217
580
|
conf = await config_future
|
|
218
581
|
pools = await gather(
|
|
219
582
|
create_pool(conf.src.root_uri, min_size=1),
|
|
220
583
|
create_pool(conf.src.owner_uri, min_size=1),
|
|
584
|
+
create_pool(conf.dst.root_uri, min_size=1),
|
|
221
585
|
)
|
|
222
|
-
|
|
586
|
+
src_root_pool, src_owner_pool, dst_root_pool = pools
|
|
223
587
|
|
|
224
588
|
try:
|
|
225
589
|
src_logger = get_logger(conf.db, conf.dc, "preflight.src")
|
|
226
|
-
|
|
227
|
-
|
|
590
|
+
dst_logger = get_logger(conf.db, conf.dc, "preflight.dst")
|
|
591
|
+
|
|
592
|
+
result = {}
|
|
593
|
+
|
|
594
|
+
# Source DB Data
|
|
595
|
+
result["src"] = await precheck_info(
|
|
596
|
+
src_root_pool,
|
|
597
|
+
conf.src.root_user.name,
|
|
598
|
+
conf.src.owner_user.name,
|
|
599
|
+
conf.tables,
|
|
600
|
+
conf.sequences,
|
|
601
|
+
conf.schema_name,
|
|
602
|
+
src_logger,
|
|
228
603
|
)
|
|
229
|
-
result["
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
result["
|
|
604
|
+
result["src"]["pkeys"], _, _ = await analyze_table_pkeys(
|
|
605
|
+
src_owner_pool, conf.schema_name, src_logger
|
|
606
|
+
)
|
|
607
|
+
result["src"]["schema"] = conf.schema_name
|
|
608
|
+
|
|
609
|
+
# Destination DB Data
|
|
610
|
+
result["dst"] = await precheck_info(
|
|
611
|
+
dst_root_pool,
|
|
612
|
+
conf.dst.root_user.name,
|
|
613
|
+
conf.dst.owner_user.name,
|
|
614
|
+
conf.tables,
|
|
615
|
+
conf.sequences,
|
|
616
|
+
conf.schema_name,
|
|
617
|
+
dst_logger,
|
|
618
|
+
)
|
|
619
|
+
# No need to analyze pkeys for the destination database (we use this to determine replication method in only the forward case).
|
|
620
|
+
result["dst"]["schema"] = conf.schema_name
|
|
621
|
+
|
|
622
|
+
# The precheck view code treats "db" as the name of the database pair, not the logical dbname of the database.
|
|
623
|
+
result["src"]["db"] = conf.db
|
|
624
|
+
result["dst"]["db"] = conf.db
|
|
625
|
+
|
|
233
626
|
return result
|
|
234
627
|
finally:
|
|
235
628
|
await gather(*[p.close() for p in pools])
|