rda-python-dsquasar 3.0.1__tar.gz → 3.0.3__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.3/MANIFEST.in +1 -0
- {rda_python_dsquasar-3.0.1/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.3}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/pyproject.toml +1 -1
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/dsquasar.py +182 -116
- rda_python_dsquasar-3.0.3/src/rda_python_dsquasar/dsquasar.usg +128 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3/src/rda_python_dsquasar.egg-info}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar.egg-info/SOURCES.txt +2 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/LICENSE +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/README.md +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/setup.cfg +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/__init__.py +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/ds_quasar.py +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/dstacc.py +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/taccrec.py +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/tacctar.py +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar.egg-info/dependency_links.txt +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar.egg-info/entry_points.txt +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar.egg-info/requires.txt +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar.egg-info/top_level.txt +0 -0
- {rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/test/test_dsquasar.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include src/rda_python_dsquasar/*.usg
|
{rda_python_dsquasar-3.0.1/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.3}/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.3
|
|
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
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# to gather Web and Saved bfiles, tar them into larger (> 2GB) bfiles, and
|
|
13
13
|
# back them up onto Quasar Backup Servers, at the Globus end points of
|
|
14
14
|
# NCAR GDEX Quasar and/or NCAR GDEX Quasar Drdata, for Backup and/or
|
|
15
|
-
#
|
|
15
|
+
# disaster recovery copies, respectively. The file tarring and backing up
|
|
16
16
|
# processes are completed in utility program dsarch.
|
|
17
17
|
#
|
|
18
18
|
# Github : https://github.com/NCAR/rda-python-dsquasar.git
|
|
@@ -84,6 +84,10 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
84
84
|
self.ONESIZE = 20*self.PGLOG['ONEGBS'] # 20GB, minimal file size to tar a single file
|
|
85
85
|
self.TFCOUNT = 100 # if file count is greater, use self.MINSIZE for tar file
|
|
86
86
|
self.SUBLMTS = 2000 # file count limit for a sub-group
|
|
87
|
+
self.MPLIMIT = 500 # tar file count per batch process for multi-processing
|
|
88
|
+
self.MPMAX = 8 # maximum number of batch processes for multi-processing
|
|
89
|
+
self.MAXRUNTIME = 23*3600 # 23 hours; stop before the 24-hour PBS walltime
|
|
90
|
+
self.ONEHOUR = 3600 # seconds; time headroom needed to finish before walltime
|
|
87
91
|
self.PGBACK = {
|
|
88
92
|
'workdir' : "{}/{}/quasar_backup".format(self.PGLOG['GDEXWORK'], self.PGLOG['COMMONUSER']),
|
|
89
93
|
'mproc' : 1,
|
|
@@ -97,6 +101,8 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
97
101
|
'maxcnt' : 10,
|
|
98
102
|
'dolock' : 1,
|
|
99
103
|
'doemail' : 0,
|
|
104
|
+
'starttime' : 0, # wall-clock start of the run, for the PBS walltime guard
|
|
105
|
+
'tardone' : 0, # tar files dispatched so far, for the finish-rate estimate
|
|
100
106
|
'cmd' : None
|
|
101
107
|
}
|
|
102
108
|
self.dsids = []
|
|
@@ -105,12 +111,14 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
105
111
|
self.dstart = 0
|
|
106
112
|
|
|
107
113
|
# read in command line parameters
|
|
108
|
-
def read_parameters(self):
|
|
114
|
+
def read_parameters(self):
|
|
109
115
|
self.PGLOG['LOGFILE'] = "dsquasar.log" # set different log file
|
|
116
|
+
self.set_help_path(__file__)
|
|
110
117
|
self.dssdb_dbname()
|
|
111
118
|
option = None
|
|
112
119
|
argv = sys.argv[1:]
|
|
113
120
|
for arg in argv:
|
|
121
|
+
if re.match(r'^-(h|-help)$', arg, re.I): self.show_usage('dsquasar')
|
|
114
122
|
ms = re.match(r'^-(a|b|c|d|e|E|l|m|n|t|u|A|B|D)$', arg)
|
|
115
123
|
if ms:
|
|
116
124
|
arg = ms.group(1)
|
|
@@ -149,32 +157,13 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
149
157
|
self.add_to_dsids(arg)
|
|
150
158
|
else:
|
|
151
159
|
self.pglog(arg + ": Value without leading option", self.LGWNEX)
|
|
152
|
-
if not (self.sopts['a'] or self.dsids):
|
|
153
|
-
print("dsquasar [-b] [(-a|-t DatasetIDs)] [-c ChangeDays] [-(e|E)] [-(B|D)] [-A ActionBits] \\")
|
|
154
|
-
print(" [-l (Y|N)] [-m ProcessCount] [-n] [-u] [-d [HostName] [TryCount]")
|
|
155
|
-
print(" Option -a - gather files of all datasets for Quasar Backup, or")
|
|
156
|
-
print(" Option -t - specify dataset IDs to backup files, wildcard % is allowed")
|
|
157
|
-
print(" Option -b - turn the background mode on; no screen display")
|
|
158
|
-
print(" Option -c - change days to work with option -A 1 to rebackup changed files")
|
|
159
|
-
print(" Option -d - turn the delay mode on for running as a batch job")
|
|
160
|
-
print(" Option -e - send an email to specialist for statistics")
|
|
161
|
-
print(" Option -E - send an email to specialist for detail statistics")
|
|
162
|
-
print(" Option -B - Quasar Backup only for the provided datasets, or")
|
|
163
|
-
print(" Option -D - Quasar Backup&Drdata; default to both B & D")
|
|
164
|
-
print(" Option -A - Action bits, 1 - create dsarch input files; 2 - build tarfiles;")
|
|
165
|
-
print(" 4 - Transfer Quasar backup files; 8 - Check Backup Files;")
|
|
166
|
-
print(" 16 - Dump Backup Statistics; default to 3 for 1+2 actions,")
|
|
167
|
-
print(" and the valid values are 1,2,3,4,6,7,8,16")
|
|
168
|
-
print(" Option -l - flag to check&lock dataset before backup processes, defaults to Y")
|
|
169
|
-
print(" Option -m - number of concurrent processes, defaults to 1")
|
|
170
|
-
print(" Option -n - gather and show the available file counts with no backup actions")
|
|
171
|
-
print(" Option -u - to clean up backup locks for provided dataset IDs")
|
|
172
|
-
sys.exit(0)
|
|
160
|
+
if not (self.sopts['a'] or self.dsids): self.show_usage('dsquasar')
|
|
173
161
|
self.PGBACK['cmd'] = "dsquasar {}".format(' '.join(argv))
|
|
174
162
|
|
|
175
163
|
# do quasar backup process
|
|
176
164
|
def start_actions(self):
|
|
177
165
|
self.cmdlog(self.PGBACK['cmd'])
|
|
166
|
+
self.PGBACK['starttime'] = tm()
|
|
178
167
|
if self.sopts['u']:
|
|
179
168
|
if self.sopts['a']: self.pglog("-u: Dataset IDs must be provided to Unlock datasets", self.LOGWRN)
|
|
180
169
|
self.unlock_datasets()
|
|
@@ -331,6 +320,37 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
331
320
|
self.backup_dataset_tarfiles(dsfiles, 'B')
|
|
332
321
|
self.backup_dataset_tarfiles(dsfiles, 'D')
|
|
333
322
|
|
|
323
|
+
# size up the number of batch processes from the number of tar files that are the
|
|
324
|
+
# unit of parallel work: new infiles CINACT will create this run (estimated from the
|
|
325
|
+
# total bytes of GDEX files ready to back up, ~one tar per TARSIZE) plus existing
|
|
326
|
+
# status 'N' infile records to build (TARACT) and status 'T' tar records to transfer
|
|
327
|
+
# (BCKACT). scales one process per MPLIMIT tar files, capped at MPMAX. create-infile
|
|
328
|
+
# only (-A 1) is not a batch action (see NBACTS) so it never reaches here and stays
|
|
329
|
+
# single process.
|
|
330
|
+
def batch_process_count(self, acts):
|
|
331
|
+
tcnt = 0
|
|
332
|
+
if acts&self.CINACT:
|
|
333
|
+
sizes = [0]
|
|
334
|
+
if self.gather_dataset_files(None, False, sizes):
|
|
335
|
+
tcnt += max(1, (sizes[0] + self.TARSIZE - 1)//self.TARSIZE)
|
|
336
|
+
if acts&self.TARACT: tcnt += self.batch_tar_count('N')
|
|
337
|
+
if acts&self.BCKACT: tcnt += self.batch_tar_count('T')
|
|
338
|
+
if tcnt <= self.MPLIMIT: return 1
|
|
339
|
+
mproc = (tcnt + self.MPLIMIT - 1)//self.MPLIMIT
|
|
340
|
+
if mproc > self.MPMAX: mproc = self.MPMAX
|
|
341
|
+
return mproc
|
|
342
|
+
|
|
343
|
+
# count the bfile tar records of a given status honoring backflag/dataset scope
|
|
344
|
+
def batch_tar_count(self, status):
|
|
345
|
+
bcnd = "status = '{}'".format(status)
|
|
346
|
+
if self.PGBACK['backflag']: bcnd += " AND type = '{}'".format(self.PGBACK['backflag'])
|
|
347
|
+
if self.dsids:
|
|
348
|
+
tcnt = 0
|
|
349
|
+
for dsid in self.dsids:
|
|
350
|
+
tcnt += self.pgget('bfile', '', "dsid = '{}' AND {}".format(dsid, bcnd), self.LGWNEX)
|
|
351
|
+
return tcnt
|
|
352
|
+
return self.pgget('bfile', '', bcnd, self.LGWNEX)
|
|
353
|
+
|
|
334
354
|
# start none daemon and intialize dscheck counts
|
|
335
355
|
def start_dsquasar_none_daemon(self, act, fcnt = 0):
|
|
336
356
|
acts = self.PGBACK['action']
|
|
@@ -338,11 +358,39 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
338
358
|
dsid = self.dsids[0] if len(self.dsids) == 1 else ''
|
|
339
359
|
if not re.match(r'^[a-z]\d{6}$', dsid): dsid = ''
|
|
340
360
|
cact = ('A' if acts < 10 else '') + str(acts)
|
|
341
|
-
|
|
342
|
-
|
|
361
|
+
# only the initial delayed submit (not yet under PBS) sets qoptions for the batch
|
|
362
|
+
# job the dscheck daemon will start; size up multi-processing from the detail file
|
|
363
|
+
# count (unless an explicit -m N was given) and reserve matching PBS cpus. the
|
|
364
|
+
# invocation argv is left unchanged so a repeat submit with the same argv still
|
|
365
|
+
# blocks as a duplicate. a command-line or the PBS batch run does not set qoptions.
|
|
366
|
+
if self.PGLOG['CURBID'] < 1 and (act == self.STTACT or acts&self.NBACTS != acts):
|
|
367
|
+
# check for a duplicate command first (same lookup key init_dscheck builds);
|
|
368
|
+
# only size up multi-processing - which checks the backup files - when no
|
|
369
|
+
# matching dscheck record is on file yet, so a duplicate submit blocks without
|
|
370
|
+
# checking the backup files.
|
|
371
|
+
dargv = self.argv_to_string(sys.argv[1:], 0, "Process in Delayed Mode")
|
|
372
|
+
dargx = None
|
|
373
|
+
if len(dargv) > 100:
|
|
374
|
+
dargx = dargv[100:]
|
|
375
|
+
dargv = dargv[0:100]
|
|
376
|
+
if not self.get_dscheck("dsquasar", dargv, self.PGBACK['workdir'], self.PGLOG['CURUID'], dargx, self.LOGWRN):
|
|
377
|
+
qoptions = '-l walltime=24:00:00'
|
|
378
|
+
if acts&self.NBACTS != acts:
|
|
379
|
+
if self.PGBACK['mproc'] < 2: self.PGBACK['mproc'] = self.batch_process_count(acts)
|
|
380
|
+
if self.PGBACK['mproc'] > 1:
|
|
381
|
+
qoptions += ",select=1:ncpus={0}:mem={0}gb".format(self.PGBACK['mproc'])
|
|
382
|
+
self.set_one_boption('qoptions', qoptions, 1)
|
|
343
383
|
self.init_dscheck(0, '', "dsquasar", dsid, cact, self.PGBACK['workdir'],
|
|
344
384
|
self.PGLOG['CURUID'], self.bopts, self.LOGWRN)
|
|
345
|
-
|
|
385
|
+
# on the PBS batch run, take the process count from the reserved ncpus in qoptions
|
|
386
|
+
# instead of recomputing it
|
|
387
|
+
if self.PGLOG['DSCHECK'] and self.PGBACK['mproc'] < 2 and acts&self.NBACTS != acts:
|
|
388
|
+
pgrec = self.pgget('dscheck', 'qoptions',
|
|
389
|
+
"cindex = {}".format(self.PGLOG['DSCHECK']['cindex']), self.LGEREX)
|
|
390
|
+
if pgrec and pgrec['qoptions']:
|
|
391
|
+
ms = re.search(r'ncpus=(\d+)', pgrec['qoptions'])
|
|
392
|
+
if ms: self.PGBACK['mproc'] = int(ms.group(1))
|
|
393
|
+
if self.PGBACK['mproc'] > 1 and acts&self.NBACTS == acts: self.PGBACK['mproc'] = 1
|
|
346
394
|
elif self.PGBACK['mproc'] > 1 and acts&self.NBACTS == acts:
|
|
347
395
|
self.PGBACK['mproc'] = 1
|
|
348
396
|
self.start_none_daemon('dsquasar', '', self.PGLOG['CURUID'], self.PGBACK['mproc'], 60, 1)
|
|
@@ -409,7 +457,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
409
457
|
dmsg = list(bfiles)[0] if dcnt == 1 else "{} datasets".format(dcnt)
|
|
410
458
|
self.pglog("{}: {} for {} ...".format(amsg, bmsg, dmsg), self.WARNLG)
|
|
411
459
|
qinfo = {'backflag' : backflag, 'bid' : self.current_bid(), 'dsids' : [], 'fcnt' : 0,
|
|
412
|
-
'size' : 0, 'infiles' : [], 'instr' : '', 'qdsids' : [],
|
|
460
|
+
'size' : 0, 'infiles' : [], 'instr' : '', 'qdsids' : [], 'abids' : [],
|
|
413
461
|
'dslocks' : [], 'qfcnt' : 0, 'qsize' : 0, 'qcnt' : 0}
|
|
414
462
|
for dsid in bfiles:
|
|
415
463
|
if len(qinfo['dsids']) == self.DSCNT: self.process_one_backup_file(qinfo, True, False)
|
|
@@ -427,6 +475,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
427
475
|
self.lock_dataset(dsid, 0, self.LGEREX)
|
|
428
476
|
qinfo['dslocks'].remove(dsid)
|
|
429
477
|
qinfo['dsids'] = []
|
|
478
|
+
if self.PGBACK['mproc'] > 1:
|
|
479
|
+
self.check_child(None, 0, self.LOGWRN, 1) # wait all child processes done
|
|
480
|
+
self.confirm_quasar_counts(qinfo, qinfo['abids'], "status = 'T'") # recount confirmed backups from RDADB
|
|
430
481
|
if qinfo['dslocks']:
|
|
431
482
|
for dsid in qinfo['dslocks']:
|
|
432
483
|
self.lock_dataset(dsid, 0, self.LGEREX) # unlock the locked datasets
|
|
@@ -475,7 +526,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
475
526
|
bmsg = self.BACKMSG[backflag]
|
|
476
527
|
s = 's' if bcnt > 1 else ''
|
|
477
528
|
self.pglog("{}: {} {} file{}...".format(amsg, bcnt, bmsg, s), self.WARNLG)
|
|
478
|
-
qinfo = {'backflag' : backflag, 'bid' : 0, 'dsids' : [], 'fcnt' : 0, 'size' : 0,
|
|
529
|
+
qinfo = {'backflag' : backflag, 'bid' : 0, 'dsids' : [], 'fcnt' : 0, 'size' : 0, 'abids' : [],
|
|
479
530
|
'infiles' : [], 'instr' : '', 'qdsids' : [], 'qfcnt' : 0, 'qsize' : 0, 'qcnt' : 0}
|
|
480
531
|
for dsid in bfiles:
|
|
481
532
|
if self.PGBACK['dolock'] and self.lock_dataset(dsid, 1, self.LOGERR) < 1: continue
|
|
@@ -485,6 +536,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
485
536
|
for bkey in binfo: qinfo[bkey] = binfo[bkey]
|
|
486
537
|
self.process_one_backup_file(qinfo, False)
|
|
487
538
|
if self.PGBACK['dolock']: self.lock_dataset(dsid, 0, self.LOGERR)
|
|
539
|
+
if self.PGBACK['mproc'] > 1:
|
|
540
|
+
self.check_child(None, 0, self.LOGWRN, 1) # wait all child processes done
|
|
541
|
+
self.confirm_quasar_counts(qinfo, qinfo['abids'], "status = 'T'") # recount confirmed backups from RDADB
|
|
488
542
|
qcnt = qinfo['qcnt']
|
|
489
543
|
if qcnt > 0:
|
|
490
544
|
self.PGBACK['bckcnt'] += qcnt
|
|
@@ -522,6 +576,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
522
576
|
if qinfo['size'] < self.BCKSIZE: continue
|
|
523
577
|
self.transfer_quasar_tarfiles(qinfo)
|
|
524
578
|
if qinfo['size'] > 0: self.transfer_quasar_tarfiles(qinfo)
|
|
579
|
+
if self.PGBACK['mproc'] > 1:
|
|
580
|
+
self.check_child(None, 0, self.LOGWRN, 1) # wait all child processes done
|
|
581
|
+
self.confirm_quasar_counts(qinfo, list(bfiles), "status = 'A'") # recount confirmed backups from RDADB
|
|
525
582
|
qcnt = qinfo['qcnt']
|
|
526
583
|
if qcnt > 0:
|
|
527
584
|
self.PGBACK['bckcnt'] += qcnt
|
|
@@ -562,11 +619,67 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
562
619
|
if self.PGBACK['pstep']: self.record_dscheck_status("F")
|
|
563
620
|
self.pgexit(0)
|
|
564
621
|
|
|
622
|
+
# guard the -A 3 (Create Input&Tar, build status 'N' infiles) and -A 4 (Transfer,
|
|
623
|
+
# send status 'T' tars) PBS batch jobs against the 24-hour walltime: once past
|
|
624
|
+
# MAXRUNTIME, if the tar files still to process cannot finish within the last hour at
|
|
625
|
+
# the rate achieved so far, send a progress email and stop cleanly so the report is
|
|
626
|
+
# not lost to a PBS timeout; the remaining records are left for the next scheduled run
|
|
627
|
+
# to resume. a no-op for command-line runs, other actions, or before the cutoff.
|
|
628
|
+
def check_batch_deadline(self, qinfo):
|
|
629
|
+
act = self.PGBACK['action']
|
|
630
|
+
if self.PGLOG['CURBID'] < 1 or act not in (self.CTACTS, self.BCKACT): return
|
|
631
|
+
elapsed = tm() - self.PGBACK['starttime']
|
|
632
|
+
if elapsed < self.MAXRUNTIME: return
|
|
633
|
+
status, work = ('N', 'build') if act == self.CTACTS else ('T', 'transfer')
|
|
634
|
+
remaining = self.batch_tar_count(status)
|
|
635
|
+
if remaining < 1: return
|
|
636
|
+
done = self.PGBACK['tardone']
|
|
637
|
+
need = remaining*elapsed/done if done > 0 else elapsed
|
|
638
|
+
if need <= self.ONEHOUR: return # enough time left to finish before the walltime
|
|
639
|
+
if self.PGBACK['mproc'] > 1: self.check_child(None, 0, self.LOGWRN, 1) # wait all children
|
|
640
|
+
if qinfo and qinfo.get('dslocks'):
|
|
641
|
+
for dsid in qinfo['dslocks']: self.lock_dataset(dsid, 0, self.LGEREX)
|
|
642
|
+
etime = self.seconds_to_string_time(int(elapsed))
|
|
643
|
+
amsg = self.ACTMSG[act]
|
|
644
|
+
msg = "{}: Stopped after {} with {} tar file(s) still to {} - not enough time to finish before the 24-hour PBS walltime; the next scheduled run will resume".format(amsg, etime, remaining, work)
|
|
645
|
+
self.pglog(self.INDENT + msg, self.LOGACT)
|
|
646
|
+
if self.PGBACK['doemail']:
|
|
647
|
+
bmsg = self.BACKMSG[self.PGBACK['backflag']] if self.PGBACK['backflag'] else 'backup'
|
|
648
|
+
self.set_email("{}: {} - stopped early before the PBS walltime with {} {} tar file(s) remaining!".format(self.PGBACK['cmd'], amsg, remaining, bmsg), self.EMLTOP)
|
|
649
|
+
title = "dsquasar: {} stopped early ({} remaining)".format(amsg, remaining)
|
|
650
|
+
if self.PGBACK['errcnt']: title += " Error({})".format(self.PGBACK['errcnt'])
|
|
651
|
+
if self.PGLOG['DSCHECK']:
|
|
652
|
+
self.build_customized_email("dscheck", "einfo", "cindex = {}".format(self.PGLOG['DSCHECK']['cindex']), title, self.LOGWRN)
|
|
653
|
+
else:
|
|
654
|
+
self.pglog(title, self.LOGWRN|self.SNDEML)
|
|
655
|
+
if self.PGBACK['pstep']: self.record_dscheck_status("D")
|
|
656
|
+
self.pgexit(0)
|
|
657
|
+
|
|
658
|
+
# recompute the confirmed backup counts from RDADB after all child processes
|
|
659
|
+
# finished, so a multi-process summary reflects succeeded (not just started) work
|
|
660
|
+
def confirm_quasar_counts(self, qinfo, bids, dcnd):
|
|
661
|
+
qinfo['qcnt'] = qinfo['qfcnt'] = qinfo['qsize'] = 0
|
|
662
|
+
qinfo['qdsids'] = []
|
|
663
|
+
if not bids: return
|
|
664
|
+
cnd = "{} AND bid IN ({})".format(dcnd, ','.join(str(bid) for bid in bids))
|
|
665
|
+
pgrecs = self.pgmget('bfile', "dsid, dsids, data_size size, (scount + wcount) fcnt", cnd, self.LGEREX)
|
|
666
|
+
bcnt = len(pgrecs['dsid']) if pgrecs else 0
|
|
667
|
+
for i in range(bcnt):
|
|
668
|
+
qinfo['qcnt'] += 1
|
|
669
|
+
qinfo['qfcnt'] += pgrecs['fcnt'][i]
|
|
670
|
+
qinfo['qsize'] += pgrecs['size'][i]
|
|
671
|
+
dsid = pgrecs['dsid'][i]
|
|
672
|
+
if dsid not in qinfo['qdsids']: qinfo['qdsids'].append(dsid)
|
|
673
|
+
if pgrecs['dsids'][i]:
|
|
674
|
+
for did in pgrecs['dsids'][i].split(','):
|
|
675
|
+
if did and did not in qinfo['qdsids']: qinfo['qdsids'].append(did)
|
|
676
|
+
|
|
565
677
|
# backup one Quasar Backup or Backup&Drdata from one or multiple inputs and,
|
|
566
678
|
# reset the quasar backup dict
|
|
567
679
|
def process_one_backup_file(self, qinfo, addback, keepid = False):
|
|
568
680
|
ccnt = self.check_child(None, 0, self.LOGWRN, -1) if self.PGBACK['mproc'] > 1 else 0
|
|
569
681
|
if self.PGSIG['QUIT']: self.quit_dsquasar(qinfo)
|
|
682
|
+
self.check_batch_deadline(qinfo)
|
|
570
683
|
dsids = qinfo['dsids']
|
|
571
684
|
dcnt = len(dsids)
|
|
572
685
|
if dcnt == 0: return
|
|
@@ -580,6 +693,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
580
693
|
else:
|
|
581
694
|
bid = qinfo['bid']
|
|
582
695
|
qfile = qinfo['bfile']
|
|
696
|
+
if 'abids' in qinfo: qinfo['abids'].append(bid) # track dispatched bids for confirmed recount
|
|
583
697
|
stat = 1
|
|
584
698
|
if self.PGBACK['action'] > self.CINACT:
|
|
585
699
|
s = 's' if fcnt > 1 else ''
|
|
@@ -601,7 +715,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
601
715
|
for infile in qinfo['infiles']: self.delete_local_file(infile)
|
|
602
716
|
elif re.search(r'file backed up to', self.PGLOG['SYSERR']):
|
|
603
717
|
if self.pgdel('bfile', f"bid = {bid}"):
|
|
604
|
-
self.pglog(f"{dsid}-{qfile}: backup tarfile deleted for
|
|
718
|
+
self.pglog(f"{dsid}-{qfile}: backup tarfile deleted for duplication", self.DTLACT)
|
|
605
719
|
for infile in qinfo['infiles']: self.delete_local_file(infile)
|
|
606
720
|
sys.exit(0) # stop child process
|
|
607
721
|
else:
|
|
@@ -614,7 +728,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
614
728
|
for infile in qinfo['infiles']: self.delete_local_file(infile)
|
|
615
729
|
elif re.search(r'file backed up to', self.PGLOG['SYSERR']):
|
|
616
730
|
if self.pgdel('bfile', f"bid = {bid}"):
|
|
617
|
-
self.pglog(f"{dsid}-{qfile}: backup tarfile deleted for
|
|
731
|
+
self.pglog(f"{dsid}-{qfile}: backup tarfile deleted for duplication", self.DTLACT)
|
|
618
732
|
for infile in qinfo['infiles']: self.delete_local_file(infile)
|
|
619
733
|
else:
|
|
620
734
|
self.PGBACK['errcnt'] += 1
|
|
@@ -623,12 +737,13 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
623
737
|
if stat:
|
|
624
738
|
# reset qinfo after quasar backup
|
|
625
739
|
qinfo['qcnt'] += 1
|
|
740
|
+
self.PGBACK['tardone'] += 1 # cumulative dispatched tars for the walltime finish-rate
|
|
626
741
|
qinfo['qfcnt'] += fcnt
|
|
627
742
|
qinfo['qsize'] += fsize
|
|
628
743
|
for dsid in dsids:
|
|
629
744
|
if dsid not in qinfo['qdsids']: qinfo['qdsids'].append(dsid)
|
|
630
745
|
if addback:
|
|
631
|
-
lastdsid = dsids.pop() if keepid else None
|
|
746
|
+
lastdsid = dsids.pop() if keepid else None
|
|
632
747
|
for dsid in dsids: # unlock all datasets but the last one
|
|
633
748
|
if dsid in qinfo['dslocks']:
|
|
634
749
|
self.lock_dataset(dsid, 0, self.LGEREX)
|
|
@@ -645,6 +760,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
645
760
|
def transfer_quasar_tarfiles(self, qinfo):
|
|
646
761
|
ccnt = self.check_child(None, 0, self.LOGWRN, -1) if self.PGBACK['mproc'] > 1 else 0
|
|
647
762
|
if self.PGSIG['QUIT']: self.quit_dsquasar(qinfo)
|
|
763
|
+
self.check_batch_deadline(qinfo)
|
|
648
764
|
# prepare for backup one tar file
|
|
649
765
|
dsids = qinfo['dsids']
|
|
650
766
|
bids = qinfo['bids']
|
|
@@ -675,10 +791,10 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
675
791
|
dstat = bstat = -1
|
|
676
792
|
if ccnt == 0 or self.PGSIG['PPID'] > 1:
|
|
677
793
|
if bflg == 'D':
|
|
678
|
-
dstat = self.
|
|
794
|
+
dstat = self.quasar_multiple_transfer(tofiles, fromfiles, 'gdex-quasar-drdata', 'gdex-glade', self.ERRACT)
|
|
679
795
|
if not dstat: self.pglog("Error Quaser Drdata for " + fmsg, self.ERRACT|self.LOGERR)
|
|
680
|
-
bstat = self.
|
|
681
|
-
if not
|
|
796
|
+
bstat = self.quasar_multiple_transfer(tofiles, fromfiles, 'gdex-quasar', 'gdex-glade', self.ERRACT)
|
|
797
|
+
if not bstat: self.pglog("Error Quaser Backup for " + fmsg, self.ERRACT|self.LOGERR)
|
|
682
798
|
if dstat == self.FINISH: dstat = self.check_globus_finished(tofiles[0], 'gdex-quasar-drdata', self.ERRACT|self.NOWAIT)
|
|
683
799
|
if bstat == self.FINISH: bstat = self.check_globus_finished(tofiles[0], 'gdex-quasar', self.ERRACT|self.NOWAIT)
|
|
684
800
|
if dstat and bstat:
|
|
@@ -698,6 +814,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
698
814
|
if dstat and bstat:
|
|
699
815
|
# reset qinfo after quasar backup
|
|
700
816
|
qinfo['qcnt'] += bcnt
|
|
817
|
+
self.PGBACK['tardone'] += bcnt # cumulative transferred tars for the walltime finish-rate
|
|
701
818
|
qinfo['qfcnt'] += fcnt
|
|
702
819
|
qinfo['qsize'] += fsize
|
|
703
820
|
for dsid in dsids:
|
|
@@ -777,7 +894,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
777
894
|
return {'wcount' : 0, 'wrecs' : [], 'scount' : 0, 'srecs' : []}
|
|
778
895
|
|
|
779
896
|
# get all files need to be backed up for a given dataset ID
|
|
780
|
-
def get_dataset_files(self, dsid, dsfiles, backflag, logact = 0):
|
|
897
|
+
def get_dataset_files(self, dsid, dsfiles, backflag, logact = 0, sizes = None):
|
|
781
898
|
dcnd = "dsid = '{}'".format(dsid)
|
|
782
899
|
fopt = self.get_backup_options(dsid, dcnd)
|
|
783
900
|
if not fopt:
|
|
@@ -817,6 +934,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
817
934
|
self.pglog("{}-G{}: Found {} Files ({})".format(dsid, gidx, rcnt, rmsg), self.LOGWRN)
|
|
818
935
|
|
|
819
936
|
fcnt = cnts['B'][0] + cnts['D'][0]
|
|
937
|
+
if sizes is not None: sizes[0] += cnts['B'][1] + cnts['D'][1]
|
|
820
938
|
if dsfiles:
|
|
821
939
|
cmsg = 'Changed ' if self.PGBACK['chgdays'] else ''
|
|
822
940
|
for bflg in dsfiles:
|
|
@@ -836,7 +954,6 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
836
954
|
|
|
837
955
|
# get web/saved files to backup for a given group condition and file option
|
|
838
956
|
def get_group_files(self, dsid, gcnd, dcnd, bfiles, fopt, infiles, cnts):
|
|
839
|
-
rcnt = 0
|
|
840
957
|
if self.PGBACK['chgdays'] > 0:
|
|
841
958
|
return self.get_group_changed_files(dsid, gcnd, dcnd, bfiles, fopt, infiles, cnts)
|
|
842
959
|
else:
|
|
@@ -982,28 +1099,27 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
982
1099
|
def get_backup_options(self, dsid, dcnd):
|
|
983
1100
|
fopt = 0
|
|
984
1101
|
bcnd = "= 0" if self.PGBACK['chgdays'] < 1 else "> 0"
|
|
985
|
-
# if self.pgget('sfile', 'sid', "{} AND bid {}".format(dcnd, bcnd), self.LGWNEX): fopt |= self.SOPT
|
|
986
1102
|
if self.pgget_wfile(dsid, 'wid', "bid " + bcnd, self.LGWNEX): fopt |= self.WOPT
|
|
987
1103
|
return fopt
|
|
988
1104
|
|
|
989
1105
|
# gather all available dataset ids to backup data files
|
|
990
|
-
def gather_dataset_files(self, dsfiles, unlock = True):
|
|
1106
|
+
def gather_dataset_files(self, dsfiles, unlock = True, sizes = None):
|
|
991
1107
|
fcnt = 0
|
|
1108
|
+
# warn per named dataset with no files; stay silent when scanning all datasets
|
|
1109
|
+
logact = self.LOGWRN if self.dsids else 0
|
|
992
1110
|
if self.dsids:
|
|
1111
|
+
pgrecs = []
|
|
993
1112
|
for dsid in self.dsids:
|
|
994
|
-
|
|
995
|
-
pgrec
|
|
996
|
-
if pgrec:
|
|
997
|
-
if unlock and pgrec['pid'] and self.lock_dataset(dsid, 0, self.LOGACT) < 1: continue
|
|
998
|
-
fcnt += self.get_dataset_files(dsid, dsfiles, pgrec['backflag'], self.LOGWRN)
|
|
1113
|
+
pgrec = self.pgget("dataset", "dsid, backflag, pid", "dsid = '{}'".format(dsid), self.LGWNEX)
|
|
1114
|
+
if pgrec: pgrecs.append(pgrec)
|
|
999
1115
|
else:
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1116
|
+
mrecs = self.pgmget("dataset", "dsid, backflag, pid", "ORDER BY dsid", self.LGWNEX)
|
|
1117
|
+
dcnt = len(mrecs['dsid']) if mrecs else 0
|
|
1118
|
+
pgrecs = [self.onerecord(mrecs, i) for i in range(dcnt)]
|
|
1119
|
+
for pgrec in pgrecs:
|
|
1120
|
+
dsid = pgrec['dsid']
|
|
1121
|
+
if unlock and pgrec['pid'] and self.lock_dataset(dsid, 0, self.LOGACT) < 1: continue
|
|
1122
|
+
fcnt += self.get_dataset_files(dsid, dsfiles, pgrec['backflag'], logact, sizes)
|
|
1007
1123
|
if dsfiles:
|
|
1008
1124
|
s = 's' if fcnt > 1 else ''
|
|
1009
1125
|
bmsg = self.BACKMSG[self.PGBACK['backflag']] if self.PGBACK['backflag'] else 'backup'
|
|
@@ -1013,28 +1129,21 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1013
1129
|
|
|
1014
1130
|
# gather all available backup file records with status N to backup
|
|
1015
1131
|
def gather_dataset_infiles(self, dsfiles):
|
|
1016
|
-
icnt = fcnt =
|
|
1132
|
+
icnt = fcnt = 0
|
|
1017
1133
|
bcnd = "status = 'N'"
|
|
1018
|
-
ocnd = " ORDER BY dsid"
|
|
1019
1134
|
flds = "bid, dsid, bfile, type, data_size size, (scount + wcount) fcnt, dsids, note"
|
|
1020
1135
|
if self.PGBACK['backflag']: bcnd += " AND type = '{}'".format(self.PGBACK['backflag'])
|
|
1021
1136
|
if self.dsids:
|
|
1022
|
-
for dsid in self.dsids
|
|
1023
|
-
dcnd = "dsid = '{}' AND {}".format(dsid, bcnd)
|
|
1024
|
-
pgrecs = self.pgmget("bfile", flds, dcnd, self.LGWNEX)
|
|
1025
|
-
bcnt = len(pgrecs['bid']) if pgrecs else 0
|
|
1026
|
-
for i in range(bcnt):
|
|
1027
|
-
cnt = self.get_backup_infile(dsfiles, self.onerecord(pgrecs, i))
|
|
1028
|
-
if cnt:
|
|
1029
|
-
icnt +=1
|
|
1030
|
-
fcnt += cnt
|
|
1137
|
+
cnds = ["dsid = '{}' AND {}".format(dsid, bcnd) for dsid in self.dsids]
|
|
1031
1138
|
else:
|
|
1032
|
-
|
|
1139
|
+
cnds = [bcnd + " ORDER BY dsid"]
|
|
1140
|
+
for dcnd in cnds:
|
|
1141
|
+
pgrecs = self.pgmget("bfile", flds, dcnd, self.LGWNEX)
|
|
1033
1142
|
bcnt = len(pgrecs['bid']) if pgrecs else 0
|
|
1034
1143
|
for i in range(bcnt):
|
|
1035
1144
|
cnt = self.get_backup_infile(dsfiles, self.onerecord(pgrecs, i))
|
|
1036
1145
|
if cnt:
|
|
1037
|
-
icnt +=1
|
|
1146
|
+
icnt += 1
|
|
1038
1147
|
fcnt += cnt
|
|
1039
1148
|
if dsfiles:
|
|
1040
1149
|
s = 's' if icnt > 1 else ''
|
|
@@ -1045,28 +1154,21 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1045
1154
|
# gather all available backup file records with status T to backup
|
|
1046
1155
|
#
|
|
1047
1156
|
def gather_dataset_tarfiles(self, dsfiles):
|
|
1048
|
-
tcnt = fcnt =
|
|
1157
|
+
tcnt = fcnt = 0
|
|
1049
1158
|
bcnd = "status = 'T'"
|
|
1050
|
-
ocnd = " ORDER BY dsid"
|
|
1051
1159
|
flds = "bid, dsid, bfile, type, data_size size, (scount + wcount) fcnt, note"
|
|
1052
1160
|
if self.PGBACK['backflag']: bcnd += " AND type = '{}'".format(self.PGBACK['backflag'])
|
|
1053
1161
|
if self.dsids:
|
|
1054
|
-
for dsid in self.dsids
|
|
1055
|
-
dcnd = "dsid = '{}' AND {}".format(dsid, bcnd)
|
|
1056
|
-
pgrecs = self.pgmget("bfile", flds, dcnd, self.LGWNEX)
|
|
1057
|
-
bcnt = len(pgrecs['bid']) if pgrecs else 0
|
|
1058
|
-
for i in range(bcnt):
|
|
1059
|
-
cnt = self.get_backup_tarfile(dsfiles, self.onerecord(pgrecs, i))
|
|
1060
|
-
if cnt:
|
|
1061
|
-
tcnt +=1
|
|
1062
|
-
fcnt += cnt
|
|
1162
|
+
cnds = ["dsid = '{}' AND {}".format(dsid, bcnd) for dsid in self.dsids]
|
|
1063
1163
|
else:
|
|
1064
|
-
|
|
1164
|
+
cnds = [bcnd + " ORDER BY dsid"]
|
|
1165
|
+
for dcnd in cnds:
|
|
1166
|
+
pgrecs = self.pgmget("bfile", flds, dcnd, self.LGWNEX)
|
|
1065
1167
|
bcnt = len(pgrecs['bid']) if pgrecs else 0
|
|
1066
1168
|
for i in range(bcnt):
|
|
1067
1169
|
cnt = self.get_backup_tarfile(dsfiles, self.onerecord(pgrecs, i))
|
|
1068
1170
|
if cnt:
|
|
1069
|
-
tcnt +=1
|
|
1171
|
+
tcnt += 1
|
|
1070
1172
|
fcnt += cnt
|
|
1071
1173
|
s = 's' if tcnt > 1 else ''
|
|
1072
1174
|
bmsg = self.BACKMSG[self.PGBACK['backflag']] if self.PGBACK['backflag'] else 'backup'
|
|
@@ -1092,7 +1194,6 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1092
1194
|
if self.PGBACK['action']&self.CINACT: ret = fcnt
|
|
1093
1195
|
if not dsfiles: return ret
|
|
1094
1196
|
dsids = [dsid]
|
|
1095
|
-
# if not (pgrec['note'] or self.rebuild_file_note(pgrec, dsids)): return 0
|
|
1096
1197
|
if not pgrec['note']:
|
|
1097
1198
|
return self.pglog(bfile + ": Miss note for N backup file", self.ERRACT)
|
|
1098
1199
|
infiles = []
|
|
@@ -1147,7 +1248,6 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1147
1248
|
if self.PGBACK['action']&self.CINACT: ret = fcnt
|
|
1148
1249
|
if not dsfiles: return ret
|
|
1149
1250
|
dsids = [dsid]
|
|
1150
|
-
# if not (pgrec['note'] or self.rebuild_file_note(pgrec, dsids)): return 0
|
|
1151
1251
|
if not pgrec['note']:
|
|
1152
1252
|
return self.pglog(bfile + ": Miss note for T backup file", self.ERRACT)
|
|
1153
1253
|
ftype = None
|
|
@@ -1347,6 +1447,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1347
1447
|
binfo = bfiles[bid]
|
|
1348
1448
|
for bkey in binfo: qinfo[bkey] = binfo[bkey]
|
|
1349
1449
|
self.process_one_quasar_pathfile(qinfo)
|
|
1450
|
+
if self.PGBACK['mproc'] > 1:
|
|
1451
|
+
self.check_child(None, 0, self.LOGWRN, 1) # wait all child processes done
|
|
1452
|
+
self.confirm_quasar_counts(qinfo, list(bfiles), "bfile LIKE 'G%/%.tar'") # recount confirmed renames from RDADB
|
|
1350
1453
|
qcnt = qinfo['qcnt']
|
|
1351
1454
|
if qcnt > 0:
|
|
1352
1455
|
s = 's' if qcnt > 1 else ''
|
|
@@ -1671,43 +1774,6 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1671
1774
|
dsfiles[pgrec['type']][pgrec['bid']] = {'note' : pgrec['note'], 'fcnt' : pgrec['fcnt'], 'size' : pgrec['size'],
|
|
1672
1775
|
'date' : pgrec['date_modified'], 'time' : pgrec['time_modified']}
|
|
1673
1776
|
|
|
1674
|
-
# rebuild missed file note
|
|
1675
|
-
def rebuild_file_note(self, pgrec, dsids):
|
|
1676
|
-
bid = pgrec['bid']
|
|
1677
|
-
fields = 'file, dsid, type, data_size, checksum'
|
|
1678
|
-
bfiles = {dsids[0] : {'S' : [], 'W' : []}}
|
|
1679
|
-
pgrecs = self.pgmget('sfile', 's' + fields, "bid = {} ORDER BY dsid, sfile".format(bid), self.LGEREX)
|
|
1680
|
-
cnt = len(pgrecs['sfile']) if pgrecs else 0
|
|
1681
|
-
for i in range(cnt):
|
|
1682
|
-
dsid = pgrecs['dsid'][i]
|
|
1683
|
-
if dsid not in dsids:
|
|
1684
|
-
dsids.append(dsid)
|
|
1685
|
-
bfiles[dsid] = {'S' : [], 'W' : []}
|
|
1686
|
-
bfiles[dsid]['S'].append([pgrecs['sfile'][i], pgrecs['type'][i], pgrecs['data_size'][i], pgrecs['checksum'][i]])
|
|
1687
|
-
pgrecs = self.pgmget('wfile', 'w' + fields, "bid = {} ORDER BY dsid, wfile".format(bid), self.LGEREX)
|
|
1688
|
-
cnt = len(pgrecs['wfile']) if pgrecs else 0
|
|
1689
|
-
for i in range(cnt):
|
|
1690
|
-
dsid = pgrecs['dsid'][i]
|
|
1691
|
-
if dsid not in dsids:
|
|
1692
|
-
dsids.append(dsid)
|
|
1693
|
-
bfiles[dsid] = {'S' : [], 'W' : []}
|
|
1694
|
-
bfiles[dsid]['W'].append([pgrecs['wfile'][i], pgrecs['type'][i], pgrecs['data_size'][i], pgrecs['checksum'][i]])
|
|
1695
|
-
# create note
|
|
1696
|
-
note = ''
|
|
1697
|
-
for dsid in bfiles:
|
|
1698
|
-
for ftype in bfiles[dsid]:
|
|
1699
|
-
files = bfiles[dsid][ftype]
|
|
1700
|
-
if not files: continue
|
|
1701
|
-
note += "<{}_{}_{}.txt>\n".format(dsid, ftype, bid)
|
|
1702
|
-
note += "DS<=>{}\n{}F<:>{}T<:>SZ<:>MC<:>\n".format(dsid, ftype, ftype)
|
|
1703
|
-
for flist in files:
|
|
1704
|
-
note += "{}<:>{}<:>{}<:>{}<:>\n".format(flist[0], flist[1], flist[2], flist[3])
|
|
1705
|
-
if note:
|
|
1706
|
-
self.pgexec("UPDATE bfile SET note = '{}' WHERE bid = {}".format(note, bid), self.LGEREX)
|
|
1707
|
-
pgrec['note'] = note
|
|
1708
|
-
return 1
|
|
1709
|
-
return 0
|
|
1710
|
-
|
|
1711
1777
|
# dump statistics for GDEX files available to create input files
|
|
1712
1778
|
def dump_dataset_files(self, dsfiles, backflag):
|
|
1713
1779
|
bfiles = dsfiles[backflag]
|
|
@@ -1922,7 +1988,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1922
1988
|
pcnt += 1
|
|
1923
1989
|
return pcnt
|
|
1924
1990
|
|
|
1925
|
-
# main function to
|
|
1991
|
+
# main function to execute this script
|
|
1926
1992
|
def main():
|
|
1927
1993
|
object = DsQuasar()
|
|
1928
1994
|
object.read_parameters()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
|
|
2
|
+
Name: dsquasar - back up RDA/GDEX data archives onto the Quasar backup servers
|
|
3
|
+
|
|
4
|
+
Check the Backup flags set in dataset.backflag and/or dsgroup.backflag of RDADB
|
|
5
|
+
to gather Web and Saved files, tar them into larger (>= 2GB) backup files, and
|
|
6
|
+
back them up onto the NCAR GDEX Quasar Globus end points, for a Backup copy
|
|
7
|
+
(gdex-quasar) and/or a disaster-recovery Drdata copy (gdex-quasar-drdata). The
|
|
8
|
+
file tarring and transferring is performed through the utility program dsarch.
|
|
9
|
+
|
|
10
|
+
Usage: dsquasar [-b] [(-a | -t DatasetIDs)] [-c ChangeDays] [-(e|E)] \
|
|
11
|
+
[-(B|D)] [-A ActionBits] [-l (Y|N)] [-m ProcessCount] \
|
|
12
|
+
[-n] [-u] [-d [HostName] [TryCount]] [-h]
|
|
13
|
+
|
|
14
|
+
Either -a or -t must be given (usage is displayed otherwise). dsquasar runs
|
|
15
|
+
from the Quasar backup work directory; it changes there automatically.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DATASET SELECTION
|
|
19
|
+
|
|
20
|
+
-a Gather files of all datasets that have a Backup flag set.
|
|
21
|
+
|
|
22
|
+
-t DatasetIDs
|
|
23
|
+
One or more dataset IDs whose files are backed up. The SQL wildcard
|
|
24
|
+
'%' is allowed and is expanded against the dataset table. Ignored,
|
|
25
|
+
with a warning, when -a is also given.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
BACKUP SCOPE
|
|
29
|
+
|
|
30
|
+
-B Quasar Backup only (endpoint gdex-quasar) for the given datasets.
|
|
31
|
+
|
|
32
|
+
-D Quasar Backup & Drdata (gdex-quasar and gdex-quasar-drdata).
|
|
33
|
+
|
|
34
|
+
-B and -D are mutually exclusive; the default (neither given) backs up
|
|
35
|
+
to both the Backup and the Drdata endpoints.
|
|
36
|
+
|
|
37
|
+
-c ChangeDays
|
|
38
|
+
Used with -A 1 to re-back up files that changed within the given
|
|
39
|
+
number of days. Must be greater than 0.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
ACTIONS
|
|
43
|
+
|
|
44
|
+
-A ActionBits
|
|
45
|
+
Bit flags selecting the actions to perform; the valid values are
|
|
46
|
+
1, 2, 3, 4, 6, 7, 8, 16 and it defaults to 3 (actions 1 + 2):
|
|
47
|
+
|
|
48
|
+
1 - create the dsarch input Save/Web file lists and add the
|
|
49
|
+
backup file (bfile) placeholder records (status 'N').
|
|
50
|
+
2 - build tar files (each >= 5GB where possible) from the Saved/
|
|
51
|
+
Web files listed in the input files (status 'N' -> 'T').
|
|
52
|
+
4 - transfer the built tar files (batched to >= 90GB per Globus
|
|
53
|
+
transfer) to the Quasar backup end points (status 'T' -> 'A').
|
|
54
|
+
8 - check file sizes between RDADB and the Quasar servers, and
|
|
55
|
+
reset status to 'N' when they differ.
|
|
56
|
+
16 - dump backup statistics for files backed up and files that are
|
|
57
|
+
ready to be backed up.
|
|
58
|
+
|
|
59
|
+
Combined values: 3 = 1+2, 6 = 2+4, 7 = 1+2+4.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
RUN CONTROL
|
|
63
|
+
|
|
64
|
+
-b Turn on background mode; no screen display.
|
|
65
|
+
|
|
66
|
+
-d [HostName] [TryCount]
|
|
67
|
+
Turn on delay mode to run as a batch job. A dscheck record is created
|
|
68
|
+
and the dscheck daemon later submits the job to PBS. On the initial
|
|
69
|
+
submit dsquasar sizes multi-processing from the detail file count and
|
|
70
|
+
reserves matching PBS cpus (walltime + select=1:ncpus=N:mem=Ngb) in the
|
|
71
|
+
dscheck qoptions; the invocation arguments are left unchanged so a
|
|
72
|
+
repeat submit with the same arguments still blocks as a duplicate.
|
|
73
|
+
|
|
74
|
+
-l (Y|N)
|
|
75
|
+
Whether to check and lock a dataset before backing it up. Defaults
|
|
76
|
+
to Y.
|
|
77
|
+
|
|
78
|
+
-m ProcessCount
|
|
79
|
+
Number of concurrent child processes. Defaults to 1. In delay mode
|
|
80
|
+
the process count travels through the reserved PBS cpus rather than
|
|
81
|
+
the command line.
|
|
82
|
+
|
|
83
|
+
-n Gather and show the available file counts only; no backup actions are
|
|
84
|
+
performed.
|
|
85
|
+
|
|
86
|
+
-u Clean up (unlock) backup locks for the given dataset IDs. Dataset IDs
|
|
87
|
+
must be provided (not valid with -a).
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
EMAIL
|
|
91
|
+
|
|
92
|
+
-e Send an email to the specialist with backup statistics.
|
|
93
|
+
|
|
94
|
+
-E Send an email to the specialist with detailed backup statistics.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
MISCELLANEOUS
|
|
98
|
+
|
|
99
|
+
-h Display this help document.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
EXAMPLES
|
|
103
|
+
|
|
104
|
+
1. Back up (create input files and tar files for) all flagged datasets:
|
|
105
|
+
|
|
106
|
+
dsquasar -a -A 3
|
|
107
|
+
|
|
108
|
+
2. Transfer built tar files of one dataset to both Quasar end points:
|
|
109
|
+
|
|
110
|
+
dsquasar -t d123456 -A 4
|
|
111
|
+
|
|
112
|
+
3. Show the counts of files ready to back up for a dataset, without acting:
|
|
113
|
+
|
|
114
|
+
dsquasar -t d123456 -n
|
|
115
|
+
|
|
116
|
+
4. Dump backup statistics for all datasets by email:
|
|
117
|
+
|
|
118
|
+
dsquasar -a -A 16 -e
|
|
119
|
+
|
|
120
|
+
5. Queue a delayed PBS batch job to create input files and tar files for all
|
|
121
|
+
datasets in the background (as run from cron); multi-processing is sized
|
|
122
|
+
automatically from the file count:
|
|
123
|
+
|
|
124
|
+
dsquasar -a -A 3 -e -b -d PBS
|
|
125
|
+
|
|
126
|
+
6. Unlock backup locks left on datasets:
|
|
127
|
+
|
|
128
|
+
dsquasar -t d123456 -u
|
{rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3/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.3
|
|
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.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar.egg-info/SOURCES.txt
RENAMED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
4
5
|
src/rda_python_dsquasar/__init__.py
|
|
5
6
|
src/rda_python_dsquasar/ds_quasar.py
|
|
6
7
|
src/rda_python_dsquasar/dsquasar.py
|
|
8
|
+
src/rda_python_dsquasar/dsquasar.usg
|
|
7
9
|
src/rda_python_dsquasar/dstacc.py
|
|
8
10
|
src/rda_python_dsquasar/taccrec.py
|
|
9
11
|
src/rda_python_dsquasar/tacctar.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_dsquasar-3.0.1 → rda_python_dsquasar-3.0.3}/src/rda_python_dsquasar/ds_quasar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|