rda-python-dsarch 3.0.17__py3-none-any.whl → 3.0.19__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.
@@ -58,6 +58,8 @@ class DsArch(PgArch, PgMeta):
58
58
  self.BCKNOTES = {}
59
59
  self.VINDEX = {}
60
60
  self.CHGGRPS = {}
61
+ self.CPFILES = {} # file index -> copy/move child pname (or 1/0) of an -AL first pass
62
+ self.CPDIRS = {} # file index -> cross copy direction ('G','O','B','D') of an -AL first pass
61
63
  self.ERRMSG = ''
62
64
 
63
65
  def read_parameters(self):
@@ -115,34 +117,33 @@ class DsArch(PgArch, PgMeta):
115
117
  if self.PGOPT['ACTS'] == self.OPTS['AH'][0]:
116
118
  self.ALLCNT = len(self.params['HF']) if 'HF' in self.params else len(self.params['LF'])
117
119
  if 'XC' in self.params:
118
- self.crosscopy_help_files('Copy')
120
+ self.archive_in_passes(self.crosscopy_help_files, 'Copy')
119
121
  elif 'XM' in self.params:
120
- self.crosscopy_help_files('Move')
122
+ self.archive_in_passes(self.crosscopy_help_files, 'Move')
121
123
  else:
122
- self.archive_help_files()
124
+ self.archive_in_passes(self.archive_help_files)
123
125
  if 'CL' in self.params: self.delete_local_files()
124
126
  elif self.PGOPT['ACTS'] == self.OPTS['AQ'][0]:
125
127
  if 'XC' in self.params:
126
128
  self.ALLCNT = len(self.params['QF'])
127
- self.crosscopy_backup_files()
129
+ self.archive_in_passes(self.crosscopy_backup_files)
128
130
  else:
129
131
  self.archive_backup_file()
130
132
  elif self.PGOPT['ACTS'] == self.OPTS['AS'][0]:
131
133
  self.ALLCNT = len(self.params['SF']) if 'SF' in self.params else len(self.params['LF'])
132
134
  self.cache_group_info(self.ALLCNT)
133
- self.archive_saved_files()
135
+ self.archive_in_passes(self.archive_saved_files)
134
136
  if 'CL' in self.params: self.delete_local_files()
135
137
  elif self.PGOPT['ACTS'] == self.OPTS['AW'][0]:
136
138
  self.ALLCNT = len(self.params['WF']) if 'WF' in self.params else len(self.params['LF'])
137
139
  if 'ML' in self.params and 'GX' in self.params: del self.params['ML']
138
140
  self.cache_group_info(self.ALLCNT)
139
141
  if 'XC' in self.params:
140
- self.crosscopy_web_files('Copy')
142
+ self.archive_in_passes(self.crosscopy_web_files, 'Copy')
141
143
  elif 'XM' in self.params:
142
- self.crosscopy_web_files('Move')
144
+ self.archive_in_passes(self.crosscopy_web_files, 'Move')
143
145
  else:
144
- self.archive_web_files()
145
- if self.PGSIG['BPROC'] > 1: self.set_webfile_info()
146
+ self.archive_in_passes(self.archive_web_files)
146
147
  if 'CL' in self.params: self.delete_local_files()
147
148
  elif self.PGOPT['ACTS'] & self.OPTS['CG'][0]:
148
149
  self.ALLCNT = len(self.params['GI'])
@@ -206,17 +207,17 @@ class DsArch(PgArch, PgMeta):
206
207
  (len(self.params['QF']) if 'QF' in self.params else 0))))
207
208
  if 'DD' in self.params: self.record_delete_directory(None, self.params['DD'])
208
209
  if 'HF' in self.params:
209
- self.move_help_files()
210
+ self.archive_in_passes(self.move_help_files)
210
211
  elif 'TS' in self.params:
211
- self.web_to_saved_files()
212
+ self.archive_in_passes(self.web_to_saved_files)
212
213
  elif 'TW' in self.params:
213
- self.saved_to_web_files()
214
+ self.archive_in_passes(self.saved_to_web_files)
214
215
  elif 'SF' in self.params:
215
- self.move_saved_files()
216
+ self.archive_in_passes(self.move_saved_files)
216
217
  elif 'WF' in self.params:
217
- self.move_web_files()
218
+ self.archive_in_passes(self.move_web_files)
218
219
  else:
219
- self.move_backup_files()
220
+ self.archive_in_passes(self.move_backup_files)
220
221
  if 'DD' in self.params: self.clean_delete_directory(self.PGOPT['wrnlog'])
221
222
  elif self.PGOPT['ACTS'] == self.OPTS['RQ'][0]:
222
223
  if 'WF' in self.params or 'SF' in self.params:
@@ -355,7 +356,7 @@ class DsArch(PgArch, PgMeta):
355
356
  else:
356
357
  self.cmdlog()
357
358
 
358
- def archive_web_files(self):
359
+ def archive_web_files(self, cpass=0):
359
360
  """
360
361
  Archive local files as web files on GLADE and/or the object store.
361
362
 
@@ -369,6 +370,11 @@ class DsArch(PgArch, PgMeta):
369
370
 
370
371
  Retries the whole batch if transient errors occur, stopping when the
371
372
  error count stabilises. Sets self.RETSTAT on unresolved failures.
373
+
374
+ Args:
375
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
376
+ copy pass (copies in up to N child processes, no records) and then 2
377
+ for the record pass (see archive_in_passes())
372
378
  """
373
379
  tname = 'wfile'
374
380
  dftloc = None
@@ -379,7 +385,7 @@ class DsArch(PgArch, PgMeta):
379
385
  dslocflags = set()
380
386
  dflags = {}
381
387
  self.pglog("Archive {} Web file{} of {} ...".format(self.ALLCNT, s, dsid), self.WARNLG)
382
- if 'ZD' in self.params or 'UZ' in self.params:
388
+ if cpass != 2 and ('ZD' in self.params or 'UZ' in self.params):
383
389
  self.compress_localfile_list(self.PGOPT['CACT'], self.ALLCNT)
384
390
  if 'QF' in self.params: self.params['QF'] = self.get_bid_numbers(self.params['QF'])
385
391
  self.validate_multiple_values(tname, self.ALLCNT)
@@ -406,19 +412,15 @@ class DsArch(PgArch, PgMeta):
406
412
  self.params['SZ'] = [0]*self.ALLCNT
407
413
  self.OPTS['SZ'][2] |= 2
408
414
  if 'MC' not in self.params: self.params['MC'] = [None]*self.ALLCNT
409
- reorder = errcnt = metatotal = metacnt = self.ADDCNT = self.MODCNT = bgcnt = acnt = ocnt = 0
415
+ reorder = errcnt = metatotal = metacnt = self.ADDCNT = self.MODCNT = acnt = ocnt = 0
410
416
  perrcnt = self.ALLCNT
411
417
  efiles = [1]*self.ALLCNT
412
- if self.PGSIG['BPROC'] > 1:
413
- afiles = [None]*self.ALLCNT
414
- lfiles = [None]*self.ALLCNT
415
- ofiles = [None]*self.ALLCNT
416
418
  fnames = None
417
419
  override = self.OVERRIDE
418
420
  if not override and 'GF' in self.params: override = self.OVRIDE
419
421
  while True:
420
422
  for i in range(bidx, self.ALLCNT):
421
- if self.PGSIG['BPROC'] < 2 and i > bidx and ((i-bidx)%20) == 0:
423
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
422
424
  if metacnt >= self.PGOPT['RSMAX']:
423
425
  metatotal += self.process_metadata("W", metacnt, self.PGOPT['emerol'])
424
426
  metacnt = 0
@@ -470,14 +472,20 @@ class DsArch(PgArch, PgMeta):
470
472
  if warch and locfile == afile: warch = 0
471
473
  vsnctl = 1 if pgrec and pgrec['vindex'] and pgrec['data_size'] else 0
472
474
  chksum = self.params['MC'][i]
475
+ gdone = odone = 0
476
+ if cpass == 2 and i in self.CPFILES: # copied in the -AL copy pass; recopy only what failed
477
+ (gdone, odone) = self.check_pass_copies(i, wfile, afile if warch else None, ofile if oarch else None, bucket, lsize)
478
+ if gdone: warch = 0; acnt += 1
479
+ if odone: oarch = 0; ocnt += 1
480
+ if self.PGLOG['DSCHECK'] and (gdone or odone) and not (warch or oarch): chksize += lsize
473
481
  if warch and (vsnctl or not override):
474
482
  info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
475
483
  if info:
476
484
  if pgrec and chksum and pgrec['checksum'] and pgrec['checksum'] == chksum:
477
- self.pglog("Web-{}: Same-Checksum ARCHIVED at {}:{}".format(winfo, info['date_modified'], info['time_modified']), self.PGOPT['emllog'])
485
+ self.pglog("Web-{}: Same-Checksum ARCHIVED at {}:{}".format(winfo, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['emllog']))
478
486
  warch = 0
479
487
  elif not override and info['data_size'] == lsize:
480
- self.pglog("Web-{}: Same-Size ARCHIVED at {}:{}".format(winfo, info['date_modified'], info['time_modified']), self.PGOPT['wrnlog'])
488
+ self.pglog("Web-{}: Same-Size ARCHIVED at {}:{}".format(winfo, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['wrnlog']))
481
489
  warch = 0
482
490
  elif vsnctl and not override:
483
491
  self.pglog("Web-{}: Cannot rearchive version controlled file".format(winfo), self.PGOPT['extlog'])
@@ -493,10 +501,10 @@ class DsArch(PgArch, PgMeta):
493
501
  info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
494
502
  if info:
495
503
  if pgrec and chksum and pgrec['checksum'] and pgrec['checksum'] == chksum:
