Ver código fonte

Initial mysync implementation

master
Dirk Alders 4 anos atrás
pai
commit
81f7cf4b2e
3 arquivos alterados com 176 adições e 0 exclusões
  1. 14
    0
      example/backup_config.py
  2. 25
    0
      example/sync_config.py
  3. 137
    0
      mysync

+ 14
- 0
example/backup_config.py Ver arquivo

@@ -0,0 +1,14 @@
1
+import os
2
+import sys
3
+
4
+backup = True
5
+backup_basepath = '/data/backup/ahorn'
6
+
7
+installations = ['mint', 'kubuntu', 'openSuSE']
8
+for installation in installations:
9
+    setattr(sys.modules[__name__], 'home_%s_remotepath' % installation, 'dirk@ahorn:/home')
10
+    setattr(sys.modules[__name__], 'home_%s_localpath' % installation, os.path.join(installation, 'home'))
11
+
12
+user_data_remotepath = 'dirk@ahorn:/user_data'
13
+user_data_localpath = 'user_data'
14
+user_data_skip = ['static_data']

+ 25
- 0
example/sync_config.py Ver arquivo

@@ -0,0 +1,25 @@
1
+import os
2
+import sys
3
+
4
+backup = False
5
+
6
+hosts = ['ahorn', 'erle', 'linde']
7
+__basepath__ = '/user_data'
8
+entries = (
9
+    ('dirk@%s:/user_data/bin', os.path.join(__basepath__, 'bin'), None),
10
+    ('dirk@%s:/user_data/data', os.path.join(__basepath__, 'data'), None),
11
+    ('dirk@%s:/user_data/static_data', os.path.join(__basepath__, 'static_data'), ['Audio', 'timeshift', 'lost+found']),
12
+    ('dirk@%s:/home', '/home', None),
13
+)
14
+
15
+
16
+for host in hosts:
17
+    for remote, local, skip in entries:
18
+        setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_remotepath', remote % host)
19
+        setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_localpath', local)
20
+        if skip is not None:
21
+            setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_skip', skip)
22
+
23
+
24
+mount_mockery_audio_remotepath = 'root@mount-mockery.de:/data/audio/items'
25
+mount_mockery_audio_localpath = os.path.join(__basepath__, 'static_data', 'dirk', 'Audio')

+ 137
- 0
mysync Ver arquivo

