]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/tahoe_check.py
6fdd9013de520fa34311af1e2f217c90dd0c44a8
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / tahoe_check.py
1
2 import urllib
3 import simplejson
4 from twisted.protocols.basic import LineOnlyReceiver
5 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
6                                      UnknownAliasError
7 from allmydata.scripts.common_http import do_http
8
9 class Checker:
10     pass
11
12 def check(options):
13     stdout = options.stdout
14     stderr = options.stderr
15     nodeurl = options['node-url']
16     if not nodeurl.endswith("/"):
17         nodeurl += "/"
18     where = options.where
19     try:
20         rootcap, path = get_alias(options.aliases, where, DEFAULT_ALIAS)
21     except UnknownAliasError, e:
22         print >>stderr, "error: %s" % e.args[0]
23         return 1
24     if path == '/':
25         path = ''
26     url = nodeurl + "uri/%s" % urllib.quote(rootcap)
27     if path:
28         url += "/" + escape_path(path)
29     # todo: should it end with a slash?
30     url += "?t=check&output=JSON"
31     if options["verify"]:
32         url += "&verify=true"
33     if options["repair"]:
34         url += "&repair=true"
35     if options["add-lease"]:
36         url += "&add-lease=true"
37
38     resp = do_http("POST", url)
39     if resp.status != 200:
40         print >>stderr, "ERROR", resp.status, resp.reason, resp.read()
41         return 1
42     jdata = resp.read()
43     if options.get("raw"):
44         stdout.write(jdata)
45         stdout.write("\n")
46         return 0
47     data = simplejson.loads(jdata)
48
49     if options["repair"]:
50         # show repair status
51         if data["pre-repair-results"]["results"]["healthy"]:
52             summary = "healthy"
53         else:
54             summary = "not healthy"
55         stdout.write("Summary: %s\n" % summary)
56         cr = data["pre-repair-results"]["results"]
57         stdout.write(" storage index: %s\n" % data["storage-index"])
58         stdout.write(" good-shares: %d (encoding is %d-of-%d)\n"
59                      % (cr["count-shares-good"],
60                         cr["count-shares-needed"],
61                         cr["count-shares-expected"]))
62         stdout.write(" wrong-shares: %d\n" % cr["count-wrong-shares"])
63         corrupt = cr["list-corrupt-shares"]
64         if corrupt:
65             stdout.write(" corrupt shares:\n")
66             for (serverid, storage_index, sharenum) in corrupt:
67                 stdout.write("  server %s, SI %s, shnum %d\n" %
68                              (serverid, storage_index, sharenum))
69         if data["repair-attempted"]:
70             if data["repair-successful"]:
71                 stdout.write(" repair successful\n")
72             else:
73                 stdout.write(" repair failed\n")
74     else:
75         stdout.write("Summary: %s\n" % data["summary"])
76         cr = data["results"]
77         stdout.write(" storage index: %s\n" % data["storage-index"])
78         stdout.write(" good-shares: %d (encoding is %d-of-%d)\n"
79                      % (cr["count-shares-good"],
80                         cr["count-shares-needed"],
81                         cr["count-shares-expected"]))
82         stdout.write(" wrong-shares: %d\n" % cr["count-wrong-shares"])
83         corrupt = cr["list-corrupt-shares"]
84         if corrupt:
85             stdout.write(" corrupt shares:\n")
86             for (serverid, storage_index, sharenum) in corrupt:
87                 stdout.write("  server %s, SI %s, shnum %d\n" %
88                              (serverid, storage_index, sharenum))
89     return 0
90
91
92 class FakeTransport:
93     disconnecting = False
94
95 class DeepCheckOutput(LineOnlyReceiver):
96     delimiter = "\n"
97     def __init__(self, streamer, options):
98         self.streamer = streamer
99         self.transport = FakeTransport()
100
101         self.verbose = bool(options["verbose"])
102         self.stdout = options.stdout
103         self.stderr = options.stderr
104         self.num_objects = 0
105         self.files_healthy = 0
106         self.files_unhealthy = 0
107         self.in_error = False
108
109     def lineReceived(self, line):
110         if self.in_error:
111             print >>self.stderr, line
112             return
113         if line.startswith("ERROR:"):
114             self.in_error = True
115             self.streamer.rc = 1
116             print >>self.stderr, line
117             return
118
119         d = simplejson.loads(line)
120         stdout = self.stdout
121         if d["type"] not in ("file", "directory"):
122             return
123         self.num_objects += 1
124         # non-verbose means print a progress marker every 100 files
125         if self.num_objects % 100 == 0:
126             print >>stdout, "%d objects checked.." % self.num_objects
127         cr = d["check-results"]
128         if cr["results"]["healthy"]:
129             self.files_healthy += 1
130         else:
131             self.files_unhealthy += 1
132         if self.verbose:
133             # verbose means also print one line per file
134             path = d["path"]
135             if not path:
136                 path = ["<root>"]
137             summary = cr.get("summary", "Healthy (LIT)")
138             try:
139                 print >>stdout, "%s: %s" % ("/".join(path), summary)
140             except UnicodeEncodeError:
141                 print >>stdout, "%s: %s" % ("/".join([p.encode("utf-8")
142                                                       for p in path]),
143                                             summary)
144         # always print out corrupt shares
145         for shareloc in cr["results"].get("list-corrupt-shares", []):
146             (serverid, storage_index, sharenum) = shareloc
147             print >>stdout, " corrupt: server %s, SI %s, shnum %d" % \
148                   (serverid, storage_index, sharenum)
149
150     def done(self):
151         if self.in_error:
152             return
153         stdout = self.stdout
154         print >>stdout, "done: %d objects checked, %d healthy, %d unhealthy" \
155               % (self.num_objects, self.files_healthy, self.files_unhealthy)
156
157 class DeepCheckAndRepairOutput(LineOnlyReceiver):
158     delimiter = "\n"
159     def __init__(self, streamer, options):
160         self.streamer = streamer
161         self.transport = FakeTransport()
162
163         self.verbose = bool(options["verbose"])
164         self.stdout = options.stdout
165         self.stderr = options.stderr
166         self.num_objects = 0
167         self.pre_repair_files_healthy = 0
168         self.pre_repair_files_unhealthy = 0
169         self.repairs_attempted = 0
170         self.repairs_successful = 0
171         self.post_repair_files_healthy = 0
172         self.post_repair_files_unhealthy = 0
173         self.in_error = False
174
175     def lineReceived(self, line):
176         if self.in_error:
177             print >>self.stderr, line
178             return
179         if line.startswith("ERROR:"):
180             self.in_error = True
181             self.streamer.rc = 1
182             print >>self.stderr, line
183             return
184
185         d = simplejson.loads(line)
186         stdout = self.stdout
187         if d["type"] not in ("file", "directory"):
188             return
189         self.num_objects += 1
190         # non-verbose means print a progress marker every 100 files
191         if self.num_objects % 100 == 0:
192             print >>stdout, "%d objects checked.." % self.num_objects
193         crr = d["check-and-repair-results"]
194         if d["storage-index"]:
195             if crr["pre-repair-results"]["results"]["healthy"]:
196                 was_healthy = True
197                 self.pre_repair_files_healthy += 1
198             else:
199                 was_healthy = False
200                 self.pre_repair_files_unhealthy += 1
201             if crr["post-repair-results"]["results"]["healthy"]:
202                 self.post_repair_files_healthy += 1
203             else:
204                 self.post_repair_files_unhealthy += 1
205         else:
206             # LIT file
207             was_healthy = True
208             self.pre_repair_files_healthy += 1
209             self.post_repair_files_healthy += 1
210         if crr["repair-attempted"]:
211             self.repairs_attempted += 1
212             if crr["repair-successful"]:
213                 self.repairs_successful += 1
214         if self.verbose:
215             # verbose means also print one line per file
216             path = d["path"]
217             if not path:
218                 path = ["<root>"]
219             # we don't seem to have a summary available, so build one
220             if was_healthy:
221                 summary = "healthy"
222             else:
223                 summary = "not healthy"
224             try:
225                 print >>stdout, "%s: %s" % ("/".join(path), summary)
226             except UnicodeEncodeError:
227                 print >>stdout, "%s: %s" % ("/".join([p.encode("utf-8")
228                                                       for p in path]),
229                                             summary)
230         # always print out corrupt shares
231         prr = crr.get("pre-repair-results", {})
232         for shareloc in prr.get("results", {}).get("list-corrupt-shares", []):
233             (serverid, storage_index, sharenum) = shareloc
234             print >>stdout, " corrupt: server %s, SI %s, shnum %d" % \
235                   (serverid, storage_index, sharenum)
236
237         # always print out repairs
238         if crr["repair-attempted"]:
239             if crr["repair-successful"]:
240                 print >>stdout, " repair successful"
241             else:
242                 print >>stdout, " repair failed"
243
244     def done(self):
245         if self.in_error:
246             return
247         stdout = self.stdout
248         print >>stdout, "done: %d objects checked" % self.num_objects
249         print >>stdout, " pre-repair: %d healthy, %d unhealthy" \
250               % (self.pre_repair_files_healthy,
251                  self.pre_repair_files_unhealthy)
252         print >>stdout, " %d repairs attempted, %d successful, %d failed" \
253               % (self.repairs_attempted,
254                  self.repairs_successful,
255                  (self.repairs_attempted - self.repairs_successful))
256         print >>stdout, " post-repair: %d healthy, %d unhealthy" \
257               % (self.post_repair_files_healthy,
258                  self.post_repair_files_unhealthy)
259
260 class DeepCheckStreamer(LineOnlyReceiver):
261
262     def run(self, options):
263         stdout = options.stdout
264         stderr = options.stderr
265         self.rc = 0
266         self.options = options
267         nodeurl = options['node-url']
268         if not nodeurl.endswith("/"):
269             nodeurl += "/"
270         self.nodeurl = nodeurl
271         where = options.where
272         try:
273             rootcap, path = get_alias(options.aliases, where, DEFAULT_ALIAS)
274         except UnknownAliasError, e:
275             print >>stderr, "error: %s" % e.args[0]
276             return 1
277         if path == '/':
278             path = ''
279         url = nodeurl + "uri/%s" % urllib.quote(rootcap)
280         if path:
281             url += "/" + escape_path(path)
282         # todo: should it end with a slash?
283         url += "?t=stream-deep-check"
284         if options["verify"]:
285             url += "&verify=true"
286         if options["repair"]:
287             url += "&repair=true"
288             output = DeepCheckAndRepairOutput(self, options)
289         else:
290             output = DeepCheckOutput(self, options)
291         if options["add-lease"]:
292             url += "&add-lease=true"
293         resp = do_http("POST", url)
294         if resp.status not in (200, 302):
295             print >>stderr, "ERROR", resp.status, resp.reason, resp.read()
296             return 1
297
298         # use Twisted to split this into lines
299         while True:
300             chunk = resp.read(100)
301             if not chunk:
302                 break
303             if self.options["raw"]:
304                 stdout.write(chunk)
305             else:
306                 output.dataReceived(chunk)
307         if not self.options["raw"]:
308             output.done()
309         return self.rc
310
311 def deepcheck(options):
312     return DeepCheckStreamer().run(options)