773
776
_("Rerun command with --force option to actually delete."))
776
def sync_archive(col_stats):
778
781
Synchronize local archive manifest file and sig chains to remote archives.
779
782
Copy missing files from remote to local as needed to make sure the local
780
783
archive is synchronized to remote storage.
782
@type col_stats: CollectionStatus object
783
@param col_stats: collection status
788
788
suffixes = [".g", ".gpg", ".z", ".gz"]
790
790
def get_metafiles(filelist):
792
Return metafiles of interest from the file list.
793
Files of interest are:
794
sigtar - signature files
795
manifest - signature files
798
duplicity partial files
801
@return: list of duplicity metadata files
804
need_passphrase = False
792
805
for fn in filelist:
793
806
pr = file_naming.parse(fn)
794
if pr and (pr.type in ["full-sig", "new-sig"] or pr.manifest):
812
need_passphrase = True
813
if pr.type in ["full-sig", "new-sig"] or pr.manifest:
795
814
base, ext = os.path.splitext(fn)
796
815
if ext in suffixes:
797
816
metafiles[base] = fn
799
818
metafiles[fn] = fn
819
return metafiles, need_passphrase
802
821
def copy_raw(src_iter, filename):
835
def resolve_basename(fn):
837
@return: (parsedresult, local_name, remote_name)
839
suffix = file_naming.get_suffix(globals.encryption, not globals.encryption)
840
rem_name = fn + suffix
842
pr = file_naming.parse(fn)
844
suffix = file_naming.get_suffix(False, False)
846
suffix = file_naming.get_suffix(False, True)
847
loc_name = fn + suffix
849
return (pr, loc_name, rem_name)
851
def remove_local(fn):
852
pr, loc_name, rem_name = resolve_basename(fn)
854
del_name = globals.archive_dir.append(loc_name).name
856
log.Notice(_("Deleting local %s (not authoritative at backend).") % del_name)
816
859
def copy_to_local(fn):
818
861
Copy remote file fn to local cache.
849
892
log.Notice(_("Copying %s to local cache.") % fn)
851
suffix = file_naming.get_suffix(globals.encryption, False)
852
rem_name = fn + suffix
854
pr = file_naming.parse(fn)
856
suffix = file_naming.get_suffix(False, False)
858
suffix = file_naming.get_suffix(False, True)
859
loc_name = fn + suffix
894
pr, loc_name, rem_name = resolve_basename(fn)
861
896
fileobj = globals.backend.get_fileobj_read(rem_name)
862
897
src_iter = SrcIter(fileobj)
864
copy_raw(src_iter, globals.archive_dir.append(loc_name).name)
900
globals.archive_dir.append(loc_name).name)
866
gpg.GzipWriteFile(src_iter, globals.archive_dir.append(loc_name).name)
902
gpg.GzipWriteFile(src_iter,
903
globals.archive_dir.append(loc_name).name,
868
906
# get remote metafile list
869
907
remlist = globals.backend.list()
870
remote_metafiles = get_metafiles(remlist)
908
remote_metafiles, rem_needpass = get_metafiles(remlist)
872
910
# get local metafile list
873
911
loclist = globals.archive_dir.listdir()
874
local_metafiles = get_metafiles(loclist)
876
# we have the list of metafiles on both sides, now we remove
877
# the intersection of local and remote metafiles. What's left
878
# are the missing file names on the other side.
912
local_metafiles, loc_needpass = get_metafiles(loclist)
914
if rem_needpass or loc_needpass:
915
globals.gpg_profile.passphrase = get_passphrase(1, "sync")
917
# we have the list of metafiles on both sides. remote is always
918
# authoritative. figure out which are local spurious (should not
919
# be there) and missing (should be there but are not).
879
920
local_keys = local_metafiles.keys()
880
for base in local_keys:
881
if remote_metafiles.has_key(base):
882
del local_metafiles[base]
883
del remote_metafiles[base]
884
local_missing = remote_metafiles.keys()
921
remote_keys = remote_metafiles.keys()
926
for key in remote_keys:
927
if not key in local_keys:
928
local_missing.append(key)
930
for key in local_keys:
931
if not key in remote_keys:
932
local_spurious.append(key)
886
934
# finally finish the process
887
if not local_missing:
935
if not local_missing and not local_spurious:
888
936
log.Notice(_("Local and Remote metadata are synchronized, no sync needed."))
890
938
local_missing.sort()
939
local_spurious.sort()
891
940
if not globals.dry_run:
892
941
log.Notice(_("Synchronizing remote metadata to local cache..."))
942
for fn in local_spurious:
893
944
for fn in local_missing:
894
945
copy_to_local(fn)
896
log.Notice(_("Sync would copy the following from remote to local:")
897
+ "\n" + "\n".join(local_missing))
948
log.Notice(_("Sync would copy the following from remote to local:")
949
+ "\n" + "\n".join(local_missing))
951
log.Notice(_("Sync would remove the following spurious local files:")
952
+ "\n" + "\n".join(local_spurious))
900
955
def check_last_manifest(col_stats):
1036
1091
check_resources(action)
1093
# check archive synch with remote, fix if needed
1038
1096
# get current collection status
1039
1097
col_stats = collections.CollectionsStatus(globals.backend,
1040
1098
globals.archive_dir).set_values()
1042
# check archive synch with remote, fix if needed
1043
sync_archive(col_stats)
1046
1101
# if we have to clean up the last partial, then col_stats are invalidated
1047
1102
# and we have to start the process all over again until clean.