0
|
1 #!/usr/bin/env python
|
|
2
|
|
3 # The MIT License
|
|
4 #
|
|
5 # Copyright (c) 2010 Louis Opter <kalessin@kalessin.fr>
|
|
6 #
|
|
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8 # of this software and associated documentation files (the "Software"), to deal
|
|
9 # in the Software without restriction, including without limitation the rights
|
|
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11 # copies of the Software, and to permit persons to whom the Software is
|
|
12 # furnished to do so, subject to the following conditions:
|
|
13 #
|
|
14 # The above copyright notice and this permission notice shall be included in
|
|
15 # all copies or substantial portions of the Software.
|
|
16 #
|
|
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23 # THE SOFTWARE.
|
|
24
|
|
25 import errno
|
|
26 import os
|
|
27 import shutil
|
|
28 import subprocess
|
|
29 import sys
|
|
30 import tarfile
|
|
31 import tempfile
|
|
32
|
|
33 class TempDir(object):
|
|
34 """Wrap tempfile.mkdtemp to use the with statement"""
|
|
35
|
|
36 def __init__(self):
|
|
37 pass
|
|
38
|
|
39 def __enter__(self):
|
|
40 self.__path = tempfile.mkdtemp()
|
|
41 return self.__path
|
|
42
|
|
43 def __exit__(self, exc_type, exc_value, traceback):
|
|
44 shutil.rmtree(self.__path)
|
|
45
|
|
46 def status(message):
|
|
47 print '%s: %s.' % (os.path.basename(sys.argv[0]), message)
|
|
48
|
|
49 def die(message):
|
|
50 status(message)
|
|
51 status('a log has been recorded in %s' % log_path)
|
|
52 sys.exit(1)
|
|
53
|
|
54 def dump_db():
|
|
55 """Use pg_dump to backup the databse into `root'"""
|
|
56
|
|
57 dump_path = os.path.join(root, db_name + '.sql')
|
|
58 with open(dump_path, 'w') as dump:
|
|
59 status('Dumping database to %s' % dump_path)
|
|
60 if subprocess.call(['sudo', '-u', 'postgres', 'pg_dump', db_name],
|
|
61 stdout=dump, stderr=log) != 0:
|
|
62 die('error while dumping database %s to a temporary directory' % db_name)
|
|
63
|
|
64 status('Database %s dumped' % db_name)
|
|
65
|
|
66 def archive():
|
|
67 """Create a tar bzipped archive of the db dump and the www-dir"""
|
|
68
|
|
69 try:
|
|
70 archive = tarfile.open(os.path.join(root, archive_name), 'w:bz2')
|
|
71 archive.add(os.path.join(root, db_name + '.sql'), db_name + '.sql')
|
|
72 archive.add(www_dir, os.path.basename(www_dir))
|
|
73 archive.close()
|
|
74 except tarfile.TarError, ex:
|
|
75 die('Can\'t create the tar archive %s: %s' % (archive_name, str(ex)))
|
|
76
|
|
77 status('Archive %s created' % archive_name)
|
|
78
|
|
79 def send_archive():
|
|
80 """Send the tarball using scp or s3cmd"""
|
|
81
|
|
82 archive = os.path.join(root, archive_name)
|
|
83 if dest[0:5] == 's3://':
|
|
84 ret = subprocess.call(['s3cmd', '--acl-private', 'put', archive, dest], stdout=log, stderr=log)
|
|
85 else:
|
|
86 ret = subprocess.call(['scp', archive, dest], stdout=log, stderr=log)
|
|
87
|
|
88 if ret != 0:
|
|
89 die('error while cpying %s to %s' % (archive_name, dest))
|
|
90
|
|
91 status('Archive %s copied to "%s"' % (archive_name, dest))
|
|
92
|
|
93 if __name__ == '__main__':
|
|
94 if len(sys.argv) != 4 or sys.argv[1] == '--help':
|
|
95 print 'Usage: %s db-name www-dir [[user@]host:]path|s3://bucket/[path]' % os.path.basename(sys.argv[0])
|
|
96 sys.exit(0)
|
|
97
|
|
98 # globals
|
|
99 db_name = sys.argv[1]
|
|
100 # python basename return '' for a path with an ending slash...
|
|
101 www_dir = sys.argv[2] != '/' and os.path.join('/var/www', sys.argv[2]) or '/var/www'
|
|
102 dest = sys.argv[3]
|
|
103 archive_name = 'backup-lapp-app-%s.tar.bz2' % db_name
|
|
104 log_path = os.path.join(os.getenv('TMP') or '/tmp/', db_name + '.log')
|
|
105
|
|
106 if not os.path.exists(www_dir):
|
|
107 status('%s: %s' % (www_dir, os.strerror(errno.ENOENT)))
|
|
108 sys.exit(1)
|
|
109
|
|
110 with open(log_path, 'w') as log:
|
|
111 with TempDir() as root:
|
|
112 status('root=%s' % root)
|
|
113 dump_db()
|
|
114 archive()
|
|
115 send_archive()
|
|
116 os.remove(log_path)
|
|
117
|
|
118 sys.exit(0)
|