496
- self.pglog("Object-{}-{}: Same-Checksum ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), self.PGOPT['emllog'])
504
+ self.pglog("Object-{}-{}: Same-Checksum ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['emllog']))
497
505
  oarch = 0
498
506
  elif not override and info['data_size'] == lsize:
499
- self.pglog("Object-{}-{}: Same-Size ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), self.PGOPT['wrnlog'])
507
+ self.pglog("Object-{}-{}: Same-Size ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['wrnlog']))
500
508
  oarch = 0
501
509
  elif vsnctl and not override:
502
510
  self.pglog("Object-{}-{}: Cannot rearchive version controlled file".format(bucket, ofile), self.PGOPT['extlog'])
@@ -510,6 +518,10 @@ class DsArch(PgArch, PgMeta):
510
518
  dflags['O'] = bucket
511
519
  continue
512
520
  if (warch + oarch) > 0:
521
+ if cpass == 1: # -AL copy pass: copy in a child process; the record pass sets the record
522
+ self.run_copy(i, self.copy_archive_file, afile if warch else None, ofile if oarch else None,
523
+ lfile, bucket, (replace if oarch else 0), self.PGOPT['emerol']|override)
524
+ continue
513
525
  if warch:
514
526
  if not self.local_copy_local(afile, lfile, self.PGOPT['emerol']|override):
515
527
  errcnt += 1
@@ -526,40 +538,41 @@ class DsArch(PgArch, PgMeta):
526
538
  continue
527
539
  ocnt += 1
528
540
  if self.PGLOG['DSCHECK']: chksize += lsize
529
- if self.PGSIG['BPROC'] > 1:
530
- afiles[i] = afile
531
- lfiles[i] = lfile
532
- ofiles[i] = ofile
533
- bgcnt += 1
534
- if self.PGSIG['BPROC'] < 2:
535
- if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
536
- info = None
537
- if afile: # also when already in place, so a directory gets its real total size
538
- info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
539
- elif oarch:
540
- info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
541
- elif pgrec:
542
- info = self.get_file_origin_info(wfile, pgrec)
543
- elif locflag == 'O':
544
- info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
545
- wid = self.set_one_webfile(i, pgrec, wfile, fnames, type, info)
546
- if not wid:
547
- self.params['LF'][i] = self.params['WF'][i] = None
548
- continue
549
- if 'GX' in self.params and self.PGOPT['GXTYP'].find(type) > -1:
550
- metacnt += self.record_meta_gather('W', dsid, wfile, self.params['DF'][i])
551
- self.cache_meta_tindex(dsid, wid, 'W')
552
- if pgrec and pgrec['meta_link'] and pgrec['meta_link'] != 'N':
553
- if 'DX' in self.params or self.PGOPT['GXTYP'].find(type) < 0 and self.PGOPT['GXTYP'].find(pgrec['type']) > -1:
554
- metacnt += self.record_meta_delete('W', dsid, wfile)
555
- elif 'GI' in self.params:
556
- gindex = self.params['GI'][i]
557
- if gindex != pgrec['gindex'] and (gindex or (self.OPTS['GI'][2]&2) == 0):
558
- metacnt += self.record_meta_summary('W', dsid, gindex, pgrec['gindex'])
559
- if errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
541
+ if cpass == 1: continue # the record pass sets the record, also for a file not copied
542
+ if gdone: warch = 1 # as if copied in this pass, for the file info below
543
+ if odone: oarch = 1
544
+ if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
545
+ info = None
546
+ if afile: # also when already in place, so a directory gets its real total size
547
+ info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
548
+ elif oarch:
549
+ info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
550
+ elif pgrec:
551
+ info = self.get_file_origin_info(wfile, pgrec)
552
+ elif locflag == 'O':
553
+ info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
554
+ wid = self.set_one_webfile(i, pgrec, wfile, fnames, type, info)
555
+ if not wid:
556
+ self.params['LF'][i] = self.params['WF'][i] = None
557
+ continue
558
+ if 'GX' in self.params and self.PGOPT['GXTYP'].find(type) > -1:
559
+ metacnt += self.record_meta_gather('W', dsid, wfile, self.params['DF'][i])
560
+ self.cache_meta_tindex(dsid, wid, 'W')
561
+ if pgrec and pgrec['meta_link'] and pgrec['meta_link'] != 'N':
562
+ if 'DX' in self.params or self.PGOPT['GXTYP'].find(type) < 0 and self.PGOPT['GXTYP'].find(pgrec['type']) > -1:
563
+ metacnt += self.record_meta_delete('W', dsid, wfile)
564
+ elif 'GI' in self.params:
565
+ gindex = self.params['GI'][i]
566
+ if gindex != pgrec['gindex'] and (gindex or (self.OPTS['GI'][2]&2) == 0):
567
+ metacnt += self.record_meta_summary('W', dsid, gindex, pgrec['gindex'])
568
+ if cpass == 1 or errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
560
569
  perrcnt = errcnt
561
- self.pglog("Rearchive failed {}/{} Web file{} for {}".format(errcnt, self.ALLCNT. s, dsid), self.PGOPT['emllog'])
570
+ self.pglog("Rearchive failed {}/{} Web file{} for {}".format(errcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
562
571
  errcnt = self.reset_errmsg(0)
572
+ if cpass == 1: # the record pass checks the copies, retries failed ones and sets the records
573
+ self.wait_copy_children()
574
+ self.reset_errmsg(0) # the record pass finds and counts any error again
575
+ return
563
576
  if errcnt:
564
577
  if 'CL' in self.params: del self.params['CL']
565
578
  self.RETSTAT = self.reset_errmsg(errcnt)
@@ -567,13 +580,6 @@ class DsArch(PgArch, PgMeta):
567
580
  if efiles[i]: self.params['WF'][i] = ''
568
581
  if self.PGLOG['DSCHECK']:
569
582
  self.check_storage_dflags(dflags, self.PGLOG['DSCHECK'], self.PGOPT['emerol'])
570
- if bgcnt:
571
- self.check_background(None, 0, self.LOGWRN, 1)
572
- for i in range(self.ALLCNT):
573
- if afiles[i]:
574
- self.validate_gladearch(afiles[i], lfiles[i], i)
575
- if ofiles[i]:
576
- self.validate_objectarch(ofiles[i], lfiles[i], bucket, i)
577
583
  if acnt > 0:
578
584
  self.pglog("{} of {} Web file{} archived for {}".format(acnt, self.ALLCNT, s, self.params['DS']), self.PGOPT['emllog'])
579
585
  if ocnt > 0:
@@ -590,7 +596,7 @@ class DsArch(PgArch, PgMeta):
590
596
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
591
597
  if dslocflags: self.set_dataset_locflag(dsid, dslocflags.pop())
592
598
 
593
- def archive_help_files(self):
599
+ def archive_help_files(self, cpass=0):
594
600
  """
595
601
  Archive local files as dataset help/documentation files on GLADE/object store.
596
602
 
@@ -598,6 +604,11 @@ class DsArch(PgArch, PgMeta):
598
604
  file path (RTPATH['H<type>']) and registering records in the hfile table.
599
605
  Handles both GLADE (warch) and object-store (oarch) destinations based on
600
606
  the file's location flag. Sets self.RETSTAT on unresolved failures.
607
+
608
+ Args:
609
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
610
+ copy pass (copies in up to N child processes, no records) and then 2
611
+ for the record pass (see archive_in_passes())
601
612
  """
602
613
  tname = 'hfile'
603
614
  dsid = self.params['DS']
@@ -631,18 +642,14 @@ class DsArch(PgArch, PgMeta):
631
642
  self.params['SZ'] = [0]*self.ALLCNT
632
643
  self.OPTS['SZ'][2] |= 2
633
644
  if 'MC' not in self.params: self.params['MC'] = [None]*self.ALLCNT
634
- reorder = errcnt = self.ADDCNT = self.MODCNT = bgcnt = acnt = ocnt = 0
645
+ reorder = errcnt = self.ADDCNT = self.MODCNT = acnt = ocnt = 0
635
646
  perrcnt = self.ALLCNT
636
647
  efiles = [1]*self.ALLCNT
637
- if self.PGSIG['BPROC'] > 1:
638
- afiles = [None]*self.ALLCNT
639
- lfiles = [None]*self.ALLCNT
640
- ofiles = [None]*self.ALLCNT
641
648
  override = self.OVERRIDE
642
649
  fnames = None
643
650
  while True:
644
651
  for i in range(bidx, self.ALLCNT):
645
- if self.PGSIG['BPROC'] < 2 and i > bidx and ((i-bidx)%20) == 0:
652
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
646
653
  if self.PGLOG['DSCHECK']:
647
654
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
648
655
  if 'EM' in self.params:
@@ -694,14 +701,20 @@ class DsArch(PgArch, PgMeta):
694
701
  self.pglog("{}: Archive Help file from {} ...".format(hfile, locfile), self.WARNLG)
695
702
  if harch and locfile == afile: harch = 0
696
703
  chksum = self.params['MC'][i]
704
+ gdone = odone = 0
705
+ if cpass == 2 and i in self.CPFILES: # copied in the -AL copy pass; recopy only what failed
706
+ (gdone, odone) = self.check_pass_copies(i, hfile, afile if harch else None, ofile if oarch else None, bucket, lsize)
707
+ if gdone: harch = 0; acnt += 1
708
+ if odone: oarch = 0; ocnt += 1
709
+ if self.PGLOG['DSCHECK'] and (gdone or odone) and not (harch or oarch): chksize += lsize
697
710
  if harch and not self.OVERRIDE:
698
711
  info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
699
712
  if info:
700
713
  if pgrec and chksum and pgrec['checksum'] and pgrec['checksum'] == chksum:
701
- self.pglog("Help-{}: Same-Checksum ARCHIVED at {}:{}".format(hinfo, info['date_modified'], info['time_modified']), self.PGOPT['emllog'])
714
+ self.pglog("Help-{}: Same-Checksum ARCHIVED at {}:{}".format(hinfo, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['emllog']))
702
715
  harch = 0
703
716
  elif info['data_size'] == lsize:
704
- self.pglog("Help-{}: Same-Size ARCHIVED at {}:{}".format(hinfo, info['date_modified'], info['time_modified']), self.PGOPT['wrnlog'])
717
+ self.pglog("Help-{}: Same-Size ARCHIVED at {}:{}".format(hinfo, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['wrnlog']))
705
718
  harch = 0
706
719
  elif info is not None:
707
720
  errcnt += 1
@@ -712,10 +725,10 @@ class DsArch(PgArch, PgMeta):
712
725
  info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
713
726
  if info:
714
727
  if pgrec and chksum and pgrec['checksum'] and pgrec['checksum'] == chksum:
715
- self.pglog("Object-{}-{}: Same-Checksum ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), self.PGOPT['emllog'])
728
+ self.pglog("Object-{}-{}: Same-Checksum ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['emllog']))
716
729
  oarch = 0
717
730
  elif not override and info['data_size'] == lsize:
718
- self.pglog("Object-{}-{}: Same-Size ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), self.PGOPT['wrnlog'])
731
+ self.pglog("Object-{}-{}: Same-Size ARCHIVED at {}:{}".format(bucket, ofile, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['wrnlog']))
719
732
  oarch = 0
720
733
  else:
721
734
  replace = 1
@@ -725,6 +738,10 @@ class DsArch(PgArch, PgMeta):
725
738
  dflags['O'] = bucket
726
739
  continue
727
740
  if (harch + oarch) > 0:
741
+ if cpass == 1: # -AL copy pass: copy in a child process; the record pass sets the record
742
+ self.run_copy(i, self.copy_archive_file, afile if harch else None, ofile if oarch else None,
743
+ lfile, bucket, (replace if oarch else 0), self.PGOPT['emerol']|override)
744
+ continue
728
745
  if harch:
729
746
  if not self.local_copy_local(afile, lfile, self.PGOPT['emerol']|override):
730
747
  errcnt += 1
@@ -740,30 +757,31 @@ class DsArch(PgArch, PgMeta):
740
757
  continue
741
758
  ocnt += 1
742
759
  if self.PGLOG['DSCHECK']: chksize += lsize
743
- if self.PGSIG['BPROC'] > 1:
744
- afiles[i] = afile
745
- lfiles[i] = lfile
746
- ofiles[i] = ofile
747
- bgcnt += 1
748
- if self.PGSIG['BPROC'] < 2:
749
- if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
750
- info = None
751
- if harch:
752
- info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
753
- elif oarch:
754
- info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
755
- elif pgrec:
756
- info = self.get_file_origin_info(hfile, pgrec)
757
- elif locflag == 'O':
758
- info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
759
- hid = self.set_one_helpfile(i, pgrec, hfile, fnames, type, info)
760
- if not hid:
761
- self.params['LF'][i] = self.params['WF'][i] = None
762
- continue
763
- if errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
760
+ if cpass == 1: continue # the record pass sets the record, also for a file not copied
761
+ if gdone: harch = 1 # as if copied in this pass, for the file info below
762
+ if odone: oarch = 1
763
+ if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
764
+ info = None
765
+ if harch:
766
+ info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
767
+ elif oarch:
768
+ info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
769
+ elif pgrec:
770
+ info = self.get_file_origin_info(hfile, pgrec)
771
+ elif locflag == 'O':
772
+ info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
773
+ hid = self.set_one_helpfile(i, pgrec, hfile, fnames, type, info)
774
+ if not hid:
775
+ self.params['LF'][i] = self.params['WF'][i] = None
776
+ continue
777
+ if cpass == 1 or errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
764
778
  perrcnt = errcnt
765
- self.pglog("Rearchive failed {}/{} Help file{} for {}".format(errcnt, self.ALLCNT. s, dsid), self.PGOPT['emllog'])
779
+ self.pglog("Rearchive failed {}/{} Help file{} for {}".format(errcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
766
780
  errcnt = self.reset_errmsg(0)
781
+ if cpass == 1: # the record pass checks the copies, retries failed ones and sets the records
782
+ self.wait_copy_children()
783
+ self.reset_errmsg(0) # the record pass finds and counts any error again
784
+ return
767
785
  if errcnt:
768
786
  if 'CL' in self.params: del self.params['CL']
769
787
  self.RETSTAT = self.reset_errmsg(errcnt)
@@ -771,13 +789,6 @@ class DsArch(PgArch, PgMeta):
771
789
  if efiles[i]: self.params['HF'][i] = ''
772
790
  if self.PGLOG['DSCHECK']:
773
791
  self.check_storage_dflags(dflags, self.PGLOG['DSCHECK'], self.PGOPT['emerol'])
774
- if bgcnt:
775
- self.check_background(None, 0, self.LOGWRN, 1)
776
- for i in range(self.ALLCNT):
777
- if afiles[i]:
778
- self.validate_gladearch(afiles[i], lfiles[i], i)
779
- if ofiles[i]:
780
- self.validate_objectarch(ofiles[i], lfiles[i], bucket, i)
781
792
  if acnt > 0:
782
793
  self.pglog("{} of {} Help file{} archived for {}".format(acnt, self.ALLCNT, s, self.params['DS']), self.PGOPT['emllog'])
783
794
  if ocnt > 0:
@@ -791,7 +802,7 @@ class DsArch(PgArch, PgMeta):
791
802
  self.reset_rdadb_version(dsid)
792
803
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
793
804
 
794
- def archive_saved_files(self):
805
+ def archive_saved_files(self, cpass=0):
795
806
  """
796
807
  Archive local files as dataset saved files on GLADE.
797
808
 
@@ -799,6 +810,11 @@ class DsArch(PgArch, PgMeta):
799
810
  type constraints, handles version control checks, and registers or updates
800
811
  the saved file record in RDADB via set_one_savedfile(). Retries on
801
812
  transient copy failures. Sets self.RETSTAT on unresolved failures.
813
+
814
+ Args:
815
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
816
+ copy pass (copies in up to N child processes, no records) and then 2
817
+ for the record pass (see archive_in_passes())
802
818
  """
803
819
  tname = 'sfile'
804
820
  dftloc = 'G'
@@ -808,7 +824,7 @@ class DsArch(PgArch, PgMeta):
808
824
  bidx = chksize = 0
809
825
  dflags = {}
810
826
  self.pglog("Archive {} Saved file{} of {} ...".format(self.ALLCNT, s, dsid), self.WARNLG)
811
- if 'ZD' in self.params or 'UZ' in self.params:
827
+ if cpass != 2 and ('ZD' in self.params or 'UZ' in self.params):
812
828
  self.compress_localfile_list(self.PGOPT['CACT'], self.ALLCNT)
813
829
  if 'QF' in self.params: self.params['QF'] = self.get_bid_numbers(self.params['QF'])
814
830
  self.validate_multiple_values(tname, self.ALLCNT)
@@ -832,18 +848,15 @@ class DsArch(PgArch, PgMeta):
832
848
  self.params['SZ'] = [0]*self.ALLCNT
833
849
  self.OPTS['SZ'][2] |= 2
834
850
  if 'MC' not in self.params: self.params['MC'] = [None]*self.ALLCNT
835
- reorder = errcnt = self.ADDCNT = self.MODCNT = bgcnt = acnt = 0
851
+ reorder = errcnt = self.ADDCNT = self.MODCNT = acnt = 0
836
852
  perrcnt = self.ALLCNT
837
853
  efiles = [1]*self.ALLCNT
838
- if self.PGSIG['BPROC'] > 1:
839
- afiles = [None]*self.ALLCNT
840
- lfiles = [None]*self.ALLCNT
841
854
  fnames = None
842
855
  override = self.OVERRIDE
843
856
  if not override and 'GF' in self.params: override = self.OVRIDE
844
857
  while True:
845
858
  for i in range(bidx, self.ALLCNT):
846
- if self.PGSIG['BPROC'] < 2 and i > bidx and ((i-bidx)%20) == 0:
859
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
847
860
  if self.PGLOG['DSCHECK']:
848
861
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
849
862
  if 'EM' in self.params:
@@ -895,14 +908,20 @@ class DsArch(PgArch, PgMeta):
895
908
  self.pglog("{}: Archive Saved file from {} ...".format(sinfo, locfile), self.WARNLG)
896
909
  vsnctl = 1 if pgrec and pgrec['vindex'] and pgrec['data_size'] else 0
897
910
  chksum = self.params['MC'][i]
911
+ if cpass == 2 and i in self.CPFILES: # copied in the -AL copy pass; recopy only if it failed
912
+ (gdone, odone) = self.check_pass_copies(i, sfile, afile if sarch else None, None, None, lsize)
913
+ if gdone:
914
+ sarch = 0
915
+ acnt += 1
916
+ if self.PGLOG['DSCHECK']: chksize += lsize
898
917
  if sarch and (vsnctl or not override):
899
918
  info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
900
919
  if info:
901
920
  if pgrec and chksum and pgrec['checksum'] and pgrec['checksum'] == chksum:
902
- self.pglog("Saved-{}: Same-Checksum ARCHIVED at {}:{}".format(sinfo, info['date_modified'], info['time_modified']), self.PGOPT['emllog'])
921
+ self.pglog("Saved-{}: Same-Checksum ARCHIVED at {}:{}".format(sinfo, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['emllog']))
903
922
  sarch = 0
904
923
  elif not override and info['data_size'] == lsize:
905
- self.pglog("Saved-{}: Same-Size ARCHIVED at {}:{}".format(sinfo, info['date_modified'], info['time_modified']), self.PGOPT['wrnlog'])
924
+ self.pglog("Saved-{}: Same-Size ARCHIVED at {}:{}".format(sinfo, info['date_modified'], info['time_modified']), (0 if cpass == 1 else self.PGOPT['wrnlog']))
906
925
  sarch = 0
907
926
  elif vsnctl and not override:
908
927
  self.pglog("Saved-{}: Cannot rearchive version controlled file".format(sinfo), self.PGOPT['extlog'])
@@ -914,29 +933,32 @@ class DsArch(PgArch, PgMeta):
914
933
  dflags['G'] = self.PGLOG['DECSHOME']
915
934
  continue
916
935
  if sarch:
936
+ if cpass == 1: # -AL copy pass: copy in a child process; the record pass sets the record
937
+ self.run_copy(i, self.copy_archive_file, afile, None, lfile, None, 0, self.PGOPT['emerol']|override)
938
+ continue
917
939
  if not self.local_copy_local(afile, lfile, self.PGOPT['emerol']|override):
918
940
  errcnt += 1
919
941
  efiles[i] = 1
920
942
  continue
921
943
  acnt += 1
922
944
  if self.PGLOG['DSCHECK']: chksize += lsize
923
- if self.PGSIG['BPROC'] > 1:
924
- afiles[i] = afile
925
- lfiles[i] = lfile
926
- bgcnt += 1
927
- if self.PGSIG['BPROC'] < 2:
928
- if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
929
- info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE) # also when already in place
930
- if not info and pgrec:
931
- info = self.get_file_origin_info(sfile, pgrec)
932
- sid = self.set_one_savedfile(i, pgrec, sfile, fnames, type, info)
933
- if not sid:
934
- self.params['LF'][i] = self.params['SF'][i] = None
935
- continue
936
- if errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
945
+ if cpass == 1: continue # the record pass sets the record, also for a file not copied
946
+ if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
947
+ info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE) # also when already in place
948
+ if not info and pgrec:
949
+ info = self.get_file_origin_info(sfile, pgrec)
950
+ sid = self.set_one_savedfile(i, pgrec, sfile, fnames, type, info)
951
+ if not sid:
952
+ self.params['LF'][i] = self.params['SF'][i] = None
953
+ continue
954
+ if cpass == 1 or errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
937
955
  perrcnt = errcnt
938
956
  self.pglog("Rearchive failed {}/{} Saved file{} for {}!".format(errcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
939
957
  errcnt = self.reset_errmsg(0)
958
+ if cpass == 1: # the record pass checks the copies, retries failed ones and sets the records
959
+ self.wait_copy_children()
960
+ self.reset_errmsg(0) # the record pass finds and counts any error again
961
+ return
940
962
  if errcnt:
941
963
  if 'CL' in self.params: del self.params['CL']
942
964
  self.RETSTAT = self.reset_errmsg(errcnt)
@@ -944,10 +966,6 @@ class DsArch(PgArch, PgMeta):
944
966
  if efiles[i]: self.params['SF'][i] = ''
945
967
  if self.PGLOG['DSCHECK']:
946
968
  self.check_storage_dflags(dflags, self.PGLOG['DSCHECK'], self.PGOPT['emerol'])
947
- if bgcnt:
948
- self.check_background(None, 0, self.LOGWRN, 1)
949
- for i in range(self.ALLCNT):
950
- if afiles[i]: self.validate_gladearch(afiles[i], lfiles[i], i)
951
969
  if acnt > 0:
952
970
  self.pglog("{} of {} Saved file{} archived for {}".format(acnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
953
971
  if self.PGLOG['DSCHECK']:
@@ -959,12 +977,14 @@ class DsArch(PgArch, PgMeta):
959
977
  self.reset_rdadb_version(dsid)
960
978
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
961
979
 
962
- def crosscopy_web_files(self, aname):
980
+ def crosscopy_web_files(self, aname, cpass=0):
963
981
  """
964
982
  Cross-copy (or cross-move) web files between GLADE and the object store.
965
983
 
966
984
  Args:
967
985
  aname -- 'Copy' to copy files, 'Move' to move (deletes the source)
986
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
987
+ copy pass and 2 for the record pass (see archive_in_passes())
968
988
  """
969
989
  tname = 'wfile'
970
990
  dsid = self.params['DS']
@@ -981,18 +1001,14 @@ class DsArch(PgArch, PgMeta):
981
1001
  if bidx == self.ALLCNT: return
982
1002
  self.set_rearchive_filenumber(dsid, bidx, self.ALLCNT, 4)
983
1003
  chksize = self.PGLOG['DSCHECK']['size']
984
- reorder = errcnt = metatotal = metacnt = self.MODCNT = bgcnt = acnt = ocnt = 0
1004
+ reorder = errcnt = metatotal = metacnt = self.MODCNT = acnt = ocnt = 0
985
1005
  perrcnt = self.ALLCNT
986
1006
  efiles = [1]*self.ALLCNT
987
1007
  self.params['LC'] = ['B']*self.ALLCNT
988
- if self.PGSIG['BPROC'] > 1:
989
- afiles = [None]*self.ALLCNT
990
- ofiles = [None]*self.ALLCNT
991
- warchs = [None]*self.ALLCNT
992
1008
  fnames = None
993
1009
  while True:
994
1010
  for i in range(bidx, self.ALLCNT):
995
- if self.PGSIG['BPROC'] < 2 and i > bidx and ((i-bidx)%20) == 0:
1011
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
996
1012
  if self.PGLOG['DSCHECK']:
997
1013
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
998
1014
  if metacnt >= self.PGOPT['RSMAX']:
@@ -1038,7 +1054,32 @@ class DsArch(PgArch, PgMeta):
1038
1054
  elif (warch + oarch) == 0 and pgrec['locflag'] == 'B':
1039
1055
  self.pglog(winfo + ": No need Cross {}, Both Web & Object Exist".format(aname), self.PGOPT['wrnlog'])
1040
1056
  continue
1041
- if warch:
1057
+ if cpass == 1: # -AL copy pass: cross copy in a child process; the record pass does the rest
1058
+ if warch or oarch:
1059
+ cdir = self.CPDIRS[i] = 'G' if warch else 'O'
1060
+ self.run_copy(i, self.cross_copy_file, cdir, (afile if warch else ofile), (ofile if warch else afile),
1061
+ bucket, None, self.PGOPT['emerol']|self.OVERRIDE)
1062
+ continue
1063
+ cdone = None
1064
+ if cpass == 2 and i in self.CPDIRS: # cross copied in the copy pass; copy again only if it failed
1065
+ cdir = self.CPDIRS[i]
1066
+ if self.check_cross_copy(i, winfo, cdir, (afile if cdir == 'G' else ofile), bucket, pgrec['data_size']):
1067
+ cdone = cdir
1068
+ elif cdir == 'G':
1069
+ warch = 1
1070
+ else:
1071
+ oarch = 1
1072
+ if cdone == 'G':
1073
+ if aname == 'Move':
1074
+ self.params['LC'][i] = 'G'
1075
+ self.delete_object_file(ofile, bucket, self.PGOPT['extlog'])
1076
+ acnt += 1
1077
+ elif cdone == 'O':
1078
+ if aname == 'Move':
1079
+ self.params['LC'][i] = 'O'
1080
+ self.delete_local_file(afile, self.PGOPT['extlog'])
1081
+ ocnt += 1
1082
+ elif warch:
1042
1083
  if not self.object_copy_local(afile, ofile, bucket, self.PGOPT['emerol']|self.OVERRIDE):
1043
1084
  errcnt += 1
1044
1085
  efiles[i] = 1
@@ -1058,46 +1099,37 @@ class DsArch(PgArch, PgMeta):
1058
1099
  self.params['LC'][i] = 'O'
1059
1100
  self.delete_local_file(afile, self.PGOPT['extlog'])
1060
1101
  ocnt += 1
1061
- if self.PGSIG['BPROC'] > 1:
1062
- afiles[i] = afile
1063
- ofiles[i] = ofile
1064
- warchs[i] = warch
1065
- bgcnt += 1
1066
1102
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
1067
- if self.PGSIG['BPROC'] < 2:
1068
- if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
1069
- info = self.get_file_origin_info(wfile, pgrec)
1070
- wid = self.set_one_webfile(i, pgrec, wfile, fnames, type, info)
1071
- if not wid:
1072
- self.params['WF'][i] = None
1073
- continue
1074
- if 'GX' in self.params and self.PGOPT['GXTYP'].find(type) > -1:
1075
- metacnt += self.record_meta_gather('W', dsid, wfile, self.params['DF'][i])
1076
- self.cache_meta_tindex(dsid, wid, 'W')
1077
- if pgrec['meta_link'] and pgrec['meta_link'] != 'N':
1078
- if 'DX' in self.params or self.PGOPT['GXTYP'].find(type) < 0 and self.PGOPT['GXTYP'].find(pgrec['type']) > -1:
1079
- metacnt += self.record_meta_delete('W', dsid, wfile)
1080
- elif 'GI' in self.params:
1081
- gindex = self.params['GI'][i]
1082
- if gindex != pgrec['gindex'] and (gindex or (self.OPTS['GI'][2]&2) == 0):
1083
- metacnt += self.record_meta_summary('W', dsid, gindex, pgrec['gindex'])
1084
- if errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
1103
+ if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
1104
+ info = self.get_file_origin_info(wfile, pgrec)
1105
+ wid = self.set_one_webfile(i, pgrec, wfile, fnames, type, info)
1106
+ if not wid:
1107
+ self.params['WF'][i] = None
1108
+ continue
1109
+ if 'GX' in self.params and self.PGOPT['GXTYP'].find(type) > -1:
1110
+ metacnt += self.record_meta_gather('W', dsid, wfile, self.params['DF'][i] if 'DF' in self.params else pgrec['data_format'])
1111
+ self.cache_meta_tindex(dsid, wid, 'W')
1112
+ if pgrec['meta_link'] and pgrec['meta_link'] != 'N':
1113
+ if 'DX' in self.params or self.PGOPT['GXTYP'].find(type) < 0 and self.PGOPT['GXTYP'].find(pgrec['type']) > -1:
1114
+ metacnt += self.record_meta_delete('W', dsid, wfile)
1115
+ elif 'GI' in self.params:
1116
+ gindex = self.params['GI'][i]
1117
+ if gindex != pgrec['gindex'] and (gindex or (self.OPTS['GI'][2]&2) == 0):
1118
+ metacnt += self.record_meta_summary('W', dsid, gindex, pgrec['gindex'])
1119
+ if cpass == 1 or errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
1085
1120
  perrcnt = errcnt
1086
1121
  self.pglog("Recopy failed {}/{} Web file{} for {}".format(errcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
1087
1122
  errcnt = self.reset_errmsg(0)
1123
+ if cpass == 1: # the record pass checks the copies, copies again failed ones and sets the records
1124
+ self.wait_copy_children()
1125
+ self.reset_errmsg(0) # the record pass finds and counts any error again
1126
+ return
1088
1127
  if errcnt:
1089
1128
  self.RETSTAT = self.reset_errmsg(errcnt)
1090
1129
  for i in range(bidx, self.ALLCNT):
1091
1130
  if efiles[i]: self.params['WF'][i] = ''
1092
1131
  if self.PGLOG['DSCHECK']:
1093
1132
  self.check_storage_dflags(dflags, self.PGLOG['DSCHECK'], self.PGOPT['emerol'])
1094
- if bgcnt:
1095
- self.check_background(None, 0, self.LOGWRN, 1)
1096
- for i in range(self.ALLCNT):
1097
- if warchs[i]:
1098
- self.validate_gladearch(afiles[i], "{}-{}".format(bucket, ofiles[i]), i)
1099
- elif ofiles[i]:
1100
- self.validate_objectarch(ofiles[i], afiles[i], bucket, i)
1101
1133
  astr = 'Moved' if aname == 'Move' else 'Copied'
1102
1134
  if acnt > 0: self.pglog("{} of {} Web file{} Cross {} for {}".format(acnt, self.ALLCNT, s, astr, dsid), self.PGOPT['emllog'])
1103
1135
  if ocnt > 0: self.pglog("{} of {} Object file{} Cross {} for {}".format(ocnt, self.ALLCNT, s, astr, dsid), self.PGOPT['emllog'])
@@ -1112,12 +1144,14 @@ class DsArch(PgArch, PgMeta):
1112
1144
  self.reset_rdadb_version(dsid)
1113
1145
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
1114
1146
 
1115
- def crosscopy_help_files(self, aname):
1147
+ def crosscopy_help_files(self, aname, cpass=0):
1116
1148
  """
1117
1149
  Cross-copy (or cross-move) help files between GLADE and the object store.
1118
1150
 
1119
1151
  Args:
1120
1152
  aname -- 'Copy' to copy files, 'Move' to move (deletes the source)
1153
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
1154
+ copy pass and 2 for the record pass (see archive_in_passes())
1121
1155
  """
1122
1156
  tname = 'hfile'
1123
1157
  dsid = self.params['DS']
@@ -1135,18 +1169,14 @@ class DsArch(PgArch, PgMeta):
1135
1169
  if bidx == self.ALLCNT: return
1136
1170
  self.set_rearchive_filenumber(dsid, bidx, self.ALLCNT, 4)
1137
1171
  chksize = self.PGLOG['DSCHECK']['size']
1138
- reorder = errcnt = self.MODCNT = bgcnt = acnt = ocnt = 0
1172
+ reorder = errcnt = self.MODCNT = acnt = ocnt = 0
1139
1173
  perrcnt = self.ALLCNT
1140
1174
  efiles = [1]*self.ALLCNT
1141
1175
  self.params['LC'] = ['B']*self.ALLCNT
1142
- if self.PGSIG['BPROC'] > 1:
1143
- afiles = [None]*self.ALLCNT
1144
- ofiles = [None]*self.ALLCNT
1145
- harchs = [None]*self.ALLCNT
1146
1176
  fnames = None
1147
1177
  while True:
1148
1178
  for i in range(bidx, self.ALLCNT):
1149
- if self.PGSIG['BPROC'] < 2 and i > bidx and ((i-bidx)%20) == 0:
1179
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
1150
1180
  if self.PGLOG['DSCHECK']:
1151
1181
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
1152
1182
  if 'EM' in self.params:
@@ -1202,7 +1232,32 @@ class DsArch(PgArch, PgMeta):
1202
1232
  elif not (harch or oarch) and pgrec['locflag'] == 'B':
1203
1233
  self.pglog(hinfo + ": No need Cross {} Help file, on Both Glade & Object".format(aname), self.PGOPT['wrnlog'])
1204
1234
  continue
1205
- if harch:
1235
+ if cpass == 1: # -AL copy pass: cross copy in a child process; the record pass does the rest
1236
+ if harch or oarch:
1237
+ cdir = self.CPDIRS[i] = 'G' if harch else 'O'
1238
+ self.run_copy(i, self.cross_copy_file, cdir, (afile if harch else ofile), (ofile if harch else afile),
1239
+ bucket, None, self.PGOPT['emerol']|self.OVERRIDE)
1240
+ continue
1241
+ cdone = None
1242
+ if cpass == 2 and i in self.CPDIRS: # cross copied in the copy pass; copy again only if it failed
1243
+ cdir = self.CPDIRS[i]
1244
+ if self.check_cross_copy(i, hinfo, cdir, (afile if cdir == 'G' else ofile), bucket, pgrec['data_size']):
1245
+ cdone = cdir
1246
+ elif cdir == 'G':
1247
+ harch = 1
1248
+ else:
1249
+ oarch = 1
1250
+ if cdone == 'G':
1251
+ if aname == 'Move':
1252
+ self.params['LC'][i] = 'G'
1253
+ self.delete_object_file(ofile, bucket, self.PGOPT['extlog'])
1254
+ acnt += 1
1255
+ elif cdone == 'O':
1256
+ if aname == 'Move':
1257
+ self.params['LC'][i] = 'O'
1258
+ self.delete_local_file(afile, self.PGOPT['extlog'])
1259
+ ocnt += 1
1260
+ elif harch:
1206
1261
  if not self.object_copy_local(afile, ofile, bucket, self.PGOPT['emerol']|self.OVERRIDE):
1207
1262
  errcnt += 1
1208
1263
  efiles[i] = 1
@@ -1222,36 +1277,27 @@ class DsArch(PgArch, PgMeta):
1222
1277
  self.params['LC'][i] = 'O'
1223
1278
  self.delete_local_file(afile, self.PGOPT['extlog'])
1224
1279
  ocnt += 1
1225
- if self.PGSIG['BPROC'] > 1:
1226
- afiles[i] = afile
1227
- ofiles[i] = ofile
1228
- harchs[i] = harch
1229
- bgcnt += 1
1230
1280
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
1231
- if self.PGSIG['BPROC'] < 2:
1232
- if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
1233
- info = self.get_file_origin_info(hfile, pgrec)
1234
- hid = self.set_one_helpfile(i, pgrec, hfile, fnames, type, info)
1235
- if not hid:
1236
- self.params['HF'][i] = None
1237
- continue
1238
- if errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
1281
+ if not fnames: fnames = self.get_field_keys(tname, None, "G") # get setting fields if not yet
1282
+ info = self.get_file_origin_info(hfile, pgrec)
1283
+ hid = self.set_one_helpfile(i, pgrec, hfile, fnames, type, info)
1284
+ if not hid:
1285
+ self.params['HF'][i] = None
1286
+ continue
1287
+ if cpass == 1 or errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
1239
1288
  perrcnt = errcnt
1240
1289
  self.pglog("Re{} {} Help file{} for {}".format(aname, errcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
1241
1290
  errcnt = self.reset_errmsg(0)
1291
+ if cpass == 1: # the record pass checks the copies, copies again failed ones and sets the records
1292
+ self.wait_copy_children()
1293
+ self.reset_errmsg(0) # the record pass finds and counts any error again
1294
+ return
1242
1295
  if errcnt:
1243
1296
  self.RETSTAT = self.reset_errmsg(errcnt)
1244
1297
  for i in range(bidx, self.ALLCNT):
1245
1298
  if efiles[i]: self.params['SF'][i] = ''
1246
1299
  if self.PGLOG['DSCHECK']:
1247
1300
  self.check_storage_dflags(dflags, self.PGLOG['DSCHECK'], self.PGOPT['emerol'])
1248
- if bgcnt:
1249
- self.check_background(None, 0, self.LOGWRN, 1)
1250
- for i in range(self.ALLCNT):
1251
- if harchs[i]:
1252
- self.validate_gladearch(afiles[i], "{}-{}".format(bucket, ofiles[i]), i)
1253
- elif ofiles[i]:
1254
- self.validate_objectarch(ofiles[i], afiles[i], bucket, i)
1255
1301
  astr = 'Moved' if aname == 'Move' else 'Copied'
1256
1302
  if acnt > 0: self.pglog("{} of {} Help file{} Cross {} for {}".format(acnt, self.ALLCNT, s, astr, dsid), self.PGOPT['emllog'])
1257
1303
  if ocnt > 0: self.pglog("{} of {} Object file{} Cross {} for {}".format(ocnt, self.ALLCNT, s, astr, dsid), self.PGOPT['emllog'])
@@ -1838,13 +1884,17 @@ class DsArch(PgArch, PgMeta):
1838
1884
  self.set_dscheck_dcount(self.ALLCNT, chksize, self.PGOPT['extlog'])
1839
1885
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
1840
1886
 
1841
- def crosscopy_backup_files(self):
1887
+ def crosscopy_backup_files(self, cpass=0):
1842
1888
  """
1843
1889
  Cross-copy Quasar backup files between Globus endpoints.
1844
1890
 
1845
1891
  Transfers backup files between gdex-quasar and gdex-quasar-drdata
1846
1892
  endpoints using Globus transfer, updating RDADB records on completion.
1847
1893
  Sets self.RETSTAT on transfer failures.
1894
+
1895
+ Args:
1896
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
1897
+ copy pass and 2 for the record pass (see archive_in_passes())
1848
1898
  """
1849
1899
  tname = 'bfile'
1850
1900
  qtype = 'D'
@@ -1864,16 +1914,13 @@ class DsArch(PgArch, PgMeta):
1864
1914
  if bidx == self.ALLCNT: return
1865
1915
  self.set_rearchive_filenumber(dsid, bidx, self.ALLCNT, 4)
1866
1916
  chksize = self.PGLOG['DSCHECK']['size']
1867
- reorder = errcnt = self.MODCNT = bgcnt = bcnt = dcnt = 0
1917
+ reorder = errcnt = self.MODCNT = bcnt = dcnt = 0
1868
1918
  perrcnt = self.ALLCNT
1869
1919
  efiles = [1]*self.ALLCNT
1870
- if self.PGSIG['BPROC'] > 1:
1871
- qfiles = [None]*self.ALLCNT
1872
- barchs = [None]*self.ALLCNT
1873
1920
  fnames = None
1874
1921
  while True:
1875
1922
  for i in range(bidx, self.ALLCNT):
1876
- if self.PGSIG['BPROC'] < 2 and i > bidx and ((i-bidx)%20) == 0:
1923
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
1877
1924
  if self.PGLOG['DSCHECK']:
1878
1925
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
1879
1926
  if 'EM' in self.params:
@@ -1912,7 +1959,26 @@ class DsArch(PgArch, PgMeta):
1912
1959
  elif not (barch or darch) and pgrec['type'] == 'D':
1913
1960
  self.pglog(binfo + ": No need Cross Copy, Both Backup & Drdata Exist", self.PGOPT['wrnlog'])
1914
1961
  continue
1915
- if barch:
1962
+ if cpass == 1: # -AL copy pass: cross copy in a child process; the record pass sets the record
1963
+ if barch or darch:
1964
+ cdir = self.CPDIRS[i] = 'B' if barch else 'D'
1965
+ self.run_copy(i, self.cross_copy_file, cdir, qfile, qfile, (bpoint if barch else dpoint),
1966
+ (dpoint if barch else bpoint), self.PGOPT['emerol']|self.OVERRIDE)
1967
+ continue
1968
+ cdone = None
1969
+ if cpass == 2 and i in self.CPDIRS: # cross copied in the copy pass; copy again only if it failed
1970
+ cdir = self.CPDIRS[i]
1971
+ if self.check_cross_copy(i, binfo, cdir, bfile, (bpoint if cdir == 'B' else dpoint), pgrec['data_size']):
1972
+ cdone = cdir
1973
+ elif cdir == 'B':
1974
+ barch = 1
1975
+ else:
1976
+ darch = 1
1977
+ if cdone == 'B':
1978
+ bcnt += 1
1979
+ elif cdone == 'D':
1980
+ dcnt += 1
1981
+ elif barch:
1916
1982
  bstat = self.endpoint_copy_endpoint(qfile, qfile, bpoint, dpoint, self.PGOPT['emerol']|self.OVERRIDE)
1917
1983
  if bstat == self.FINISH: bstat = self.check_globus_finished(qfile, bpoint, self.PGOPT['emerol']|self.NOWAIT)
1918
1984
  if not bstat:
@@ -1930,17 +1996,12 @@ class DsArch(PgArch, PgMeta):
1930
1996
  dflags['D'] = dpoint
1931
1997
  continue
1932
1998
  dcnt += 1
1933
- if self.PGSIG['BPROC'] > 1:
1934
- qfiles[i] = qfile
1935
- barchs[i] = barch
1936
- bgcnt += 1
1937
1999
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
1938
- if self.PGSIG['BPROC'] < 2:
1939
- if not fnames: fnames = self.get_field_keys(tname) # get setting fields if not yet
1940
- bid = self.set_one_backfile(i, pgrec, bfile, fnames, qtype)
1941
- if not bid:
1942
- self.params['QF'][i] = None
1943
- continue
2000
+ if not fnames: fnames = self.get_field_keys(tname) # get setting fields if not yet
2001
+ bid = self.set_one_backfile(i, pgrec, bfile, fnames, qtype)
2002
+ if not bid:
2003
+ self.params['QF'][i] = None
2004
+ continue
1944
2005
  if errcnt == 0 or errcnt >= perrcnt or self.PGLOG['DSCHECK']: break
1945
2006
  perrcnt = errcnt
1946
2007
  self.pglog("Recopy {} Quasar file{} for {}".format(errcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
@@ -1951,13 +2012,6 @@ class DsArch(PgArch, PgMeta):
1951
2012
  if efiles[i]: self.params['QF'][i] = ''
1952
2013
  if self.PGLOG['DSCHECK']:
1953
2014
  self.check_storage_dflags(dflags, self.PGLOG['DSCHECK'], self.PGOPT['emerol'])
1954
- if bgcnt:
1955
- self.check_background(None, 0, self.LOGWRN, 1)
1956
- for i in range(self.ALLCNT):
1957
- if barchs[i]:
1958
- self.validate_backarch(qfiles[i], "{}-{}".format(dpoint, qfiles[i]), bpoint, i)
1959
- elif qfiles[i]:
1960
- self.validate_backarch(qfiles[i], "{}-{}".format(bpoint, qfiles[i]), dpoint, i)
1961
2015
  if bcnt > 0: self.pglog("{} of {} Backup file{} Cross Copied for {}".format(bcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
1962
2016
  if dcnt > 0: self.pglog("{} of {} Drdata file{} Cross Copied for {}".format(dcnt, self.ALLCNT, s, dsid), self.PGOPT['emllog'])
1963
2017
  if self.PGLOG['DSCHECK']:
@@ -1968,57 +2022,265 @@ class DsArch(PgArch, PgMeta):
1968
2022
  self.reset_rdadb_version(dsid)
1969
2023
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
1970
2024
 
1971
- def validate_gladearch(self, file, ofile, i):
2025
+ def archive_in_passes(self, archfunc, *args):
1972
2026
  """
1973
- Verify that a GLADE archive file exists and matches the expected file info.
2027
+ Run a file action once, or in two passes for -AL.
1974
2028
 
1975
- Logs a fatal error when validation fails, setting the file entry at
1976
- index *i* to None so it is skipped in subsequent processing.
2029
+ Used for archive_web/help/saved_files, crosscopy_web/help/backup_files and
2030
+ move_web/help/saved/backup_files. With -AL N (N > 1, so PGSIG['MPROC'] > 1)
2031
+ the action runs twice:
2032
+ 1. copy pass -- the usual checks, then the slow file work of each file (its
2033
+ copy, cross copy or moves) runs in one of up to N child processes
2034
+ (run_copy()); no records are set;
2035
+ 2. record pass -- the usual single process loop; work the copy pass did is
2036
+ checked (check_pass_copies(), check_cross_copy(), pass_moved()) instead
2037
+ of done again, and done again only if it failed; source deletes of a
2038
+ cross move, records, dscheck progress and metadata are all done here
2039
+ exactly as without -AL.
1977
2040
 
1978
2041
  Args:
1979
- file -- original local file name (for error messages)
1980
- ofile -- expected absolute GLADE archive path to check
1981
- i -- file index (used to null out params entries on failure)
1982
- """
1983
- info = self.check_local_file(file, 6, self.PGOPT['errlog']|self.PFSIZE)
1984
- if not info:
1985
- self.pglog("Error archiving {} to {}".format(ofile, file), self.LGEREX)
1986
- elif self.OPTS['SZ'][2]&2 and self.params['SZ'] and info['data_size'] != self.params['SZ'][i]:
1987
- self.pglog("Wrong Sizes: ({}){}/({}){}".format(file, info['data_size'], ofile, self.params['SZ'][i]), self.LGEREX)
1988
- else:
1989
- self.set_local_mode(file, 1, self.PGLOG['FILEMODE'], info['mode'], info['logname'], self.PGOPT['errlog'])
2042
+ archfunc -- bound action method taking its own args and then the pass
2043
+ number (0, 1 or 2) as keyword cpass
2044
+ args -- arguments of archfunc before cpass, such as 'Copy'/'Move'
2045
+ """
2046
+ if self.PGSIG['MPROC'] < 2: return archfunc(*args)
2047
+ self.CPFILES = {}
2048
+ self.CPDIRS = {}
2049
+ archfunc(*args, cpass=1)
2050
+ archfunc(*args, cpass=2)
2051
+ self.CPFILES = {}
2052
+ self.CPDIRS = {}
2053
+
2054
+ def run_copy(self, i, copyfunc, *args):
2055
+ """
2056
+ Run the copy of file *i* in a child process for the -AL copy pass.
2057
+
2058
+ Waits for a free child slot (up to PGSIG['MPROC'] running), forks, and runs
2059
+ copyfunc(*args) in the child, which then leaves with os._exit(): it skips the
2060
+ parent's exit handling, so it cannot close the parent's database connection,
2061
+ send email or end the run; NOQUIT also keeps an EXITLG error in the copy from
2062
+ exiting early. The parent records the child's process name in
2063
+ self.CPFILES[i] (its exit code then shows up in self.CEXITS) and returns at
2064
+ once. If no child can be started, the copy runs in the parent and
2065
+ self.CPFILES[i] is 1 or 0 for its result.
1990
2066
 
1991
- def validate_objectarch(self, file, ofile, bucket, i):
2067
+ Args:
2068
+ i -- file index
2069
+ copyfunc -- function doing the copy; returns true on success
2070
+ args -- arguments of copyfunc
2071
+ """
2072
+ for fh in (sys.stdout, sys.stderr): # nothing buffered is written twice by the child
2073
+ try:
2074
+ fh.flush()
2075
+ except Exception:
2076
+ pass
2077
+ pname = "dsarch_copy_{}".format(i)
2078
+ self.CEXITS.pop(pname, None)
2079
+ stat = self.start_child(pname, self.LOGWRN, 1)
2080
+ if stat <= 0: # cannot fork, copy in the parent
2081
+ self.CPFILES[i] = 1 if copyfunc(*args) else 0
2082
+ return self.CPFILES[i]
2083
+ if self.PGSIG['PPID'] > 1: # in the child process
2084
+ self.PGLOG['NOQUIT'] = 1
2085
+ ret = 0
2086
+ try:
2087
+ ret = copyfunc(*args)
2088
+ except BaseException as e:
2089
+ self.pglog("Copy {}: {}".format(i, str(e)), self.LOGERR)
2090
+ for fh in (sys.stdout, sys.stderr):
2091
+ try:
2092
+ fh.flush()
2093
+ except Exception:
2094
+ pass
2095
+ os._exit(0 if ret else 1)
2096
+ self.CPFILES[i] = pname # the child's exit code shows up in self.CEXITS[pname]
2097
+ return 1
2098
+
2099
+ def wait_copy_children(self):
2100
+ """Wait until all child processes of the -AL copy pass are done."""
2101
+ if self.PGSIG['MPROC'] > 1: self.check_child(None, 0, self.LOGWRN, 1)
2102
+
2103
+ def copy_archive_file(self, afile, ofile, lfile, bucket, replace, logact):
2104
+ """
2105
+ Copy one local file to GLADE and/or the object store; run in a copy pass child.
2106
+
2107
+ Args:
2108
+ afile -- absolute GLADE target path, or None for no GLADE copy
2109
+ ofile -- object store key, or None for no object copy
2110
+ lfile -- local source file
2111
+ bucket -- object store bucket
2112
+ replace -- true to delete the existing object first
2113
+ logact -- logging flags for the copies
2114
+
2115
+ Returns:
2116
+ 1 if all copies succeeded, 0 otherwise
2117
+ """
2118
+ if afile and not self.local_copy_local(afile, lfile, logact): return 0
2119
+ if ofile:
2120
+ if replace: self.delete_object_file(ofile, bucket)
2121
+ if not self.local_copy_object(ofile, lfile, bucket, None, logact): return 0
2122
+ return 1
2123
+
2124
+ def child_copy_failed(self, i):
2125
+ """
2126
+ Return true if the -AL copy pass work of file *i* is known to have failed.
2127
+
2128
+ self.CPFILES[i] holds the child's process name, whose exit code is then in
2129
+ self.CEXITS, or 1/0 for work done in the parent; unknown counts as not failed.
2130
+ """
2131
+ cstat = self.CPFILES.get(i)
2132
+ if isinstance(cstat, str): # done in a child: failed if it exited with an error
2133
+ ecode = self.CEXITS.get(cstat)
2134
+ return ecode is not None and ecode != 0
2135
+ return cstat == 0 # done in the parent: 1 or 0
2136
+
2137
+ def cross_copy_file(self, cdir, tofile, fromfile, topoint, frompoint, logact):
1992
2138
  """
1993
- Verify that an object-store file exists and matches the expected file info.
2139
+ Cross copy one file in the given direction; run in a copy pass child.
1994
2140
 
1995
2141
  Args:
1996
- file -- original local file name (for error messages)
1997
- ofile -- object-store key path to check
1998
- bucket -- object-store bucket name
1999
- i -- file index (used to null out params entries on failure)
2142
+ cdir -- 'G' object store to GLADE, 'O' GLADE to object store,
2143
+ 'B'/'D' Globus copy to the Quasar backup/drdata endpoint
2144
+ tofile -- target path, object key or Quasar file
2145
+ fromfile -- source path, object key or Quasar file
2146
+ topoint -- object store bucket for 'G'/'O', target endpoint for 'B'/'D'
2147
+ frompoint -- source endpoint for 'B'/'D', None otherwise
2148
+ logact -- logging flags
2149
+
2150
+ Returns:
2151
+ a true value if the copy succeeded (Globus copies wait for their task)
2000
2152
  """
2001
- info = self.check_object_file(file, bucket, 0, self.PGOPT['errlog'])
2002
- if not info:
2003
- self.pglog("Error archiving {} to {}-{}".format(ofile, bucket, file), self.LGEREX)
2004
- elif self.OPTS['SZ'][2]&2 and self.params['SZ'] and info['data_size'] != self.params['SZ'][i]:
2005
- self.pglog("Wrong Sizes: ({}-{}){}/({}){}".format(bucket, file, info['data_size'], ofile, self.params['SZ'][i]), self.LGEREX)
2153
+ if cdir == 'G': return self.object_copy_local(tofile, fromfile, topoint, logact)
2154
+ if cdir == 'O': return self.local_copy_object(tofile, fromfile, topoint, None, logact)
2155
+ stat = self.endpoint_copy_endpoint(tofile, fromfile, topoint, frompoint, logact)
2156
+ if stat == self.FINISH: stat = self.check_globus_finished(tofile, topoint, logact|self.NOWAIT)
2157
+ return stat
2006
2158
 
2007
- def validate_backarch(self, file, ofile, endpoint, i):
2159
+ def check_cross_copy(self, i, finfo, cdir, tofile, topoint, size):
2008
2160
  """
2009
- Verify that a Quasar backup file exists at the given endpoint.
2161
+ Check a cross copy, upload or download the -AL copy pass made, for the record pass.
2010
2162
 
2011
2163
  Args:
2012
- file -- original local file name (for error messages)
2013
- ofile -- backup file path on the Quasar endpoint
2014
- endpoint -- Globus endpoint identifier string
2015
- i -- file index (used to null out params entries on failure)
2164
+ i -- file index, the key of self.CPFILES
2165
+ finfo -- file description, for the message
2166
+ cdir -- copy direction, see cross_copy_file()
2167
+ tofile -- target path, object key or Quasar file
2168
+ topoint -- object store bucket or target endpoint
2169
+ size -- size in the file record, 0 to skip the size check
2170
+
2171
+ Returns:
2172
+ 1 if the target holds the file, 0 if the copy is to be made again
2173
+ """
2174
+ if not self.child_copy_failed(i):
2175
+ if cdir == 'G':
2176
+ info = self.check_local_file(tofile, 0, self.PGOPT['emerol']|self.PFSIZE)
2177
+ elif cdir == 'O':
2178
+ info = self.check_object_file(tofile, topoint, 0, self.PGOPT['emerol'])
2179
+ else:
2180
+ info = self.check_backup_file(tofile, topoint, 0, self.PGOPT['emerol'])
2181
+ if info and (not size or info['data_size'] == size): return 1
2182
+ self.pglog("{}: copy in child process failed, copy again".format(finfo), self.PGOPT['wrnlog'])
2183
+ return 0
2184
+
2185
+ def do_move(self, mtype, tofile, fromfile, where, logact):
2186
+ """
2187
+ Do one step of a file's move: a move, an upload/download or a delete.
2188
+
2189
+ Args:
2190
+ mtype -- 'L' move a GLADE file, 'O' move an object, 'B' move a Quasar
2191
+ backup file, 'U' upload a GLADE file to the object store,
2192
+ 'G' download an object to GLADE, 'X' delete an object,
2193
+ 'Y' delete a GLADE file
2194
+ tofile -- new path, object key or Quasar file (None for a delete)
2195
+ fromfile -- old path, object key or Quasar file
2196
+ where -- bucket for 'O'/'U'/'G'/'X', endpoint for 'B', None otherwise
2197
+ logact -- logging flags
2198
+
2199
+ Returns:
2200
+ a true value if done (a move also when it was moved already)
2201
+ """
2202
+ if mtype == 'L': return self.move_local_file(tofile, fromfile, logact)
2203
+ if mtype == 'O': return self.move_object_file(tofile, fromfile, where, where, logact)
2204
+ if mtype == 'B': return self.move_backup_file(tofile, fromfile, where, logact)
2205
+ if mtype == 'U': return self.local_copy_object(tofile, fromfile, where, None, logact)
2206
+ if mtype == 'G': return self.object_copy_local(tofile, fromfile, where, logact)
2207
+ if mtype == 'X': return self.delete_object_file(fromfile, where, logact)
2208
+ return self.delete_local_file(fromfile, logact)
2209
+
2210
+ def move_pass_files(self, moves, logact):
2211
+ """
2212
+ Do the moves of one file in order; run in a copy pass child.
2213
+
2214
+ Args:
2215
+ moves -- list of (mtype, tofile, fromfile, where) steps, see do_move()
2216
+ logact -- logging flags
2217
+
2218
+ Returns:
2219
+ 1 if all moves succeeded, 0 at the first failure
2220
+ """
2221
+ for (mtype, tofile, fromfile, where) in moves:
2222
+ if not self.do_move(mtype, tofile, fromfile, where, logact): return 0
2223
+ return 1
2224
+
2225
+ def move_one(self, i, mtype, tofile, fromfile, where, logact):
2226
+ """
2227
+ Move one file in the record pass, or the only pass without -AL.
2228
+
2229
+ When the -AL copy pass already moved it (target there, source gone) the
2230
+ move counts as done without another call; otherwise do_move() moves it,
2231
+ which also finishes a move that failed in its child process.
2232
+ """
2233
+ if i in self.CPFILES and self.pass_moved(mtype, tofile, fromfile, where): return 1
2234
+ return self.do_move(mtype, tofile, fromfile, where, logact)
2235
+
2236
+ def pass_moved(self, mtype, tofile, fromfile, where):
2237
+ """
2238
+ Return true if the target of a move exists and its source is gone, checked quietly.
2239
+
2240
+ The source counts as gone only when its check finds no file (None), not
2241
+ when the check itself fails (FAILURE), so an error leads to a real move.
2242
+ """
2243
+ if mtype == 'L':
2244
+ check = lambda f: self.check_local_file(f, 0, 0)
2245
+ elif mtype == 'O':
2246
+ check = lambda f: self.check_object_file(f, where, 0, 0)
2247
+ else:
2248
+ check = lambda f: self.check_backup_file(f, where, 0, 0)
2249
+ return bool(check(tofile)) and check(fromfile) is None
2250
+
2251
+ def check_pass_copies(self, i, fname, afile, ofile, bucket, lsize):
2016
2252
  """
2017
- info = self.check_backup_file(file, endpoint, 0, self.PGOPT['errlog'])
2018
- if not info:
2019
- self.pglog("Error archiving {} to {}-{}".format(ofile, endpoint, file), self.LGEREX)
2020
- elif self.OPTS['SZ'][2]&2 and self.params['SZ'] and info['data_size'] != self.params['SZ'][i]:
2021
- self.pglog("Wrong Sizes: ({}-{}){}/({}){}".format(endpoint, file, info['data_size'], ofile, self.params['SZ'][i]), self.LGEREX)
2253
+ Check the targets the -AL copy pass copied a file to, for the record pass.
2254
+
2255
+ A copy whose child exited with an error counts as failed even when the
2256
+ target holds an older file of the same size (as with -OE); when the exit
2257
+ code is unknown, a target of size *lsize* counts as copied.
2258
+
2259
+ Args:
2260
+ i -- file index, the key of self.CPFILES
2261
+ fname -- archived file name, for the message
2262
+ afile -- GLADE target path the copy pass copied to, or None
2263
+ ofile -- object store key the copy pass copied to, or None
2264
+ bucket -- object store bucket
2265
+ lsize -- size of the local source file
2266
+
2267
+ Returns:
2268
+ (gdone, odone) -- 1 for each target that holds the file with size *lsize*;
2269
+ a 0 for a target given means its copy failed and is to be copied again
2270
+ """
2271
+ gdone = odone = 0
2272
+ if self.child_copy_failed(i):
2273
+ self.pglog("{}: copy in child process failed, copy again".format(fname), self.PGOPT['wrnlog'])
2274
+ return (0, 0)
2275
+ if afile:
2276
+ info = self.check_local_file(afile, 1, self.PGOPT['emerol']|self.PFSIZE)
2277
+ if info and info['data_size'] == lsize: gdone = 1
2278
+ if ofile:
2279
+ info = self.check_object_file(ofile, bucket, 1, self.PGOPT['emerol'])
2280
+ if info and info['data_size'] == lsize: odone = 1
2281
+ if afile and not gdone or ofile and not odone:
2282
+ self.pglog("{}: copy in child process failed, copy again".format(fname), self.PGOPT['wrnlog'])
2283
+ return (gdone, odone)
2022
2284
 
2023
2285
  def get_dataset_info(self):
2024
2286
  """
@@ -3222,7 +3484,7 @@ class DsArch(PgArch, PgMeta):
3222
3484
  self.ADDCNT += 1
3223
3485
  return bid
3224
3486
 
3225
- def move_web_files(self):
3487
+ def move_web_files(self, cpass=0):
3226
3488
  """
3227
3489
  Rename or relocate web files on disk and update their RDADB records.
3228
3490
 
@@ -3230,6 +3492,11 @@ class DsArch(PgArch, PgMeta):
3230
3492
  type changes (e.g. D→N). Supports -TS (to-saved) conversions and
3231
3493
  updates backup file notes via save_move_info().
3232
3494
  Sets self.RETSTAT on unresolved failures.
3495
+
3496
+ Args:
3497
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
3498
+ move pass (moves in up to N child processes, no records) and then 2
3499
+ for the record pass (see archive_in_passes())
3233
3500
  """
3234
3501
  tname = 'wfile'
3235
3502
  dsid = self.params['DS']
@@ -3295,7 +3562,7 @@ class DsArch(PgArch, PgMeta):
3295
3562
  self.validate_multiple_values(tname, self.ALLCNT, fnames)
3296
3563
  reorder = metatotal = metacnt = wcnt = ocnt = self.MODCNT = 0
3297
3564
  for i in range(bidx, self.ALLCNT):
3298
- if i > bidx and ((i-bidx)%20) == 0:
3565
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
3299
3566
  if metacnt > self.PGOPT['RSMAX']:
3300
3567
  metatotal += self.process_metadata("W", metacnt, self.PGOPT['emerol'])
3301
3568
  metacnt = 0
@@ -3335,8 +3602,14 @@ class DsArch(PgArch, PgMeta):
3335
3602
  wmove = 0
3336
3603
  elif pgrec['locflag'] == 'G':
3337
3604
  omove = 0
3605
+ if cpass == 1: # -AL move pass: move the files in a child process; the record pass sets the record
3606
+ moves = []
3607
+ if wmove and aolds[i] != anews[i]: moves.append(('L', anews[i], aolds[i], None))
3608
+ if omove and oolds[i] != onews[i]: moves.append(('O', onews[i], oolds[i], bucket))
3609
+ if moves: self.run_copy(i, self.move_pass_files, moves, self.PGOPT['emerol']|self.OVERRIDE)
3610
+ continue
3338
3611
  if wmove and aolds[i] != anews[i]:
3339
- ret = self.move_local_file(anews[i], aolds[i], self.PGOPT['emerol']|self.OVERRIDE)
3612
+ ret = self.move_one(i, 'L', anews[i], aolds[i], None, self.PGOPT['emerol']|self.OVERRIDE)
3340
3613
  if not ret:
3341
3614
  self.RETSTAT = 1
3342
3615
  continue
@@ -3344,7 +3617,7 @@ class DsArch(PgArch, PgMeta):
3344
3617
  wcnt += 1
3345
3618
  self.set_web_move(pgrec)
3346
3619
  if omove and oolds[i] != onews[i]:
3347
- if not self.move_object_file(onews[i], oolds[i], bucket, bucket, self.PGOPT['emerol']|self.OVERRIDE):
3620
+ if not self.move_one(i, 'O', onews[i], oolds[i], bucket, self.PGOPT['emerol']|self.OVERRIDE):
3348
3621
  self.RETSTAT = 1
3349
3622
  continue
3350
3623
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
@@ -3359,6 +3632,10 @@ class DsArch(PgArch, PgMeta):
3359
3632
  gindex = self.params['GI'][i]
3360
3633
  if gindex != pgrec['gindex'] and (gindex or (self.OPTS['GI'][2]&2) == 0):
3361
3634
  metacnt += self.record_meta_summary('W', dsid, gindex, pgrec['gindex'])
3635
+ if cpass == 1: # the record pass checks the moves, finishes failed ones and sets the records
3636
+ self.wait_copy_children()
3637
+ self.reset_errmsg(0) # the record pass finds and counts any error again
3638
+ return
3362
3639
  self.pglog("{}/{}/{}, Disk/Object/Record, of {} Web file{} moved".format(wcnt, ocnt, self.MODCNT, self.ALLCNT, s), self.PGOPT['emllog'])
3363
3640
  if metacnt > 0:
3364
3641
  metatotal += self.process_metadata('W', metacnt, self.PGOPT['emerol'])
@@ -3372,12 +3649,17 @@ class DsArch(PgArch, PgMeta):
3372
3649
  self.reset_rdadb_version(self.params['OD'])
3373
3650
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ''
3374
3651
 
3375
- def move_help_files(self):
3652
+ def move_help_files(self, cpass=0):
3376
3653
  """
3377
3654
  Rename or relocate help files on disk and update their RDADB records.
3378
3655
 
3379
3656
  Handles renaming within the same dataset and cross-dataset moves, along
3380
3657
  with any type changes. Sets self.RETSTAT on unresolved failures.
3658
+
3659
+ Args:
3660
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
3661
+ move pass (moves in up to N child processes, no records) and then 2
3662
+ for the record pass (see archive_in_passes())
3381
3663
  """
3382
3664
  tname = 'hfile'
3383
3665
  dsid = self.params['DS']
@@ -3435,7 +3717,7 @@ class DsArch(PgArch, PgMeta):
3435
3717
  self.validate_multiple_values(tname, self.ALLCNT, fnames)
3436
3718
  reorder = hcnt = ocnt = self.MODCNT = 0
3437
3719
  for i in range(bidx, self.ALLCNT):
3438
- if i > bidx and ((i-bidx)%20) == 0:
3720
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
3439
3721
  if self.PGLOG['DSCHECK']:
3440
3722
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
3441
3723
  if 'EM' in self.params:
@@ -3458,20 +3740,30 @@ class DsArch(PgArch, PgMeta):
3458
3740
  hmove = 0
3459
3741
  elif pgrec['locflag'] == 'G':
3460
3742
  omove = 0
3743
+ if cpass == 1: # -AL move pass: move the files in a child process; the record pass sets the record
3744
+ moves = []
3745
+ if hmove and aolds[i] != anews[i]: moves.append(('L', anews[i], aolds[i], None))
3746
+ if omove and oolds[i] != onews[i]: moves.append(('O', onews[i], oolds[i], bucket))
3747
+ if moves: self.run_copy(i, self.move_pass_files, moves, self.PGOPT['emerol']|self.OVERRIDE)
3748
+ continue
3461
3749
  if hmove and aolds[i] != anews[i]:
3462
- ret = self.move_local_file(anews[i], aolds[i], self.PGOPT['emerol']|self.OVERRIDE)
3750
+ ret = self.move_one(i, 'L', anews[i], aolds[i], None, self.PGOPT['emerol']|self.OVERRIDE)
3463
3751
  if not ret:
3464
3752
  self.RETSTAT = 1
3465
3753
  continue
3466
3754
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
3467
3755
  hcnt += 1
3468
3756
  if omove and oolds[i] != onews[i]:
3469
- if not self.move_object_file(onews[i], oolds[i], bucket, bucket, self.PGOPT['emerol']|self.OVERRIDE):
3757
+ if not self.move_one(i, 'O', onews[i], oolds[i], bucket, self.PGOPT['emerol']|self.OVERRIDE):
3470
3758
  self.RETSTAT = 1
3471
3759
  continue
3472
3760
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
3473
3761
  ocnt += 1
3474
3762
  if not self.set_one_helpfile(i, pgrec, hnews[i], fnames, tnews[i], None, tmpds): continue
3763
+ if cpass == 1: # the record pass checks the moves, finishes failed ones and sets the records
3764
+ self.wait_copy_children()
3765
+ self.reset_errmsg(0) # the record pass finds and counts any error again
3766
+ return
3475
3767
  self.pglog("{}/{}/{}/{}, Disk/Object/Record, of {} Help file{} moved".format(hcnt, ocnt, self.MODCNT, self.ALLCNT, s), self.PGOPT['emllog'])
3476
3768
  if self.PGLOG['DSCHECK']: self.set_dscheck_dcount(self.ALLCNT, 0, self.PGOPT['extlog'])
3477
3769
  if 'ON' in self.params: reorder = self.reorder_files(dsid, self.params['ON'], tname)
@@ -3481,7 +3773,7 @@ class DsArch(PgArch, PgMeta):
3481
3773
  self.reset_rdadb_version(self.params['OD'])
3482
3774
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ''
3483
3775
 
3484
- def saved_to_web_files(self):
3776
+ def saved_to_web_files(self, cpass=0):
3485
3777
  """
3486
3778
  Convert saved files to web files (-TS action).
3487
3779
 
@@ -3489,6 +3781,11 @@ class DsArch(PgArch, PgMeta):
3489
3781
  store), creates a new wfile RDADB record, and removes the old sfile
3490
3782
  record. Preserves backup links by updating bfile.note via save_move_info().
3491
3783
  Sets self.RETSTAT on unresolved failures.
3784
+
3785
+ Args:
3786
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
3787
+ move pass (moves in up to N child processes, no records) and then 2
3788
+ for the record pass (see archive_in_passes())
3492
3789
  """
3493
3790
  tname = 'wfile'
3494
3791
  dsid = self.params['DS']
@@ -3566,7 +3863,7 @@ class DsArch(PgArch, PgMeta):
3566
3863
  fnames = None
3567
3864
  reorder = metatotal = metacnt = wcnt = ocnt = dcnt = self.ADDCNT = self.MODCNT = 0
3568
3865
  for i in range(bidx, self.ALLCNT):
3569
- if i > bidx and ((i-bidx)%20) == 0:
3866
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
3570
3867
  if metacnt > self.PGOPT['RSMAX']:
3571
3868
  metatotal += self.process_metadata("W", metacnt, self.PGOPT['emerol'])
3572
3869
  metacnt = 0
@@ -3604,8 +3901,16 @@ class DsArch(PgArch, PgMeta):
3604
3901
  elif locflag == 'G':
3605
3902
  omove = 0
3606
3903
  dslocflags.add('G')
3904
+ if cpass == 1: # -AL move pass: move the file in a child process; the record pass sets the record
3905
+ moves = []
3906
+ if wmove: moves.append(('L', anews[i], aolds[i], None))
3907
+ if omove: moves.append(('U', onews[i], (anews[i] if wmove else aolds[i]), tobucket))
3908
+ if omove and not wmove: moves.append(('Y', None, aolds[i], None))
3909
+ if moves: self.run_copy(i, self.move_pass_files, moves, self.PGOPT['emerol']|self.OVERRIDE)
3910
+ continue
3911
+ inpass = cpass == 2 and i in self.CPFILES
3607
3912
  if wmove:
3608
- stat = self.move_local_file(anews[i], aolds[i], self.PGOPT['emerol']|self.OVERRIDE)
3913
+ stat = self.move_one(i, 'L', anews[i], aolds[i], None, self.PGOPT['emerol']|self.OVERRIDE)
3609
3914
  sfrom = 0
3610
3915
  if not stat:
3611
3916
  self.RETSTAT = 1
@@ -3613,13 +3918,18 @@ class DsArch(PgArch, PgMeta):
3613
3918
  wcnt += 1
3614
3919
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
3615
3920
  if omove:
3616
- stat = self.local_copy_object(onews[i], aolds[i], tobucket, None, self.PGOPT['emerol']|self.OVERRIDE)
3921
+ # upload from the new web path once the saved file was moved there
3922
+ if inpass and self.check_cross_copy(i, solds[i], 'O', onews[i], tobucket, pgrec['data_size']):
3923
+ stat = 1 # uploaded in the move pass
3924
+ else:
3925
+ stat = self.local_copy_object(onews[i], (aolds[i] if sfrom else anews[i]), tobucket, None, self.PGOPT['emerol']|self.OVERRIDE)
3617
3926
  if not stat:
3618
3927
  self.RETSTAT = 1
3619
3928
  continue
3620
3929
  ocnt += 1
3621
3930
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
3622
- if sfrom: self.delete_local_file(aolds[i], self.PGOPT['emerol'])
3931
+ if sfrom and (not inpass or self.check_local_file(aolds[i], 0, 0)):
3932
+ self.delete_local_file(aolds[i], self.PGOPT['emerol'])
3623
3933
  if pgrec['bid'] and not self.params['QF'][i]: self.params['QF'][i] = pgrec['bid']
3624
3934
  if pgrec['gindex'] and not self.params['GI'][i]: self.params['GI'][i] = pgrec['gindex']
3625
3935
  if pgrec['data_size'] and not self.params['SZ'][i]: self.params['SZ'][i] = pgrec['data_size']
@@ -3637,6 +3947,10 @@ class DsArch(PgArch, PgMeta):
3637
3947
  if 'GX' in self.params and self.PGOPT['GXTYP'].find(type) > -1:
3638
3948
  metacnt += self.record_meta_gather('W', dsid, wnews[i], self.params['DF'][i])
3639
3949
  self.cache_meta_tindex(dsid, wid, 'W')
3950
+ if cpass == 1: # the record pass checks the moves, finishes failed ones and sets the records
3951
+ self.wait_copy_children()
3952
+ self.reset_errmsg(0) # the record pass finds and counts any error again
3953
+ return
3640
3954
  self.pglog("{}/{}/{}/{}, Disk/Object/RecordDelete/RecordAdd, of {} Saved file{} moved".format(wcnt, ocnt, dcnt, self.ADDCNT, self.ALLCNT, s), self.PGOPT['emllog'])
3641
3955
  if metacnt > 0:
3642
3956
  metatotal += self.process_metadata('W', metacnt, self.PGOPT['emerol'])
@@ -3844,12 +4158,17 @@ class DsArch(PgArch, PgMeta):
3844
4158
  if (dcnt + reorder) > 0: self.reset_rdadb_version(dsid)
3845
4159
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
3846
4160
 
3847
- def move_saved_files(self):
4161
+ def move_saved_files(self, cpass=0):
3848
4162
  """
3849
4163
  Rename or relocate saved files on disk and update their RDADB records.
3850
4164
 
3851
4165
  Handles renaming within the same dataset and cross-dataset moves, along
3852
4166
  with any type changes. Sets self.RETSTAT on unresolved failures.
4167
+
4168
+ Args:
4169
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
4170
+ move pass (moves in up to N child processes, no records) and then 2
4171
+ for the record pass (see archive_in_passes())
3853
4172
  """
3854
4173
  tname = 'sfile'
3855
4174
  dsid = self.params['DS']
@@ -3911,7 +4230,7 @@ class DsArch(PgArch, PgMeta):
3911
4230
  chksize = self.PGLOG['DSCHECK']['size']
3912
4231
  reorder = self.MODCNT = scnt = 0
3913
4232
  for i in range(bidx, self.ALLCNT):
3914
- if i > bidx and ((i-bidx)%20) == 0:
4233
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
3915
4234
  if self.PGLOG['DSCHECK']:
3916
4235
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
3917
4236
  if 'EM' in self.params:
@@ -3940,14 +4259,23 @@ class DsArch(PgArch, PgMeta):
3940
4259
  not self.pgget("dsgroup", "", "{} and gindex = {}".format(dcnd, pgrec['gindex']), self.PGOPT['extlog'])):
3941
4260
  self.pglog("Group Index {} is not in RDADB for {}\n".format(pgrec['gindex'], dsid) +
3942
4261
  "Specify Original/New group index via options -OG/-GI", self.PGOPT['extlog'])
4262
+ if cpass == 1: # -AL move pass: move the files in a child process; the record pass sets the record
4263
+ moves = []
4264
+ if aolds[i] != anews[i]: moves.append(('L', anews[i], aolds[i], None))
4265
+ if moves: self.run_copy(i, self.move_pass_files, moves, self.PGOPT['emerol']|self.OVERRIDE)
4266
+ continue
3943
4267
  if aolds[i] != anews[i]:
3944
- if not self.move_local_file(anews[i], aolds[i], self.PGOPT['emerol']|self.OVERRIDE):
4268
+ if not self.move_one(i, 'L', anews[i], aolds[i], None, self.PGOPT['emerol']|self.OVERRIDE):
3945
4269
  self.RETSTAT = 1
3946
4270
  continue
3947
4271
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
3948
4272
  scnt += 1
3949
4273
  self.set_one_savedfile(i, pgrec, snews[i], fnames, tnews[i], None, tmpds)
3950
4274
  if pgrec['bid']: self.save_move_info(pgrec['bid'], solds[i], type, 'S', self.params['OD'], snews[i], tnews[i], 'S', dsid)
4275
+ if cpass == 1: # the record pass checks the moves, finishes failed ones and sets the records
4276
+ self.wait_copy_children()
4277
+ self.reset_errmsg(0) # the record pass finds and counts any error again
4278
+ return
3951
4279
  self.pglog("{}/{}, Disk/Record, of {} Saved file{} moved".format(scnt, self.MODCNT, self.ALLCNT, s), self.PGOPT['emllog'])
3952
4280
  if self.PGLOG['DSCHECK']:
3953
4281
  self.set_dscheck_dcount(self.ALLCNT, chksize, self.PGOPT['extlog'])
@@ -3959,13 +4287,18 @@ class DsArch(PgArch, PgMeta):
3959
4287
  self.reset_rdadb_version(self.params['OD'])
3960
4288
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
3961
4289
 
3962
- def move_backup_files(self):
4290
+ def move_backup_files(self, cpass=0):
3963
4291
  """
3964
4292
  Move Quasar backup files to a different dataset and update RDADB records.
3965
4293
 
3966
4294
  Transfers ownership of bfile records to the target dataset (-OD) and
3967
4295
  updates the backup archive note via save_move_info().
3968
4296
  Sets self.RETSTAT on failures.
4297
+
4298
+ Args:
4299
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
4300
+ move pass (moves in up to N child processes, no records) and then 2
4301
+ for the record pass (see archive_in_passes())
3969
4302
  """
3970
4303
  s = 's' if self.ALLCNT > 1 else ''
3971
4304
  tname = 'bfile'
@@ -4014,7 +4347,7 @@ class DsArch(PgArch, PgMeta):
4014
4347
  chksize = self.PGLOG['DSCHECK']['size']
4015
4348
  reorder = self.MODCNT = bcnt = dcnt = 0
4016
4349
  for i in range(bidx, self.ALLCNT):
4017
- if i > bidx and ((i-bidx)%20) == 0:
4350
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
4018
4351
  if self.PGLOG['DSCHECK']:
4019
4352
  self.set_dscheck_dcount(i, chksize, self.PGOPT['extlog'])
4020
4353
  if 'EM' in self.params:
@@ -4037,20 +4370,31 @@ class DsArch(PgArch, PgMeta):
4037
4370
  continue
4038
4371
  dmove = bmove = 1
4039
4372
  if type == 'B': dmove = 0
4373
+ if cpass == 1: # -AL move pass: move the files in a child process; the record pass sets the record
4374
+ moves = []
4375
+ if qolds[i] != qnews[i]:
4376
+ if bmove: moves.append(('B', qnews[i], qolds[i], bkend))
4377
+ if dmove: moves.append(('B', qnews[i], qolds[i], drend))
4378
+ if moves: self.run_copy(i, self.move_pass_files, moves, self.PGOPT['emerol']|self.OVERRIDE)
4379
+ continue
4040
4380
  if qolds[i] != qnews[i]:
4041
4381
  if bmove:
4042
- if not self.move_backup_file(qnews[i], qolds[i], bkend, self.PGOPT['emerol']|self.OVERRIDE):
4382
+ if not self.move_one(i, 'B', qnews[i], qolds[i], bkend, self.PGOPT['emerol']|self.OVERRIDE):
4043
4383
  self.RETSTAT = 1
4044
4384
  continue
4045
4385
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
4046
4386
  bcnt += 1
4047
4387
  if dmove:
4048
- if not self.move_backup_file(qnews[i], qolds[i], drend, self.PGOPT['emerol']|self.OVERRIDE):
4388
+ if not self.move_one(i, 'B', qnews[i], qolds[i], drend, self.PGOPT['emerol']|self.OVERRIDE):
4049
4389
  self.RETSTAT = 1
4050
4390
  continue
4051
4391
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
4052
4392
  dcnt += 1
4053
4393
  self.set_one_backfile(i, pgrec, bnews[i], fnames, tnews[i], tmpds)
4394
+ if cpass == 1: # the record pass checks the moves, finishes failed ones and sets the records
4395
+ self.wait_copy_children()
4396
+ self.reset_errmsg(0) # the record pass finds and counts any error again
4397
+ return
4054
4398
  self.pglog("{}/{}/{}, Quasar/Drdata/Record, of {} Backup file{} moved".format(bcnt, dcnt, self.MODCNT, self.ALLCNT, s), self.PGOPT['emllog'])
4055
4399
  if self.PGLOG['DSCHECK']:
4056
4400
  self.set_dscheck_dcount(self.ALLCNT, chksize, self.PGOPT['extlog'])
@@ -4062,7 +4406,7 @@ class DsArch(PgArch, PgMeta):
4062
4406
  self.reset_rdadb_version(self.params['OD'])
4063
4407
  if 'EM' in self.params: self.PGLOG['PRGMSG'] = ""
4064
4408
 
4065
- def web_to_saved_files(self):
4409
+ def web_to_saved_files(self, cpass=0):
4066
4410
  """
4067
4411
  Convert web files to saved files (-TW action).
4068
4412
 
@@ -4070,6 +4414,11 @@ class DsArch(PgArch, PgMeta):
4070
4414
  new sfile RDADB record, and removes the old wfile record. Optionally
4071
4415
  also deletes the file from the object store. Preserves backup links.
4072
4416
  Sets self.RETSTAT on unresolved failures.
4417
+
4418
+ Args:
4419
+ cpass -- 0 for a single pass; with -AL N, archive_in_passes() calls 1 for the
4420
+ move pass (moves in up to N child processes, no records) and then 2
4421
+ for the record pass (see archive_in_passes())
4073
4422
  """
4074
4423
  s = 's' if self.ALLCNT > 1 else ''
4075
4424
  tname = 'sfile'
@@ -4147,7 +4496,7 @@ class DsArch(PgArch, PgMeta):
4147
4496
  fnames = None
4148
4497
  metatotal = metacnt = reorder = wcnt = ocnt = dcnt = self.ADDCNT = self.MODCNT = 0
4149
4498
  for i in range(bidx, self.ALLCNT):
4150
- if i > bidx and ((i-bidx)%20) == 0:
4499
+ if cpass != 1 and i > bidx and ((i-bidx)%20) == 0:
4151
4500
  if metacnt >= self.PGOPT['RSMAX']:
4152
4501
  metatotal += self.process_meta_delete("W", self.PGOPT['emerol'])
4153
4502
  metacnt = 0
@@ -4179,19 +4528,29 @@ class DsArch(PgArch, PgMeta):
4179
4528
  wfrom = 0
4180
4529
  elif locflag == 'G':
4181
4530
  ofrom = 0
4531
+ if cpass == 1: # -AL move pass: move the file in a child process; the record pass sets the record
4532
+ moves = [('L', anews[i], aolds[i], None) if wfrom else ('G', anews[i], oolds[i], frombucket)]
4533
+ if ofrom: moves.append(('X', None, oolds[i], frombucket))
4534
+ self.run_copy(i, self.move_pass_files, moves, self.PGOPT['emerol']|self.OVERRIDE)
4535
+ continue
4536
+ inpass = cpass == 2 and i in self.CPFILES
4182
4537
  if wfrom:
4183
- stat = self.move_local_file(anews[i], aolds[i], self.PGOPT['emerol']|self.OVERRIDE)
4538
+ stat = self.move_one(i, 'L', anews[i], aolds[i], None, self.PGOPT['emerol']|self.OVERRIDE)
4184
4539
  wfrom = 0
4185
4540
  wcnt += 1
4186
4541
  else:
4187
- stat = self.object_copy_local(anews[i], oolds[i], frombucket, self.PGOPT['emerol']|self.OVERRIDE)
4542
+ if inpass and self.check_cross_copy(i, wolds[i], 'G', anews[i], None, pgrec['data_size']):
4543
+ stat = 1 # downloaded in the move pass
4544
+ else:
4545
+ stat = self.object_copy_local(anews[i], oolds[i], frombucket, self.PGOPT['emerol']|self.OVERRIDE)
4188
4546
  ocnt += 1
4189
4547
  if not stat:
4190
4548
  self.RETSTAT = 1
4191
4549
  continue
4192
4550
  if self.PGLOG['DSCHECK']: chksize += pgrec['data_size']
4193
4551
  if wfrom: self.delete_local_file(aolds[i], self.PGOPT['emerol'])
4194
- if ofrom: self.delete_object_file(oolds[i], frombucket, self.PGOPT['emerol'])
4552
+ if ofrom and (not inpass or self.check_object_file(oolds[i], frombucket, 0, 0)):
4553
+ self.delete_object_file(oolds[i], frombucket, self.PGOPT['emerol'])
4195
4554
  if self.PGOPT['GXTYP'].find(type) > -1 and ('DX' in self.params or pgrec and pgrec['meta_link'] and pgrec['meta_link'] != 'N' and 'KM' not in self.params):
4196
4555
  metacnt += self.record_meta_delete('W', dsid, pgrec['wfile'])
4197
4556
  if pgrec['bid'] and not self.params['QF'][i]: self.params['QF'][i] = pgrec['bid']
@@ -4208,6 +4567,10 @@ class DsArch(PgArch, PgMeta):
4208
4567
  info = self.get_file_origin_info(snews[i], pgrec)
4209
4568
  self.set_one_savedfile(i, None, snews[i], fnames, tnews[i], info, dsid, 12)
4210
4569
  if pgrec['bid']: self.save_move_info(pgrec['bid'], wolds[i], type, 'W', self.params['OD'], snews[i], tnews[i], 'S', dsid)
4570
+ if cpass == 1: # the record pass checks the moves, finishes failed ones and sets the records
4571
+ self.wait_copy_children()
4572
+ self.reset_errmsg(0) # the record pass finds and counts any error again
4573
+ return
4211
4574
  self.pglog("{}/{}/{}/{}, Disk/Object/RecordDelete/RecordAdd, of {} Web file{} moved".format(wcnt, ocnt, dcnt, self.ADDCNT, self.ALLCNT, s), self.PGOPT['emllog'])
4212
4575
  if metacnt > 0:
4213
4576
  metatotal += self.process_meta_delete('W', self.PGOPT['emerol'])