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
|
#!/usr/bin/python
#
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""
Backup one or more PostgreSQL databases.
Suitable for use in crontab for daily backups.
"""
__metaclass__ = type
__all__ = []
from datetime import datetime
import logging
from optparse import OptionParser
import os
import os.path
import stat
import subprocess
import sys
MB = float(1024*1024)
return_code = 0 # Return code of this script. Set to the most recent failed
# system call's return code
def call(cmd, **kw):
log.debug(' '.join(cmd))
rv = subprocess.call(cmd, **kw)
if rv != 0:
global return_code
return_code = rv
return rv
def main(options, databases):
global return_code
#Need longer file names if this is used more than daily
#today = datetime.now().strftime('%Y%m%d_%H:%M:%S')
today = datetime.now().strftime('%Y%m%d')
backup_dir = options.backup_dir
# Check for existing files now. Also check later just in case
# there are two copies of this script running or something crazy.
# Better to bomb out now rather than to bomb out later, as later might
# be several hours away.
for database in databases:
dest = os.path.join(backup_dir, '%s.%s.dump' % (database, today))
if os.path.exists(dest):
log.fatal("%s already exists." % dest)
return 1
exit_code = 0
for database in databases:
dest = os.path.join(backup_dir, '%s.%s.dump' % (database, today))
if os.path.exists(dest):
log.fatal("%s already exists." % dest)
return 1
cmd = [
"/usr/bin/pg_dump",
"-U", "postgres",
"--format=c",
"--compress=9",
"--blobs",
"--file=%s" % dest,
database,
]
rv = call(cmd, stdin=subprocess.PIPE) # Sets return_code on failure.
if rv != 0:
log.critical("Failed to backup %s (%d)" % (database, rv))
continue
size = os.stat(dest)[stat.ST_SIZE]
log.info("Backed up %s (%0.2fMB)" % (database, size/MB))
return return_code
if __name__ == '__main__':
parser = OptionParser(
usage="usage: %prog [options] database [database ..]"
)
parser.add_option("-v", "--verbose", dest="verbose", default=0,
action="count")
parser.add_option("-q", "--quiet", dest="quiet", default=0,
action="count")
parser.add_option("-d", "--dir", dest="backup_dir",
default="/var/lib/postgres/backups")
(options, databases) = parser.parse_args()
if len(databases) == 0:
parser.error("must specify at least one database")
if not os.path.isdir(options.backup_dir):
parser.error(
"Incorrect --dir. %s does not exist or is not a directory" % (
options.backup_dir
)
)
# Setup our log
log = logging.getLogger('pgbackup')
hdlr = logging.StreamHandler(strm=sys.stderr)
hdlr.setFormatter(logging.Formatter(
fmt='%(asctime)s %(levelname)s %(message)s'
))
log.addHandler(hdlr)
verbosity = options.verbose - options.quiet
if verbosity > 0:
log.setLevel(logging.DEBUG)
elif verbosity == 0: # Default
log.setLevel(logging.INFO)
elif verbosity == -1:
log.setLevel(logging.WARN)
elif verbosity < -1:
log.setLevel(logging.ERROR)
sys.exit(main(options, databases))
|