~scode/duplicity/misc

« back to all changes in this revision

Viewing changes to duplicity-bin

  • Committer: Peter Schuller
  • Date: 2009-07-08 16:51:59 UTC
  • mfrom: (548.1.10 duplicity-src)
  • Revision ID: peter.schuller@infidyne.com-20090708165159-v65s0znz09fnq29w
* merge latest trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
90
90
    if not globals.encryption or globals.use_agent:
91
91
        return ""
92
92
 
93
 
    # no passphrase if --list-current and --archive-dir
 
93
    # no passphrase if --list-current
94
94
    elif (action == "list-current"):
95
95
        return ""
96
96
 
122
122
            if n == 2:
123
123
                pass1 = globals.gpg_profile.passphrase
124
124
            else:
125
 
                pass1 = getpass.getpass("GnuPG passphrase: ")
 
125
                if globals.gpg_profile.passphrase:
 
126
                    pass1 = globals.gpg_profile.passphrase
 
127
                else:
 
128
                    pass1 = getpass.getpass("GnuPG passphrase: ")
126
129
 
127
130
            if n == 1:
128
131
                pass2 = pass1
773
776
                   _("Rerun command with --force option to actually delete."))
774
777
 
775
778
 
776
 
def sync_archive(col_stats):
 
779
def sync_archive():
777
780
    """
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.
781
784
 
782
 
    @type col_stats: CollectionStatus object
783
 
    @param col_stats: collection status
784
 
 
785
785
    @rtype: void
786
786
    @return: void
787
787
    """
788
788
    suffixes = [".g", ".gpg", ".z", ".gz"]
789
789
 
790
790
    def get_metafiles(filelist):
 
791
        """
 
792
        Return metafiles of interest from the file list.
 
793
        Files of interest are:
 
794
          sigtar - signature files
 
795
          manifest - signature files
 
796
        Files excluded are:
 
797
          non-duplicity files
 
798
          duplicity partial files
 
799
 
 
800
        @rtype: list
 
801
        @return: list of duplicity metadata files
 
802
        """
791
803
        metafiles = {}
 
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):
 
807
            if not pr:
 
808
                continue
 
809
            if pr.partial:
 
810
                continue
 
811
            if pr.encrypted:
 
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
798
817
                else:
799
818
                    metafiles[fn] = fn
800
 
        return metafiles
 
819
        return metafiles, need_passphrase
801
820
 
802
821
    def copy_raw(src_iter, filename):
803
822
        """
813
832
            file.write(data)
814
833
        file.close()
815
834
 
 
835
    def resolve_basename(fn):
 
836
        """
 
837
        @return: (parsedresult, local_name, remote_name)
 
838
        """
 
839
        suffix = file_naming.get_suffix(globals.encryption, not globals.encryption)
 
840
        rem_name = fn + suffix
 
841
 
 
842
        pr = file_naming.parse(fn)
 
843
        if pr.manifest:
 
844
            suffix = file_naming.get_suffix(False, False)
 
845
        else:
 
846
            suffix = file_naming.get_suffix(False, True)
 
847
        loc_name = fn + suffix
 
848
 
 
849
        return (pr, loc_name, rem_name)
 
850
 
 
851
    def remove_local(fn):
 
852
        pr, loc_name, rem_name = resolve_basename(fn)
 
853
 
 
854
        del_name = globals.archive_dir.append(loc_name).name
 
855
 
 
856
        log.Notice(_("Deleting local %s (not authoritative at backend).") % del_name)
 
857
        os.unlink(del_name)
 
858
 
816
859
    def copy_to_local(fn):
817
860
        """
818
861
        Copy remote file fn to local cache.
848
891
 
849
892
        log.Notice(_("Copying %s to local cache.") % fn)
850
893
 
851
 
        suffix = file_naming.get_suffix(globals.encryption, False)
852
 
        rem_name = fn + suffix
853
 
 
854
 
        pr = file_naming.parse(fn)
855
 
        if pr.manifest:
856
 
            suffix = file_naming.get_suffix(False, False)
857
 
        else:
858
 
            suffix = file_naming.get_suffix(False, True)
859
 
        loc_name = fn + suffix
 
894
        pr, loc_name, rem_name = resolve_basename(fn)
860
895
 
861
896
        fileobj = globals.backend.get_fileobj_read(rem_name)
862
897
        src_iter = SrcIter(fileobj)
863
898
        if pr.manifest:
864
 
            copy_raw(src_iter, globals.archive_dir.append(loc_name).name)
 
899
            copy_raw(src_iter,
 
900
                     globals.archive_dir.append(loc_name).name)
865
901
        else:
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,
 
904
                              size = sys.maxint)
867
905
 
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)
871
909
 
872
910
    # get local metafile list
873
911
    loclist = globals.archive_dir.listdir()
874
 
    local_metafiles = get_metafiles(loclist)
875
 
 
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)
 
913
 
 
914
    if rem_needpass or loc_needpass:
 
915
        globals.gpg_profile.passphrase = get_passphrase(1, "sync")
 
916
 
 
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()
 
922
 
 
923
    local_missing = []
 
924
    local_spurious = []
 
925
 
 
926
    for key in remote_keys:
 
927
        if not key in local_keys:
 
928
            local_missing.append(key)
 
929
 
 
930
    for key in local_keys:
 
931
        if not key in remote_keys:
 
932
            local_spurious.append(key)
885
933
 
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."))
889
937
    else:
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:
 
943
                remove_local(fn)
893
944
            for fn in local_missing:
894
945
                copy_to_local(fn)
895
946
        else:
896
 
            log.Notice(_("Sync would copy the following from remote to local:")
897
 
                       + "\n" + "\n".join(local_missing))
 
947
            if local_missing:
 
948
                log.Notice(_("Sync would copy the following from remote to local:")
 
949
                           + "\n" + "\n".join(local_missing))
 
950
            if local_spurious:
 
951
                log.Notice(_("Sync would remove the following spurious local files:")
 
952
                           + "\n" + "\n".join(local_spurious))
898
953
 
899
954
 
900
955
def check_last_manifest(col_stats):
1035
1090
 
1036
1091
    check_resources(action)
1037
1092
 
 
1093
    # check archive synch with remote, fix if needed
 
1094
    sync_archive()
 
1095
 
1038
1096
    # get current collection status
1039
1097
    col_stats = collections.CollectionsStatus(globals.backend,
1040
1098
                                              globals.archive_dir).set_values()
1041
1099
 
1042
 
    # check archive synch with remote, fix if needed
1043
 
    sync_archive(col_stats)
1044
 
 
1045
1100
    while True:
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.