fix paths
[mirror/dsa-puppet.git] / modules / roles / files / postgresql_server / pg-receive-file-from-backup
1 #!/usr/bin/python
2
3 # Copyright (c) 2010 Peter Palfrader
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 # copy a file from the backup server, where the server runs debbackup-ssh-wrap
25
26 import sys
27 import os
28 import optparse
29 import re
30 import subprocess
31 import syslog
32 import tempfile
33 import stat
34 import hashlib
35
36 backuphost="debbackup@storace.debian.org"
37 block_size = 4096
38
39 syslog.openlog(sys.argv[0], syslog.LOG_PID, syslog.LOG_DAEMON)
40
41 def info(m):
42     syslog.syslog(syslog.LOG_INFO, m)
43 def croak(m):
44     syslog.syslog(syslog.LOG_WARNING, m)
45     print >> sys.stderr, m
46     sys.exit(1)
47
48 sys.stdin.close()
49
50 parser = optparse.OptionParser()
51 parser.set_usage("%prog [<options>] <from_host> <filename> <target>")
52 (options, args) = parser.parse_args()
53
54 if len(args) != 3:
55     parser.print_help()
56     sys.exit(1)
57
58 from_host = args.pop(0)
59 filename = args.pop(0)
60 target = args.pop(0)
61
62 hostname = os.uname()[1]
63
64 # open pipe to backup server
65 p = subprocess.Popen(["ssh", "-C", backuphost, hostname, "retrieve-file", "pg", from_host, filename], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
66 p.stdin.close()
67 f = p.stdout
68
69 # read Format line
70 line = f.readline().rstrip('\n')
71 if line != "Format: 1":
72     croak("Uknown format in line 1 (%s) when getting %s:%s from backup host"%(line, from_host, filename))
73
74 # read Status line
75 line = f.readline().rstrip('\n')
76 s = line.split(':', 1)
77 if len(s) != 2:
78     croak("Protocol violation when getting %s:%s from backup host (line 2)"%(from_host, filename))
79 key = s[0]
80 if key != "Status":
81     croak("Protocol violation when getting %s:%s from backup host"%(from_host, filename))
82 first = s[1].split()[0]
83 try:
84     code = int(first)
85 except ValueError:
86     croak("Invalid code '%s'"%(first))
87
88 if code == 404:
89     info("Wanted to get %s:%s from backup host, but file does not exist."%(from_host, filename))
90     sys.exit(1)
91 elif code != 200:
92     info("Unknown code %d when trying to get %s:%s from backup host"%(code, from_host, filename))
93
94 # get rest of the headers
95 metadata = {}
96 while True:
97     line = f.readline().rstrip('\n')
98     if line == "": break
99     line = line.strip()
100     s = line.split(':', 1)
101     if len(s) != 2:
102         croak("Protocol violation when getting %s:%s from backup host"%(from_host, filename))
103     key = s[0]
104     value = s[1].lstrip()
105     metadata[key] = value
106
107 # verify we like them
108 for k in ['Size', 'SHA-512']:
109     if not k in metadata:
110         croak("Key %s not found in metadata when getting %s:%s from backup host"%(k, from_host, filename))
111 try:
112     size = int(metadata['Size'])
113 except ValueError:
114     croak("Invalid size '%s'"%(size))
115 checksum = metadata['SHA-512']
116
117 info("Getting %s:%s from backup host (size %d)."%(from_host, filename, size))
118
119 target=os.path.abspath(target)
120 targetdir=os.path.dirname(target)
121 tmp = tempfile.NamedTemporaryFile(dir=targetdir, prefix=".tmp.pg-receive-file-from-backup-%s:%s."%(from_host, filename))
122
123 # read file
124 running_size = 0
125 digest = hashlib.sha512()
126 while True:
127     buf = f.read(block_size)
128     if not buf: break
129     digest.update(buf)
130     tmp.write(buf)
131
132     running_size += len(buf)
133     if running_size > size:
134         croak("Size mismatch")
135 f.close()
136 p.wait()
137 tmp.flush()
138
139 file_size = os.stat(tmp.name)[stat.ST_SIZE]
140
141 # check size and checksum
142 if file_size != size:
143     croak("Size mismatch")
144 if file_size != running_size:
145     croak("Size mismatch. WTF.")
146 if checksum != digest.hexdigest():
147     croak("Checksum mismatch. WTF.")
148
149 # and try to link
150 try:
151     os.link(tmp.name, target)
152 except Exception, e:
153     croak("Failed at linking to target: %s"%(e))
154
155 tmp.close()
156 info("Successfully stored %s."%(target))
157
158
159 # vim:set et:
160 # vim:set ts=4:
161 # vim:set shiftwidth=4: