summaryrefslogtreecommitdiff
path: root/src/local/backup-rhizi-neo4j
blob: e642dd53620afcb022b7b46f3590d0aa8a6dd127 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python

from datetime import datetime
import os
import shutil
import re
from glob import glob
from os.path import basename, dirname, exists

root = '/var/lib/rhizi/backup'

debug = True

backup_template = 'backup-%s.dump'
backup_glob = backup_template % '*'
backup_re = re.compile(backup_template % '(\d\d\d\d)(\d\d)(\d\d)')

DAILY_KEPT = 7 # should be >= 7
WEEKLY_KEPT = 5 # should be >= 5

def unlink(filename):
    if debug:
        print("unlinking %s")
    os.unlink(filename)

def makedirs(path):
    if exists(path):
        return
    if debug:
        print("makedirs %s" % path)
    os.makedirs(path)

def copy(src, dst):
    if debug:
        print("copy %s %s" % (src, dst))
    if not exists(src):
        print("bug: %s doesn't exist") % src
        raise SystemExit
    dst_path = dirname(dst)
    if not exists(dst_path):
        makedirs(dst_path)
    shutil.copy(src, dst)

def neo4jshell(command, stdout):
    cmdline = "neo4j-shell -c '%s' > '%s'" % (command, stdout)
    if debug:
        print("executing %r" % cmdline)
    os.system(cmdline)

def next_daily_filename():
    filename = os.path.join(daily_root(), backup_template % datetime.now().strftime('%Y%m%d'))
    return filename

def daily_root():
    return os.path.join(root, 'daily')

def weekly_root():
    return os.path.join(root, 'weekly')

def monthly_root():
    return os.path.join(root, 'monthly')

def backup_year_month_day(filename):
    return map(int, backup_re.match(basename(filename)).groups())

def to_weekly(filename):
    year, month, day = backup_year_month_day(filename)
    _, (wkly_year, wkly_month, wkly_day) = latest_weekly()
    #import pdb; pdb.set_trace()
    delta = datetime(year, month, day) - datetime(wkly_year, wkly_month, wkly_day)
    return delta.days >= 7

def to_monthly(filename):
    """ return True if filename is newer by a month from the newest monthly """
    year, month, day = backup_year_month_day(filename)
    _, (monthly_year, monthly_month, monthly_day) = latest_monthly()
    if monthly_year < year:
        month += 12
    return month > monthly_month and day >= monthly_day

def latest_weekly():
    return latest(weekly_root())

def latest_monthly():
    return latest(monthly_root())

def latest(subdir):
    """
    returns filename, (year, month, day)
    """
    filenames = glob(os.path.join(subdir, backup_glob))
    if len(filenames) == 0:
        return 'no_such_file', (1848, 1, 1)
    return max((fname, backup_year_month_day(fname)) for fname in filenames)

def sorted_backups(subdir):
    def cmp_files(fa, fb):
        ta =  os.stat(fa).mtime
        tb =  os.stat(fb).mtime
        if ta == tb:
            return 0
        if ta < tb:
            return -1
        return 1
    return sorted(glob(os.path.join(weekly_root(), backup_glob)), cmp=cmp_files)

def create_daily_backup():
    filename = next_daily_filename()
    makedirs(dirname(filename))
    neo4jshell("dump", stdout=filename)
    return filename

def main():
    # create the daily backup
    filename = create_daily_backup()

    # copy out to weekly if a week passed since last weekly, otherwise remove excess
    if to_weekly(filename):
        copy(filename, os.path.join(weekly_root(), basename(filename)))
    for filename in sorted_backups(daily_root())[DAILY_KEPT:]:
        unlink(filename)

    # rotate out to monthly if a month passed since last monthly, otherwise remove excess
    if to_monthly(filename):
        copy(filename, os.path.join(monthly_root(), basename(filename)))
    for filename in sorted_backups(weekly_root())[WEEKLY_KEPT:]:
        unlink(filename)

if __name__ == '__main__':
    main()