@@ -0,0 +1,137 @@
1
+#!/usr/bin/python3
2
+# -*- coding: utf-8 -*-
3
+#
4
+
5
+import config
6
+import os
7
+import sys
8
+import time
9
+import subprocess
10
+
11
+DEBUG = False
12
+
13
+RSYNC_REMOTE_COMMAND = 'sudo rsync'
14
+TIME_FORMAT = "%Y-%m-%d_%H-%M-%S"
15
+try:
16
+    basepath = config.backup_basepath
17
+except AttributeError:
18
+    basepath = os.path.dirname(os.path.abspath(__file__))
19
+timestamp = time.strftime(TIME_FORMAT)
20
+output_lines = 5
21
+
22
+PROP_REMOTE = 'remotepath'
23
+PROP_LOCAL = 'localpath'
24
+PROP_SKIP = 'skip'
25
+
26
+
27
+HEADER = '\033[95m'
28
+OKBLUE = '\033[94m'
29
+OKGREEN = '\033[92m'
30
+WARNING = '\033[93m'
31
+FAIL = '\033[91m'
32
+ENDC = '\033[0m'
33
+BOLD = '\033[1m'
34
+UNDERLINE = '\033[4m'
35
+CLEAR_REST_OF_LINE = '\033[K'
36
+
37
+
38
+def status_line(line):
39
+    sys.stdout.write('\r  .....' + line.replace('\n', '')[-60:] + CLEAR_REST_OF_LINE)
40
+    sys.stdout.flush()
41
+
42
+
43
+def parse_config_to_dict():
44
+    d = {}
45
+    config_dict = dir(config)
46
+    for key in config_dict:
47
+        if key.endswith('_' + PROP_REMOTE):
48
+            entry_name = key[:-len(PROP_REMOTE) - 1]
49
+            if entry_name + '_' + PROP_LOCAL in config_dict:
50
+                d[entry_name] = {}
51
+                for key in config_dict:
52
+                    if key.startswith(entry_name):
53
+                        d[entry_name][key[key.rfind('_') + 1:]] = getattr(config, key)
54
+    return d
55
+
56
+
57
+def rsync_command(**kwargs):
58
+    def path_filter(path):
59
+        return path if path.endswith('/') else path + '/'
60
+    #
61
+    if config.backup:
62
+        localpath = path_filter(os.path.join(basepath, timestamp, kwargs[PROP_LOCAL]))
63
+        if timestamp not in localpath:
64
+            sys.stderr.write('Unable to backup to %s. Please give a relative path for a backup!\n' % localpath)
65
+            localpath = None
66
+    else:
67
+        localpath = path_filter(os.path.join(basepath, kwargs.get('localpath', '')))
68
+    if localpath is not None:
69
+        remotepath = path_filter(kwargs.get(PROP_REMOTE))
70
+        skip = kwargs.get(PROP_SKIP)
71
+        cmd_l = ['rsync', '-avn' if DEBUG else '-av', '--delete', '--rsync-path="%s"' % RSYNC_REMOTE_COMMAND, remotepath, localpath]
72
+        if skip is not None:
73
+            cmd_l.append('--exclude=%s' % ','.join(skip))
74
+        if not os.path.exists(localpath):
75
+            return 'mkdir -p %s && ' % localpath + ' '.join(cmd_l)
76
+        return ' '.join(cmd_l)
77
+
78
+
79
+def link_copy_command():
80
+    newest_backup = None
81
+    if not os.path.exists(basepath):
82
+        os.system('mkdir -p ' + basepath)
83
+    for path in os.listdir(basepath):
84
+        try:
85
+            t = time.strptime(path, TIME_FORMAT)
86
+            if newest_backup is None or t > newest_backup:
87
+                newest_backup = t
88
+        except ValueError:
89
+            pass   # seems to be not one of my backups
90
+    if newest_backup is not None:
91
+        newest_backup = os.path.join(basepath, time.strftime(TIME_FORMAT, newest_backup))
92
+        return 'cp -alv ' + newest_backup + ' ' + os.path.join(basepath, timestamp)
93
+
94
+
95
+def help_msg():
96
+    backup_dict = parse_config_to_dict()
97
+    print("Possible arguments are:")
98
+    for entry in backup_dict:
99
+        print("  * %s (%s -> %s)" % (entry + (20 - len(entry)) * ' ', backup_dict[entry][PROP_REMOTE] + (40 - len(backup_dict[entry][PROP_REMOTE])) * ' ', backup_dict[entry][PROP_LOCAL] + (40 - len(backup_dict[entry][PROP_LOCAL])) * ' '))
100
+
101
+def make_link_copy():
102
+    cmd = link_copy_command()
103
+    if cmd is not None:
104
+        sys.stdout.write(BOLD + HEADER + UNDERLINE + 'Creating Link copy of last backup.' + ENDC + '\n\n')
105
+        with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
106
+            err = b""
107
+            for line in p.stdout:
108
+                status_line(line.decode('utf-8'))
109
+            err += p.stderr.read()
110
+        sys.stdout.write('\n' + FAIL + BOLD + err.decode('utf-8') + ENDC)
111
+        sys.stdout.write('\n' + 80 * '-' + '\n')
112
+
113
+#
114
+if __name__ == '__main__':
115
+    backup_dict = parse_config_to_dict()
116
+    #
117
+    backup_dict = parse_config_to_dict()
118
+    did_nothing = True
119
+    for entry in backup_dict:
120
+        if entry in sys.argv:
121
+            cmd = rsync_command(**backup_dict[entry])
122
+            if cmd is not None:
123
+                if config.backup and did_nothing:
124
+                    make_link_copy()
125
+                did_nothing = False
126
+                sys.stdout.write(BOLD + HEADER + UNDERLINE + 'Doing %s for %s:' % ('backup' if config.backup else 'sync', repr(entry)) + ENDC + '\n\n')
127
+                with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
128
+                    err = b""
129
+                    for line in p.stdout:
130
+                        status_line(line.decode('utf-8'))
131
+                    err += p.stderr.read()
132
+                sys.stdout.write('\n' + FAIL + BOLD + err.decode('utf-8') + ENDC)
133
+                sys.stdout.write('\n' + 80 * '-' + '\n')
134
+    if did_nothing:
135
+        help_msg()
136
+else:
137
+    help_msg()

Carregando…
Cancelar
Salvar