4 from twisted.protocols.basic import LineOnlyReceiver
5 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
7 from allmydata.scripts.common_http import do_http, format_http_error
8 from allmydata.util.encodingutil import quote_output, quote_path
13 def _quote_serverid_index_share(serverid, storage_index, sharenum):
14 return "server %s, SI %s, shnum %r" % (quote_output(serverid, quotemarks=False),
15 quote_output(storage_index, quotemarks=False),
19 stdout = options.stdout
20 stderr = options.stderr
21 nodeurl = options['node-url']
22 if not nodeurl.endswith("/"):
26 rootcap, path = get_alias(options.aliases, where, DEFAULT_ALIAS)
27 except UnknownAliasError, e:
32 url = nodeurl + "uri/%s" % urllib.quote(rootcap)
34 url += "/" + escape_path(path)
35 # todo: should it end with a slash?
36 url += "?t=check&output=JSON"
41 if options["add-lease"]:
42 url += "&add-lease=true"
44 resp = do_http("POST", url)
45 if resp.status != 200:
46 print >>stderr, format_http_error("ERROR", resp)
49 if options.get("raw"):
53 data = simplejson.loads(jdata)
57 if data["pre-repair-results"]["results"]["healthy"]:
60 summary = "not healthy"
61 stdout.write("Summary: %s\n" % summary)
62 cr = data["pre-repair-results"]["results"]
63 stdout.write(" storage index: %s\n" % quote_output(data["storage-index"], quotemarks=False))
64 stdout.write(" good-shares: %r (encoding is %r-of-%r)\n"
65 % (cr["count-shares-good"],
66 cr["count-shares-needed"],
67 cr["count-shares-expected"]))
68 stdout.write(" wrong-shares: %r\n" % cr["count-wrong-shares"])
69 corrupt = cr["list-corrupt-shares"]
71 stdout.write(" corrupt shares:\n")
72 for (serverid, storage_index, sharenum) in corrupt:
73 stdout.write(" %s\n" % _quote_serverid_index_share(serverid, storage_index, sharenum))
74 if data["repair-attempted"]:
75 if data["repair-successful"]:
76 stdout.write(" repair successful\n")
78 stdout.write(" repair failed\n")
80 stdout.write("Summary: %s\n" % quote_output(data["summary"], quotemarks=False))
82 stdout.write(" storage index: %s\n" % quote_output(data["storage-index"], quotemarks=False))
83 stdout.write(" good-shares: %r (encoding is %r-of-%r)\n"
84 % (cr["count-shares-good"],
85 cr["count-shares-needed"],
86 cr["count-shares-expected"]))
87 stdout.write(" wrong-shares: %r\n" % cr["count-wrong-shares"])
88 corrupt = cr["list-corrupt-shares"]
90 stdout.write(" corrupt shares:\n")
91 for (serverid, storage_index, sharenum) in corrupt:
92 stdout.write(" %s\n" % _quote_serverid_index_share(serverid, storage_index, sharenum))
99 class DeepCheckOutput(LineOnlyReceiver):
101 def __init__(self, streamer, options):
102 self.streamer = streamer
103 self.transport = FakeTransport()
105 self.verbose = bool(options["verbose"])
106 self.stdout = options.stdout
107 self.stderr = options.stderr
109 self.files_healthy = 0
110 self.files_unhealthy = 0
111 self.in_error = False
113 def lineReceived(self, line):
115 print >>self.stderr, quote_output(line, quotemarks=False)
117 if line.startswith("ERROR:"):
120 print >>self.stderr, quote_output(line, quotemarks=False)
123 d = simplejson.loads(line)
125 if d["type"] not in ("file", "directory"):
127 self.num_objects += 1
128 # non-verbose means print a progress marker every 100 files
129 if self.num_objects % 100 == 0:
130 print >>stdout, "%d objects checked.." % self.num_objects
131 cr = d["check-results"]
132 if cr["results"]["healthy"]:
133 self.files_healthy += 1
135 self.files_unhealthy += 1
137 # verbose means also print one line per file
141 summary = cr.get("summary", "Healthy (LIT)")
142 print >>stdout, "%s: %s" % (quote_path(path), quote_output(summary, quotemarks=False))
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: %s" % _quote_serverid_index_share(serverid, storage_index, sharenum)
153 print >>stdout, "done: %d objects checked, %d healthy, %d unhealthy" \
154 % (self.num_objects, self.files_healthy, self.files_unhealthy)
156 class DeepCheckAndRepairOutput(LineOnlyReceiver):
158 def __init__(self, streamer, options):
159 self.streamer = streamer
160 self.transport = FakeTransport()
162 self.verbose = bool(options["verbose"])
163 self.stdout = options.stdout
164 self.stderr = options.stderr
166 self.pre_repair_files_healthy = 0
167 self.pre_repair_files_unhealthy = 0
168 self.repairs_attempted = 0
169 self.repairs_successful = 0
170 self.post_repair_files_healthy = 0
171 self.post_repair_files_unhealthy = 0
172 self.in_error = False
174 def lineReceived(self, line):
176 print >>self.stderr, quote_output(line, quotemarks=False)
178 if line.startswith("ERROR:"):
181 print >>self.stderr, quote_output(line, quotemarks=False)
184 d = simplejson.loads(line)
186 if d["type"] not in ("file", "directory"):
188 self.num_objects += 1
189 # non-verbose means print a progress marker every 100 files
190 if self.num_objects % 100 == 0:
191 print >>stdout, "%d objects checked.." % self.num_objects
192 crr = d["check-and-repair-results"]
193 if d["storage-index"]:
194 if crr["pre-repair-results"]["results"]["healthy"]:
196 self.pre_repair_files_healthy += 1
199 self.pre_repair_files_unhealthy += 1
200 if crr["post-repair-results"]["results"]["healthy"]:
201 self.post_repair_files_healthy += 1
203 self.post_repair_files_unhealthy += 1
207 self.pre_repair_files_healthy += 1
208 self.post_repair_files_healthy += 1
209 if crr["repair-attempted"]:
210 self.repairs_attempted += 1
211 if crr["repair-successful"]:
212 self.repairs_successful += 1
214 # verbose means also print one line per file
218 # we don't seem to have a summary available, so build one
222 summary = "not healthy"
223 print >>stdout, "%s: %s" % (quote_path(path), summary)
225 # always print out corrupt shares
226 prr = crr.get("pre-repair-results", {})
227 for shareloc in prr.get("results", {}).get("list-corrupt-shares", []):
228 (serverid, storage_index, sharenum) = shareloc
229 print >>stdout, " corrupt: %s" % _quote_serverid_index_share(serverid, storage_index, sharenum)
231 # always print out repairs
232 if crr["repair-attempted"]:
233 if crr["repair-successful"]:
234 print >>stdout, " repair successful"
236 print >>stdout, " repair failed"
242 print >>stdout, "done: %d objects checked" % self.num_objects
243 print >>stdout, " pre-repair: %d healthy, %d unhealthy" \
244 % (self.pre_repair_files_healthy,
245 self.pre_repair_files_unhealthy)
246 print >>stdout, " %d repairs attempted, %d successful, %d failed" \
247 % (self.repairs_attempted,
248 self.repairs_successful,
249 (self.repairs_attempted - self.repairs_successful))
250 print >>stdout, " post-repair: %d healthy, %d unhealthy" \
251 % (self.post_repair_files_healthy,
252 self.post_repair_files_unhealthy)
254 class DeepCheckStreamer(LineOnlyReceiver):
256 def run(self, options):
257 stdout = options.stdout
258 stderr = options.stderr
260 self.options = options
261 nodeurl = options['node-url']
262 if not nodeurl.endswith("/"):
264 self.nodeurl = nodeurl
265 where = options.where
267 rootcap, path = get_alias(options.aliases, where, DEFAULT_ALIAS)
268 except UnknownAliasError, e:
273 url = nodeurl + "uri/%s" % urllib.quote(rootcap)
275 url += "/" + escape_path(path)
276 # todo: should it end with a slash?
277 url += "?t=stream-deep-check"
278 if options["verify"]:
279 url += "&verify=true"
280 if options["repair"]:
281 url += "&repair=true"
282 output = DeepCheckAndRepairOutput(self, options)
284 output = DeepCheckOutput(self, options)
285 if options["add-lease"]:
286 url += "&add-lease=true"
287 resp = do_http("POST", url)
288 if resp.status not in (200, 302):
289 print >>stderr, format_http_error("ERROR", resp)
292 # use Twisted to split this into lines
294 chunk = resp.read(100)
297 if self.options["raw"]:
300 output.dataReceived(chunk)
301 if not self.options["raw"]:
305 def deepcheck(options):
306 return DeepCheckStreamer().run(options)