1
|
1 |
|
from plugin.core.backup.models import BackupArchive |
2
|
|
|
|
3
|
1 |
|
import logging |
4
|
1 |
|
import os |
5
|
1 |
|
import shutil |
6
|
1 |
|
import tarfile |
7
|
1 |
|
import tempfile |
8
|
|
|
|
9
|
1 |
|
log = logging.getLogger(__name__) |
10
|
|
|
|
11
|
|
|
|
12
|
1 |
|
class ArchiveTask(object): |
13
|
1 |
|
@classmethod |
14
|
|
|
def run(cls, group, period, key, revisions, policy): |
15
|
|
|
base_path, date = cls.get_period(group, period, key) |
16
|
|
|
|
17
|
|
|
log.info('Archiving %s: %s (%d revisions, group: %r)', period, date, len(revisions), group) |
18
|
|
|
|
19
|
|
|
# Ensure an archive hasn't already been created |
20
|
|
|
if os.path.exists(base_path + '.bar') or os.path.exists(base_path + '.tar.gz'): |
21
|
|
|
log.info('Archive already exists for %r', key) |
22
|
|
|
return |
23
|
|
|
|
24
|
|
|
# Create archive of revisions |
25
|
|
|
tar_path, files = cls.create_archive(base_path, revisions) |
26
|
|
|
|
27
|
|
|
# Construct archive metadata |
28
|
|
|
archive = BackupArchive( |
29
|
|
|
date=key, |
30
|
|
|
archive=os.path.basename(tar_path), |
31
|
|
|
|
32
|
|
|
files=[ |
33
|
|
|
os.path.relpath(path, base_path) |
34
|
|
|
for path in files |
35
|
|
|
] |
36
|
|
|
) |
37
|
|
|
|
38
|
|
|
# Move archive to directory |
39
|
|
|
try: |
40
|
|
|
shutil.move(tar_path, base_path + '.tar.gz') |
41
|
|
|
except Exception, ex: |
42
|
|
|
log.warn('Unable to move archive to backups directory - %s', ex, exc_info=True) |
43
|
|
|
return False |
44
|
|
|
|
45
|
|
|
# Write archive metadata |
46
|
|
|
if not archive.save(base_path + '.bar'): |
47
|
|
|
# Unable to write metadata |
48
|
|
|
return False |
49
|
|
|
|
50
|
|
|
# Cleanup archived directory |
51
|
|
|
cls.cleanup(base_path, files) |
52
|
|
|
|
53
|
|
|
log.info('Archived %s: %s (%d files, group: %r)', period, date, len(files), group) |
54
|
|
|
return True |
55
|
|
|
|
56
|
1 |
|
@staticmethod |
57
|
|
|
def cleanup(base_path, files): |
58
|
|
|
# Delete files that have been archived |
59
|
|
|
for path in files: |
60
|
|
|
try: |
61
|
|
|
os.remove(path) |
62
|
|
|
except Exception, ex: |
63
|
|
|
log.info('Unable to remove file: %r - %s', path, ex, exc_info=True) |
64
|
|
|
|
65
|
|
|
# Delete old directory (if no files exist inside it) |
66
|
|
|
try: |
67
|
|
|
os.rmdir(base_path) |
68
|
|
|
except Exception, ex: |
69
|
|
|
log.info('Unable to remove directory: %r (probably contains skipped files) - %s', base_path, ex, exc_info=True) |
70
|
|
|
|
71
|
|
|
# |
72
|
|
|
# Archive creation |
73
|
|
|
# |
74
|
|
|
|
75
|
1 |
|
@classmethod |
76
|
|
|
def create_archive(cls, base_path, revisions): |
77
|
|
|
# Create temporary path for archive |
78
|
|
|
path = os.path.join(tempfile.mkdtemp(), os.path.basename(base_path) + '.tar.gz') |
79
|
|
|
|
80
|
|
|
# Create archive |
81
|
|
|
with tarfile.open(path, 'w:gz') as tar: |
82
|
|
|
# Add revisions to archive |
83
|
|
|
files = cls.add_revisions(base_path, tar, revisions) |
84
|
|
|
|
85
|
|
|
return path, files |
86
|
|
|
|
87
|
1 |
|
@classmethod |
88
|
|
|
def add_revisions(cls, base_path, tar, revisions): |
89
|
|
|
files = [] |
90
|
|
|
|
91
|
|
|
# Add revisions to tar archive |
92
|
|
|
for revision in revisions: |
93
|
|
|
# Add revision metadata |
94
|
|
|
if not os.path.exists(revision.path): |
95
|
|
|
continue |
96
|
|
|
|
97
|
|
|
cls.add_file(tar, files, base_path, revision.path) |
98
|
|
|
|
99
|
|
|
# Add revision contents |
100
|
|
|
directory = os.path.dirname(revision.path) |
101
|
|
|
|
102
|
|
|
for name in revision.contents: |
103
|
|
|
path = os.path.join(directory, name) |
104
|
|
|
|
105
|
|
|
# Add file from contents |
106
|
|
|
if not os.path.exists(path): |
107
|
|
|
log.warn('Unable to find revision file: %r', path) |
108
|
|
|
continue |
109
|
|
|
|
110
|
|
|
cls.add_file(tar, files, base_path, path) |
111
|
|
|
|
112
|
|
|
log.debug('Added revision %r to archive', os.path.basename(revision.path)) |
113
|
|
|
|
114
|
|
|
return files |
115
|
|
|
|
116
|
1 |
|
@staticmethod |
117
|
|
|
def add_file(tar, tar_files, base_path, path): |
118
|
|
|
try: |
119
|
|
|
tar.add(path, arcname=os.path.relpath(path, base_path)) |
120
|
|
|
tar_files.append(path) |
121
|
|
|
except Exception, ex: |
122
|
|
|
log.warn('Unable to add file %r to archive - %s', path, ex, exc_info=True) |
123
|
|
|
|
124
|
|
|
# |
125
|
|
|
# Helpers |
126
|
|
|
# |
127
|
|
|
|
128
|
1 |
|
@staticmethod |
129
|
|
|
def get_period(group, period, key): |
130
|
|
|
if period == 'month': |
131
|
|
|
return ( |
132
|
|
|
os.path.join(group.path, str(key[0]), '%02d' % key[1]), |
133
|
|
|
'%d-%02d' % key |
134
|
|
|
) |
135
|
|
|
|
136
|
|
|
if period == 'year': |
137
|
|
|
return ( |
138
|
|
|
os.path.join(group.path, '%d' % key), |
139
|
|
|
'%d' % key |
140
|
|
|
) |
141
|
|
|
|
142
|
|
|
raise ValueError('Unsupported archive period: %r' % period) |
143
|
|
|
|