rda-python-dbms 3.0.1__py3-none-any.whl → 3.0.2__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.
- rda_python_dbms/pgchksum.py +84 -15
- {rda_python_dbms-3.0.1.dist-info → rda_python_dbms-3.0.2.dist-info}/METADATA +5 -4
- {rda_python_dbms-3.0.1.dist-info → rda_python_dbms-3.0.2.dist-info}/RECORD +7 -7
- {rda_python_dbms-3.0.1.dist-info → rda_python_dbms-3.0.2.dist-info}/WHEEL +0 -0
- {rda_python_dbms-3.0.1.dist-info → rda_python_dbms-3.0.2.dist-info}/entry_points.txt +0 -0
- {rda_python_dbms-3.0.1.dist-info → rda_python_dbms-3.0.2.dist-info}/licenses/LICENSE +0 -0
- {rda_python_dbms-3.0.1.dist-info → rda_python_dbms-3.0.2.dist-info}/top_level.txt +0 -0
rda_python_dbms/pgchksum.py
CHANGED
|
@@ -13,11 +13,17 @@
|
|
|
13
13
|
import sys
|
|
14
14
|
import re
|
|
15
15
|
import time
|
|
16
|
+
import glob
|
|
17
|
+
from os import path as op
|
|
16
18
|
from rda_python_common.pg_cmd import PgCMD
|
|
17
19
|
from rda_python_common.pg_split import PgSplit
|
|
18
20
|
|
|
19
21
|
class PgChksum(PgCMD, PgSplit):
|
|
20
22
|
|
|
23
|
+
# Quasar backup status of a file, by bfile.type and bfile.status of its bid
|
|
24
|
+
QBSTATS = (('B', 'Quasar Backup'), ('D', 'Quasar Backup&DR'),
|
|
25
|
+
('P', 'Quasar Backup Pending'), ('N', 'Not Backed Up'), ('C', 'In COLD_STORAGE'))
|
|
26
|
+
|
|
21
27
|
def __init__(self):
|
|
22
28
|
super().__init__() # initialize parent class
|
|
23
29
|
self.PGSUM = {
|
|
@@ -48,6 +54,8 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
48
54
|
'end' : 0
|
|
49
55
|
}
|
|
50
56
|
self.MISSING = {} # dsid => list of missing file names with their location
|
|
57
|
+
self.BCKSTATS = {} # bid => Quasar backup status key in QBSTATS
|
|
58
|
+
self.COLDDIRS = {} # dsid => existing COLD_STORAGE directories of the dataset
|
|
51
59
|
self.pgname = "pgchksum"
|
|
52
60
|
self.pgcmd = None
|
|
53
61
|
|
|
@@ -88,7 +96,8 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
88
96
|
print(" -b - background process and no screen output")
|
|
89
97
|
print(" -c - File count to re-evaluate checksum; for all gathered files if not specified")
|
|
90
98
|
print(" -d - delayed mode background/batch processes on RDA machines/DAV PBS nodes")
|
|
91
|
-
print(" -e - email the primary owner of each dataset a reminder listing its missing files")
|
|
99
|
+
print(" -e - email the primary owner of each dataset a reminder listing its missing files,")
|
|
100
|
+
print(" each with its Quasar backup status (W/S files)")
|
|
92
101
|
print(" -f - Re-evaluate the specify File names only")
|
|
93
102
|
print(" -i - Dataset IDs the files belong; for all files if not specified")
|
|
94
103
|
print(" -k - skip the given file count to re-evaluate checksum")
|
|
@@ -147,7 +156,8 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
147
156
|
if self.PGSUM['x']: msg += 'and Fix '
|
|
148
157
|
msg += "checksum/size/timestamp for {} {} files{}".format(fcnt, self.PVALS['TYPE'], self.PVALS['OFFSET'])
|
|
149
158
|
self.pglog(msg, self.PVALS['emlsum'])
|
|
150
|
-
cnts = {'cnt' : 0, 'pcnt' : 0, 'xcnt' : 0, 'ecnt' : 0, 'mcnt' : 0, 'ncnt' : 0, 'tcnt' : 0, 'scnt' : 0
|
|
159
|
+
cnts = {'cnt' : 0, 'pcnt' : 0, 'xcnt' : 0, 'ecnt' : 0, 'mcnt' : 0, 'ncnt' : 0, 'tcnt' : 0, 'scnt' : 0,
|
|
160
|
+
'bcnts' : {}} # Quasar backup status counts of the missing files
|
|
151
161
|
if self.PGSUM['t'] == 'W':
|
|
152
162
|
self.evaluate_webfile_checksum(fcnt, pgrecs, cnts)
|
|
153
163
|
elif self.PGSUM['t'] == 'S':
|
|
@@ -204,23 +214,28 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
204
214
|
self.PVALS['TYPE'], dsid, self.pgname, fcnt, s, s, s, s)
|
|
205
215
|
for i in range(fcnt):
|
|
206
216
|
msg += "{}. {}\n".format(i+1, files[i])
|
|
217
|
+
if self.PGSUM['t'] != 'Q':
|
|
218
|
+
msg += ("\nA file on Quasar Backup can be restored with 'dsarch {} -RQ'; a file Not Backed Up " +
|
|
219
|
+
"cannot be restored from Quasar.\n").format(dsid)
|
|
207
220
|
msg += "\n{} on {}\n".format(self.pgcmd, self.PGLOG['HOSTNAME'])
|
|
208
221
|
receiver = ', '.join(o if o.find('@') > -1 else o + "@ucar.edu" for o in owners)
|
|
209
222
|
if self.send_python_email(subject, receiver, msg, None, '', self.PVALS['emlerr']):
|
|
210
223
|
self.pglog("{}: emailed {} about {} missing {} file{}".format(dsid, receiver, fcnt, self.PVALS['TYPE'], s), self.PVALS['emllog'])
|
|
211
224
|
|
|
212
225
|
# record a missing file for the dataset owner email
|
|
213
|
-
def add_missing_file(self, fname, pgrec, location):
|
|
226
|
+
def add_missing_file(self, fname, pgrec, location, status = None):
|
|
214
227
|
if not self.PGSUM['e']: return
|
|
215
228
|
dsid = pgrec['dsid']
|
|
216
229
|
if dsid not in self.MISSING: self.MISSING[dsid] = []
|
|
217
|
-
|
|
230
|
+
line = "{} (Type {}, missing {})".format(fname, pgrec['type'], location)
|
|
231
|
+
if status: line += "; " + status
|
|
232
|
+
self.MISSING[dsid].append(line)
|
|
218
233
|
|
|
219
234
|
# get the checksum filelist for wfile
|
|
220
235
|
def get_checksum_wfilelist(self):
|
|
221
236
|
self.PVALS['TYPE'] = "Web"
|
|
222
237
|
self.PVALS['BUCKET'] = 'gdex-data'
|
|
223
|
-
flds = "wid, wfile, type, locflag, data_size, checksum, date_modified, time_modified"
|
|
238
|
+
flds = "wid, wfile, type, locflag, bid, data_size, checksum, date_modified, time_modified"
|
|
224
239
|
if self.PGSUM['f']:
|
|
225
240
|
pgrecs = self.pgmhget('wfile', 'wid, dsid', {'wfile' : self.PGSUM['f']})
|
|
226
241
|
cnt = len(pgrecs['wid']) if pgrecs else 0
|
|
@@ -275,7 +290,7 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
275
290
|
def get_checksum_sfilelist(self):
|
|
276
291
|
self.PVALS['TYPE'] = "Saved"
|
|
277
292
|
self.PVALS['BUCKET'] = 'gdex-decsdata'
|
|
278
|
-
flds = "sid, sfile, dsid, type, locflag, data_size, checksum, date_modified, time_modified"
|
|
293
|
+
flds = "sid, sfile, dsid, type, locflag, bid, data_size, checksum, date_modified, time_modified"
|
|
279
294
|
hcnd = {}
|
|
280
295
|
fcnt = self.PGSUM['c']
|
|
281
296
|
cnt = len(self.PGSUM['f'])
|
|
@@ -367,6 +382,10 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
367
382
|
if cnts['ecnt'] > 0:
|
|
368
383
|
cntmsg += "/{}".format(cnts['ecnt'])
|
|
369
384
|
chkmsg += "/Nonexist"
|
|
385
|
+
if cnts['bcnts']:
|
|
386
|
+
# e.g. [Quasar Backup 10, Not Backed Up 2]
|
|
387
|
+
chkmsg += " [{}]".format(', '.join("{} {}".format(m, cnts['bcnts'][k])
|
|
388
|
+
for (k, m) in self.QBSTATS if k in cnts['bcnts']))
|
|
370
389
|
self.pglog("{}{} {} files{} {}".format(cntmsg, ttlmsg, self.PVALS['TYPE'], self.PVALS['OFFSET'], chkmsg), logact)
|
|
371
390
|
|
|
372
391
|
# evaluate checksums for web files
|
|
@@ -419,9 +438,7 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
419
438
|
if info:
|
|
420
439
|
self.evaluate_file_status('DSK', fname, pgrec, info, cnts)
|
|
421
440
|
else:
|
|
422
|
-
self.
|
|
423
|
-
self.add_missing_file(fname, pgrec, 'on Disk')
|
|
424
|
-
cnts['ecnt'] += 1
|
|
441
|
+
self.report_missing_file(fname, pgrec, 'on Disk', cnts)
|
|
425
442
|
|
|
426
443
|
# evaluate an object file
|
|
427
444
|
def evaluate_object_file(self, fname, pgrec, bucket, cnts):
|
|
@@ -429,9 +446,7 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
429
446
|
if info:
|
|
430
447
|
self.evaluate_file_status('OBS', "{}-{}".format(bucket, fname), pgrec, info, cnts)
|
|
431
448
|
else:
|
|
432
|
-
self.
|
|
433
|
-
self.add_missing_file("{}-{}".format(bucket, fname), pgrec, 'in Object Store')
|
|
434
|
-
cnts['ecnt'] += 1
|
|
449
|
+
self.report_missing_file("{}-{}".format(bucket, fname), pgrec, 'in Object Store', cnts)
|
|
435
450
|
|
|
436
451
|
# evaluate a quasar file
|
|
437
452
|
def evaluate_quasar_file(self, fname, pgrec, endpoint, cnts):
|
|
@@ -439,10 +454,64 @@ class PgChksum(PgCMD, PgSplit):
|
|
|
439
454
|
if info:
|
|
440
455
|
self.evaluate_file_status('QSR', "{}-{}".format(endpoint, fname), pgrec, info, cnts)
|
|
441
456
|
else:
|
|
442
|
-
self.
|
|
443
|
-
self.add_missing_file("{}-{}".format(endpoint, fname), pgrec, 'on Quasar')
|
|
444
|
-
cnts['ecnt'] += 1
|
|
457
|
+
self.report_missing_file("{}-{}".format(endpoint, fname), pgrec, 'on Quasar', cnts)
|
|
445
458
|
|
|
459
|
+
# log a missing file with the Quasar backup status of a Web/Saved file, so it
|
|
460
|
+
# tells whether the file can be restored; record it for the owner email
|
|
461
|
+
def report_missing_file(self, fname, pgrec, location, cnts):
|
|
462
|
+
msg = "{}: Type {} {} file is missing {}".format(fname, pgrec['type'], self.PVALS['TYPE'], location)
|
|
463
|
+
status = None
|
|
464
|
+
if self.PGSUM['t'] != 'Q':
|
|
465
|
+
bkey = self.quasar_backup_status(pgrec['bid'])
|
|
466
|
+
cnts['bcnts'][bkey] = cnts['bcnts'].get(bkey, 0) + 1
|
|
467
|
+
status = dict(self.QBSTATS)[bkey]
|
|
468
|
+
if bkey != 'N': status += " (Backup ID {})".format(pgrec['bid'])
|
|
469
|
+
if bkey in ('B', 'D'): status = "on " + status
|
|
470
|
+
if self.PGSUM['t'] == 'S' and location == 'on Disk':
|
|
471
|
+
sfile = pgrec['sfile']
|
|
472
|
+
# an absolute path under the dataset type directory has the same cold storage
|
|
473
|
+
# path as its relative one; other absolute paths are not in cold storage
|
|
474
|
+
tdir = "{}/{}/{}/".format(self.PGLOG['DECSHOME'], pgrec['dsid'], pgrec['type'])
|
|
475
|
+
if sfile.startswith(tdir): sfile = sfile[len(tdir):]
|
|
476
|
+
cfile = None if re.match(r'^/', sfile) else self.cold_storage_file(pgrec['dsid'], pgrec['type'], sfile)
|
|
477
|
+
if cfile:
|
|
478
|
+
cnts['bcnts']['C'] = cnts['bcnts'].get('C', 0) + 1
|
|
479
|
+
status += ", Found in COLD_STORAGE " + cfile
|
|
480
|
+
msg += "; " + status
|
|
481
|
+
self.pglog(msg, self.PVALS['emllog'])
|
|
482
|
+
self.add_missing_file(fname, pgrec, location, status)
|
|
483
|
+
cnts['ecnt'] += 1
|
|
484
|
+
|
|
485
|
+
# get the Quasar backup status key of a file by its bid: B-backup, D-backup&DR once
|
|
486
|
+
# the backup file status is A, P-pending otherwise, N-not backed up without a bid
|
|
487
|
+
def quasar_backup_status(self, bid):
|
|
488
|
+
if not bid: return 'N'
|
|
489
|
+
if bid not in self.BCKSTATS:
|
|
490
|
+
pgrec = self.pgget('bfile', 'type, status', "bid = {}".format(bid), self.PVALS['emlerr'])
|
|
491
|
+
if pgrec and pgrec['status'] == 'A' and pgrec['type'] in ('B', 'D'):
|
|
492
|
+
self.BCKSTATS[bid] = pgrec['type']
|
|
493
|
+
else:
|
|
494
|
+
self.BCKSTATS[bid] = 'P'
|
|
495
|
+
return self.BCKSTATS[bid]
|
|
496
|
+
|
|
497
|
+
# find a saved file moved into GLADE cold storage, under <DECSHOME>/COLD_STORAGE/<dsid>
|
|
498
|
+
# or <DECSHOME>/cold_storage_<YYYYMMDD>/COLD_STORAGE/<dsid>, the dated ones newest first;
|
|
499
|
+
# checking a migrated file only reads its inode, it does not recall it from tape
|
|
500
|
+
def cold_storage_file(self, dsid, stype, sfile):
|
|
501
|
+
if dsid not in self.COLDDIRS:
|
|
502
|
+
dhome = self.PGLOG['DECSHOME']
|
|
503
|
+
cdirs = []
|
|
504
|
+
for cname in ('COLD_STORAGE', 'COLD_STORAGE_SINGLE_COPY'):
|
|
505
|
+
cdirs.append("{}/{}/{}".format(dhome, cname, dsid))
|
|
506
|
+
for cdir in sorted(glob.glob(dhome + "/cold_storage_*"), reverse = True):
|
|
507
|
+
for cname in ('COLD_STORAGE', 'COLD_STORAGE_SINGLE_COPY'):
|
|
508
|
+
cdirs.append("{}/{}/{}".format(cdir, cname, dsid))
|
|
509
|
+
self.COLDDIRS[dsid] = [cdir for cdir in cdirs if op.isdir(cdir)]
|
|
510
|
+
for cdir in self.COLDDIRS[dsid]:
|
|
511
|
+
cfile = "{}/{}/{}".format(cdir, stype, sfile)
|
|
512
|
+
if op.exists(cfile): return cfile
|
|
513
|
+
return None
|
|
514
|
+
|
|
446
515
|
# evaluate a file status
|
|
447
516
|
def evaluate_file_status(self, lname, fname, pgrec, info, cnts):
|
|
448
517
|
msg = ''
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_dbms
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: RDA Python Package to manage PostreSQL databases
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-dbms
|
|
@@ -33,9 +33,10 @@ current user:
|
|
|
33
33
|
- `pgdbrestore` — restore a PostgreSQL database from a local dump directory
|
|
34
34
|
- `pgchksum` — re-check archived files against their RDADB records
|
|
35
35
|
(checksums, sizes and timestamps for Web and Saved files; sizes and
|
|
36
|
-
timestamps only for Quasar backup files),
|
|
37
|
-
(
|
|
38
|
-
|
|
36
|
+
timestamps only for Quasar backup files), report the Quasar backup status
|
|
37
|
+
(and COLD_STORAGE copy for Saved files) of each missing Web/Saved file,
|
|
38
|
+
optionally fix the records (`-x`), and email each dataset's primary owner
|
|
39
|
+
a list of its missing files (`-e`)
|
|
39
40
|
- `pgcmptables` — compare the columns, indexes and data of identical tables
|
|
40
41
|
on two database servers
|
|
41
42
|
- `pgschema` — copy the tables of a schema into the same schema in another
|
|
@@ -10,7 +10,7 @@ rda_python_dbms/pg_ddllib.py,sha256=-zawllfUCe7QZNvza-u5TAYKP4yilC9K6TetHhoGenU,
|
|
|
10
10
|
rda_python_dbms/pg_schema.py,sha256=Vsf5fKENYIOgMKE3yS0TkXhg-aeHZ2nYzpvltFAcdFY,5520
|
|
11
11
|
rda_python_dbms/pg_seq.py,sha256=wNxqsyIjcTplJErPirZ-VkZ8HS4mgQUwPz8Xwd12kYA,4914
|
|
12
12
|
rda_python_dbms/pgbackup.py,sha256=IuP3nQEEVR86YlSZuGyqsdz1-O9rgET76FnWB7iL3xk,4249
|
|
13
|
-
rda_python_dbms/pgchksum.py,sha256=
|
|
13
|
+
rda_python_dbms/pgchksum.py,sha256=1ayPqhOkD6GtXa5OXNgHgUSLy_09oowTjvl2DjIbptQ,28715
|
|
14
14
|
rda_python_dbms/pgcmptables.py,sha256=LQ4KK4JuQs2z3nBdINWYgyiGu2MF9RZjS5_cAktbs6k,47123
|
|
15
15
|
rda_python_dbms/pgdbdump.py,sha256=8d-1z16Y2DyJnQqex2wFu5svChKgncPnzdQ-iUftHg8,3831
|
|
16
16
|
rda_python_dbms/pgdbrestore.py,sha256=d12hl1r6usK1oGIFAPVWUB_KkIH04PPlugxVSnLOX1g,3110
|
|
@@ -954,9 +954,9 @@ rda_python_dbms/tables/obsua/wind_5v2_0.json,sha256=NehMFE-GMNlD5Bh_fiacfSOmZWkr
|
|
|
954
954
|
rda_python_dbms/tables/obsua/wind_60v2_0.json,sha256=jOyQgD5ph7YBMuQfvYz1Ln6X_-_Vdhl36HBexOHZ1dA,1067
|
|
955
955
|
rda_python_dbms/tables/obsua/wind_8v2_0.json,sha256=5wtCThmFeOyiAGH_Qv3_jrXjhjo9b87ehlpI9QQY2SM,1064
|
|
956
956
|
rda_python_dbms/tables/obsua/wind_9v2_0.json,sha256=7OKFUicOn15LL_WuF1VE_L8DjxsHmRP4uBnopuMIeTg,1064
|
|
957
|
-
rda_python_dbms-3.0.
|
|
958
|
-
rda_python_dbms-3.0.
|
|
959
|
-
rda_python_dbms-3.0.
|
|
960
|
-
rda_python_dbms-3.0.
|
|
961
|
-
rda_python_dbms-3.0.
|
|
962
|
-
rda_python_dbms-3.0.
|
|
957
|
+
rda_python_dbms-3.0.2.dist-info/licenses/LICENSE,sha256=Uky6fiiVw7cakFVRtVpzg-mvqOQ-WcA_H8lHqhPRg58,1101
|
|
958
|
+
rda_python_dbms-3.0.2.dist-info/METADATA,sha256=na4-Kob7rwxFI7osOy_Yu6jfh_eKCRTudW5Ta4XmR6k,3399
|
|
959
|
+
rda_python_dbms-3.0.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
960
|
+
rda_python_dbms-3.0.2.dist-info/entry_points.txt,sha256=iti6iZS2OEeBqO5aHlWeMFcbuwN_2assidP0qeSkQ6E,346
|
|
961
|
+
rda_python_dbms-3.0.2.dist-info/top_level.txt,sha256=4T1ShNUMyiqmwDIuDL7pDOm_S-6zWWW3Vy6OdAzYA4c,16
|
|
962
|
+
rda_python_dbms-3.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|