Passed
Push — develop ( 6fb855...7cfc9a )
by Dean
02:39
created

ArchiveTask.add_file()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.5786
Metric Value
cc 1
dl 0
loc 7
ccs 1
cts 6
cp 0.1666
crap 1.5786
rs 9.4285
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