rda-python-dsquasar 3.0.21__tar.gz → 3.0.23__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.
- {rda_python_dsquasar-3.0.21/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.23}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/pyproject.toml +1 -1
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/dsquasar.py +61 -17
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/dsquasar.usg +20 -4
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/dstacc.py +13 -5
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23/src/rda_python_dsquasar.egg-info}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/LICENSE +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/MANIFEST.in +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/README.md +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/setup.cfg +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/__init__.py +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/ds_quasar.py +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/taccrec.py +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/tacctar.py +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar.egg-info/SOURCES.txt +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar.egg-info/dependency_links.txt +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar.egg-info/entry_points.txt +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar.egg-info/requires.txt +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar.egg-info/top_level.txt +0 -0
- {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/test/test_dsquasar.py +0 -0
{rda_python_dsquasar-3.0.21/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.23}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_dsquasar
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.23
|
|
4
4
|
Summary: RDA Python package to backup and recover RDA data archives to and from GLOBUS Quasar backup server
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-dsquasar
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/dsquasar.py
RENAMED
|
@@ -105,7 +105,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
105
105
|
# dscheck work done, which is action independent because dcount counts GDEX files
|
|
106
106
|
# for -A 3 but tar files for -A 2/-A 4.
|
|
107
107
|
self.MAXWORKERS = 2 # default maximum concurrent workers per command (-W)
|
|
108
|
-
self.WORKGRACE =
|
|
108
|
+
self.WORKGRACE = 2*3600 # 2 hours before a running job is judged on progress
|
|
109
109
|
self.MINWPROJ = 0.9 # projected done fraction at the walltime that counts as finishing
|
|
110
110
|
self.PGBACK = {
|
|
111
111
|
'workdir' : "{}/{}/quasar_backup".format(self.PGLOG['GDEXWORK'], self.PGLOG['COMMONUSER']),
|
|
@@ -123,6 +123,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
123
123
|
'maxcnt' : 10,
|
|
124
124
|
'dolock' : 1,
|
|
125
125
|
'doemail' : 0,
|
|
126
|
+
'locflag' : None, # -L, limit the datasets gathered to one dataset.locflag
|
|
126
127
|
'starttime' : 0, # wall-clock start of the run, for the PBS walltime guard
|
|
127
128
|
'einfo' : 0, # set once a progress report is cached into dscheck.einfo
|
|
128
129
|
'eretry' : 0, # earliest retry time after a progress report failed to cache
|
|
@@ -144,7 +145,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
144
145
|
argv = sys.argv[1:]
|
|
145
146
|
for arg in argv:
|
|
146
147
|
if re.match(r'^-(h|-help)$', arg, re.I): self.show_usage('dsquasar')
|
|
147
|
-
ms = re.match(r'^-(a|b|c|d|e|E|l|m|n|t|u|w|A|B|D|W)$', arg)
|
|
148
|
+
ms = re.match(r'^-(a|b|c|d|e|E|l|m|n|t|u|w|A|B|D|L|W)$', arg)
|
|
148
149
|
if ms:
|
|
149
150
|
arg = ms.group(1)
|
|
150
151
|
if arg == 'b':
|
|
@@ -152,7 +153,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
152
153
|
elif arg in self.sopts:
|
|
153
154
|
self.sopts[arg] = 1
|
|
154
155
|
option = None
|
|
155
|
-
elif '
|
|
156
|
+
elif 'AcdLlmtwW'.find(arg) > -1:
|
|
156
157
|
option = arg
|
|
157
158
|
if arg == 'd': self.bopts = []
|
|
158
159
|
elif 'BD'.find(arg) > -1:
|
|
@@ -179,6 +180,10 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
179
180
|
if not (arg == 'Y' or arg == 'N'): self.pglog(arg +": Lock Flag(-l) must be Y or N", self.LGWNEX)
|
|
180
181
|
self.PGBACK['dolock'] = 1 if arg == 'Y' else 0
|
|
181
182
|
option = None
|
|
183
|
+
elif option == 'L':
|
|
184
|
+
if not (arg == 'G' or arg == 'O'): self.pglog(arg +": Location Flag(-L) must be G or O", self.LGWNEX)
|
|
185
|
+
self.PGBACK['locflag'] = arg
|
|
186
|
+
option = None
|
|
182
187
|
elif option == 'W':
|
|
183
188
|
self.PGBACK['maxworkers'] = int(arg)
|
|
184
189
|
if self.PGBACK['maxworkers'] < 1: self.pglog(arg +": Maximum Workers(-W) must be > 0", self.LGWNEX)
|
|
@@ -426,8 +431,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
426
431
|
dargv = dargv[0:100]
|
|
427
432
|
return self.get_dscheck("dsquasar", dargv, self.PGBACK['workdir'], self.PGLOG['CURUID'], dargx, self.LOGWRN)
|
|
428
433
|
|
|
429
|
-
# a running batch job counts as stalled once it has run longer than WORKGRACE
|
|
430
|
-
# progress so far projects to less than MINWPROJ of
|
|
434
|
+
# a running batch job counts as stalled once it has run longer than WORKGRACE, has
|
|
435
|
+
# recorded some work done, and its progress so far projects to less than MINWPROJ of
|
|
436
|
+
# its recorded work by the walltime:
|
|
431
437
|
# such a job cannot finish this attempt however long it is left alone, so an extra
|
|
432
438
|
# worker is what helps it, not more retries.
|
|
433
439
|
# the test is on the RATE, not on an absolute done fraction as it first was. a fixed
|
|
@@ -442,11 +448,15 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
442
448
|
# runs this and set_walltime_deadline reads the granted walltime back in the batch job.
|
|
443
449
|
# when the daemon caps it to a shorter queue limit the projection is optimistic, which
|
|
444
450
|
# errs towards leaving the job alone.
|
|
451
|
+
# a job that has recorded nothing done is still in its gather phases, which read as a
|
|
452
|
+
# zero rate; it is left for the next submit rather than judged. that guard is what lets
|
|
453
|
+
# WORKGRACE stay short: an extra worker can only be added by a cron submit, every 4
|
|
454
|
+
# hours, so a 6 hour grace kept it out of the first 6 to 10 hours of every attempt.
|
|
445
455
|
def worker_progress_stalled(self, pgrec):
|
|
446
456
|
if not (pgrec['pid'] and pgrec['stttime']): return False
|
|
447
457
|
if self.check_host_pid(pgrec['lockhost'], pgrec['pid']) <= 0: return False
|
|
448
458
|
elapsed = tm() - pgrec['stttime']
|
|
449
|
-
if elapsed < self.WORKGRACE: return False
|
|
459
|
+
if elapsed < self.WORKGRACE or not pgrec['dcount']: return False
|
|
450
460
|
done = pgrec['dcount']/pgrec['fcount'] if pgrec['fcount'] else 0
|
|
451
461
|
return (done*self.WALLTIME/elapsed) < self.MINWPROJ
|
|
452
462
|
|
|
@@ -918,9 +928,10 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
918
928
|
# even if PBS kills the job at the walltime. the final report follows and drops the
|
|
919
929
|
# parked one if the run does finish in time. the live email buffers are saved and put
|
|
920
930
|
# back, so the final report still carries everything logged before the cutoff.
|
|
921
|
-
#
|
|
922
|
-
#
|
|
923
|
-
#
|
|
931
|
+
# batch_term_report covers the kill itself, but only the seconds of it: SIGKILL cannot be
|
|
932
|
+
# caught, and a process stuck in uninterruptible I/O may never run the handler. this parked
|
|
933
|
+
# report is what stands between a killed job and a run that is never heard from, so it is
|
|
934
|
+
# kept as the first line of defence. every action submitted to PBS is guarded, not just the
|
|
924
935
|
# tar and transfer ones: -A 16 and the hidden -A 128 are submitted with the same 24 hour
|
|
925
936
|
# walltime and used to be excluded, so they died silently.
|
|
926
937
|
# the tar queue depths are only reported for the actions they describe. both depths are
|
|
@@ -1392,11 +1403,19 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1392
1403
|
ofile = self.join_paths(dsid, fname)
|
|
1393
1404
|
info = self.check_object_file(ofile, bucket, 1, self.ERRACT)
|
|
1394
1405
|
if not info: return False
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
if 'checksum'
|
|
1406
|
+
# an object still matching its record on size and time, with its checksum
|
|
1407
|
+
# recorded already, has nothing to evaluate: downloading it only to md5 it
|
|
1408
|
+
# again costs a transfer per file, and dsarch downloads it anyway to tar it.
|
|
1409
|
+
# a directory key such as a zarr store has no checksum, so it is always fetched
|
|
1410
|
+
if not (info['isfile'] and pgrec['checksum'] and info['data_size'] == pgrec['data_size'] and
|
|
1411
|
+
self.cmptime(info['date_modified'], info['time_modified'],
|
|
1412
|
+
pgrec['date_modified'], pgrec['time_modified']) == 0):
|
|
1413
|
+
lfile = "{}/{}/{}/{}".format(chome, self.PGLOG['BACKUPEP'], dsid, fname)
|
|
1414
|
+
# pass info on so the object is not listed a second time
|
|
1415
|
+
if not self.object_copy_local(lfile, ofile, bucket, self.ERRACT, info): return False
|
|
1416
|
+
cinfo = self.check_local_file(lfile, 32, self.ERRACT)
|
|
1417
|
+
if not cinfo: return False
|
|
1418
|
+
if 'checksum' in cinfo: info['checksum'] = cinfo['checksum']
|
|
1400
1419
|
lname = 'OBS'
|
|
1401
1420
|
else:
|
|
1402
1421
|
lfile = "{}/{}/".format(chome, dsid)
|
|
@@ -1468,13 +1487,29 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1468
1487
|
fcnt = 0
|
|
1469
1488
|
# warn per named dataset with no files; stay silent when scanning all datasets
|
|
1470
1489
|
logact = self.LOGWRN if self.dsids else 0
|
|
1490
|
+
flds = "dsid, backflag, pid, lockhost"
|
|
1491
|
+
# -L splits the work by where the data sits: an Object(O) dataset has to be staged
|
|
1492
|
+
# down from the object store and checksummed before it can be tarred, while a Glade(G)
|
|
1493
|
+
# one is only stat'ed, so the two classes run at wildly different rates and are better
|
|
1494
|
+
# off in separate runs than sharing one walltime.
|
|
1495
|
+
# G is matched as "not O" rather than "= G" on purpose: dataset.locflag is only a
|
|
1496
|
+
# summary of the per-file flags (set_dataset_locflag writes it only when every public
|
|
1497
|
+
# web file agrees, and leaves the old value otherwise), so it can hold a stale value.
|
|
1498
|
+
# as "not O" the two halves stay exhaustive and no dataset falls through both runs
|
|
1499
|
+
lcnd = ''
|
|
1500
|
+
if self.PGBACK['locflag'] == 'O':
|
|
1501
|
+
lcnd = "locflag = 'O'"
|
|
1502
|
+
elif self.PGBACK['locflag']:
|
|
1503
|
+
lcnd = "locflag <> 'O'"
|
|
1471
1504
|
if self.dsids:
|
|
1472
1505
|
pgrecs = []
|
|
1473
1506
|
for dsid in self.dsids:
|
|
1474
|
-
|
|
1507
|
+
dcnd = "dsid = '{}'".format(dsid)
|
|
1508
|
+
if lcnd: dcnd += " AND " + lcnd
|
|
1509
|
+
pgrec = self.pgget("dataset", flds, dcnd, self.LGWNEX)
|
|
1475
1510
|
if pgrec: pgrecs.append(pgrec)
|
|
1476
1511
|
else:
|
|
1477
|
-
mrecs = self.pgmget("dataset",
|
|
1512
|
+
mrecs = self.pgmget("dataset", flds, (lcnd + self.dsid_order()).strip(), self.LGWNEX)
|
|
1478
1513
|
dcnt = len(mrecs['dsid']) if mrecs else 0
|
|
1479
1514
|
pgrecs = [self.onerecord(mrecs, i) for i in range(dcnt)]
|
|
1480
1515
|
for pgrec in pgrecs:
|
|
@@ -1487,7 +1522,16 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1487
1522
|
# main runs take new files (bid = 0), so it shares no record with the worker holding
|
|
1488
1523
|
# the lock and must not skip the dataset for it
|
|
1489
1524
|
if unlock and pgrec['pid'] and not self.PGBACK['chgdays']:
|
|
1490
|
-
|
|
1525
|
+
# quiet unlock: a live holder is the normal case for a second worker, and the
|
|
1526
|
+
# library wording for it ("Cannot Unlock") reads like a failure. report the skip
|
|
1527
|
+
# here instead, in terms of the worker that is still on the dataset
|
|
1528
|
+
lstat = self.lock_dataset(dsid, 0, 0)
|
|
1529
|
+
if lstat < 1:
|
|
1530
|
+
# -1 is the live holder; 0 means the record went away or the db errored,
|
|
1531
|
+
# which lock_dataset has already logged as an error
|
|
1532
|
+
if lstat < 0:
|
|
1533
|
+
self.pglog("{}: skipped, still in use by {}/{}".format(dsid, pgrec['pid'], pgrec['lockhost']), self.LOGACT)
|
|
1534
|
+
continue
|
|
1491
1535
|
fcnt += self.get_dataset_files(dsid, dsfiles, pgrec['backflag'], logact, sizes)
|
|
1492
1536
|
if dsfiles:
|
|
1493
1537
|
s = 's' if fcnt > 1 else ''
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/dsquasar.usg
RENAMED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
file tarring and transferring is performed through the utility program dsarch.
|
|
9
9
|
|
|
10
10
|
Usage: dsquasar [-b] [(-a | -t DatasetIDs)] [-c ChangeDays] [-(e|E)] \
|
|
11
|
-
[-(B|D)] [-A ActionBits] [-l (Y|N)]
|
|
12
|
-
[-n] [-u] [-d [HostName] [TryCount]] [-h]
|
|
11
|
+
[-(B|D)] [-L (G|O)] [-A ActionBits] [-l (Y|N)] \
|
|
12
|
+
[-m ProcessCount] [-n] [-u] [-d [HostName] [TryCount]] [-h]
|
|
13
13
|
|
|
14
14
|
Either -a or -t must be given (usage is displayed otherwise). dsquasar runs
|
|
15
15
|
from the Quasar backup work directory; it changes there automatically.
|
|
@@ -24,6 +24,21 @@
|
|
|
24
24
|
'%' is allowed and is expanded against the dataset table. Ignored,
|
|
25
25
|
with a warning, when -a is also given.
|
|
26
26
|
|
|
27
|
+
-L (G|O)
|
|
28
|
+
Limit the run by dataset.locflag: O takes the datasets on the object
|
|
29
|
+
store, G takes everything else. Applies to -a and to -t alike. The
|
|
30
|
+
two classes back up at very different rates: an O dataset has every
|
|
31
|
+
file staged down from the object store and checksummed before it can
|
|
32
|
+
be tarred, while a G dataset is only stat'ed. Running them in one job
|
|
33
|
+
lets the slow class eat the whole walltime, so give them separate
|
|
34
|
+
runs, e.g. one submit with -L G and another with -L O. Defaults to
|
|
35
|
+
all datasets.
|
|
36
|
+
|
|
37
|
+
-L G and -L O together always cover every dataset, so a pair of runs
|
|
38
|
+
loses nothing. The split is by dataset, though, and dataset.locflag
|
|
39
|
+
is only a summary of the per-file flags, so a dataset holding files of
|
|
40
|
+
both kinds is backed up entirely by whichever run claims it.
|
|
41
|
+
|
|
27
42
|
|
|
28
43
|
BACKUP SCOPE
|
|
29
44
|
|
|
@@ -93,8 +108,9 @@
|
|
|
93
108
|
-W MaxWorkers
|
|
94
109
|
Maximum number of concurrent batch workers for the same command.
|
|
95
110
|
Defaults to 2. A repeat delayed submit normally blocks while the
|
|
96
|
-
batch job is running; if that job has run for more than
|
|
97
|
-
has
|
|
111
|
+
batch job is running; if that job has run for more than 2 hours,
|
|
112
|
+
has recorded some work done, and at its rate so far would finish
|
|
113
|
+
less than 90% of its recorded work within the walltime, it is
|
|
98
114
|
treated as stalled and an extra worker is submitted instead, up to
|
|
99
115
|
this limit. Extra workers rely on dataset locking to stay off each
|
|
100
116
|
other's files, so this is pinned to 1 with -l N, and also for the
|
|
@@ -861,11 +861,19 @@ class DsTACC(PgCMD, PgSplit):
|
|
|
861
861
|
ofile = self.join_paths(dsid, fname)
|
|
862
862
|
info = self.check_object_file(ofile, bucket, 1, self.ERRACT)
|
|
863
863
|
if not info: return False
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if 'checksum'
|
|
864
|
+
# an object still matching its record on size and time, with its checksum
|
|
865
|
+
# recorded already, has nothing to evaluate: downloading it only to md5 it
|
|
866
|
+
# again costs a transfer per file, and dsarch downloads it anyway to tar it.
|
|
867
|
+
# a directory key such as a zarr store has no checksum, so it is always fetched
|
|
868
|
+
if not (info['isfile'] and pgrec['checksum'] and info['data_size'] == pgrec['data_size'] and
|
|
869
|
+
self.cmptime(info['date_modified'], info['time_modified'],
|
|
870
|
+
pgrec['date_modified'], pgrec['time_modified']) == 0):
|
|
871
|
+
lfile = "{}/{}/{}/{}".format(chome, self.PGLOG['BACKUPEP'], dsid, fname)
|
|
872
|
+
# pass info on so the object is not listed a second time
|
|
873
|
+
if not self.object_copy_local(lfile, ofile, bucket, self.ERRACT, info): return False
|
|
874
|
+
cinfo = self.check_local_file(lfile, 32, self.ERRACT)
|
|
875
|
+
if not cinfo: return False
|
|
876
|
+
if 'checksum' in cinfo: info['checksum'] = cinfo['checksum']
|
|
869
877
|
lname = 'OBS'
|
|
870
878
|
else:
|
|
871
879
|
lfile = "{}/{}/".format(chome, dsid)
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23/src/rda_python_dsquasar.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_dsquasar
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.23
|
|
4
4
|
Summary: RDA Python package to backup and recover RDA data archives to and from GLOBUS Quasar backup server
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-dsquasar
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/__init__.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/ds_quasar.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/taccrec.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.23}/src/rda_python_dsquasar/tacctar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|