1 | """ |
---|
2 | Ported to Python 3. |
---|
3 | """ |
---|
4 | |
---|
5 | from six import ensure_str |
---|
6 | |
---|
7 | import os, time |
---|
8 | from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \ |
---|
9 | UnknownAliasError |
---|
10 | from allmydata.scripts.common_http import do_http, format_http_error |
---|
11 | from allmydata.util import base32 |
---|
12 | from allmydata.util.encodingutil import quote_output, is_printable_ascii |
---|
13 | from urllib.parse import quote as url_quote |
---|
14 | import json |
---|
15 | |
---|
16 | class SlowOperationRunner(object): |
---|
17 | |
---|
18 | def run(self, options): |
---|
19 | stderr = options.stderr |
---|
20 | self.options = options |
---|
21 | self.ophandle = ophandle = ensure_str(base32.b2a(os.urandom(16))) |
---|
22 | nodeurl = options['node-url'] |
---|
23 | if not nodeurl.endswith("/"): |
---|
24 | nodeurl += "/" |
---|
25 | self.nodeurl = nodeurl |
---|
26 | where = options.where |
---|
27 | try: |
---|
28 | rootcap, path = get_alias(options.aliases, where, DEFAULT_ALIAS) |
---|
29 | except UnknownAliasError as e: |
---|
30 | e.display(stderr) |
---|
31 | return 1 |
---|
32 | path = str(path, "utf-8") |
---|
33 | if path == '/': |
---|
34 | path = '' |
---|
35 | url = nodeurl + "uri/%s" % url_quote(rootcap) |
---|
36 | if path: |
---|
37 | url += "/" + escape_path(path) |
---|
38 | # todo: should it end with a slash? |
---|
39 | url = self.make_url(url, ophandle) |
---|
40 | resp = do_http("POST", url) |
---|
41 | if resp.status not in (200, 302): |
---|
42 | print(format_http_error("ERROR", resp), file=stderr) |
---|
43 | return 1 |
---|
44 | # now we poll for results. We nominally poll at t=1, 5, 10, 30, 60, |
---|
45 | # 90, k*120 seconds, but if the poll takes non-zero time, that will |
---|
46 | # be slightly longer. I'm not worried about trying to make up for |
---|
47 | # that time. |
---|
48 | |
---|
49 | return self.wait_for_results() |
---|
50 | |
---|
51 | def poll_times(self): |
---|
52 | for i in (1,5,10,30,60,90): |
---|
53 | yield i |
---|
54 | i = 120 |
---|
55 | while True: |
---|
56 | yield i |
---|
57 | i += 120 |
---|
58 | |
---|
59 | def wait_for_results(self): |
---|
60 | last = 0 |
---|
61 | for next_item in self.poll_times(): |
---|
62 | delay = next_item - last |
---|
63 | time.sleep(delay) |
---|
64 | last = next_item |
---|
65 | if self.poll(): |
---|
66 | return 0 |
---|
67 | |
---|
68 | def poll(self): |
---|
69 | url = self.nodeurl + "operations/" + self.ophandle |
---|
70 | url += "?t=status&output=JSON&release-after-complete=true" |
---|
71 | stdout = self.options.stdout |
---|
72 | stderr = self.options.stderr |
---|
73 | resp = do_http("GET", url) |
---|
74 | if resp.status != 200: |
---|
75 | print(format_http_error("ERROR", resp), file=stderr) |
---|
76 | return True |
---|
77 | jdata = resp.read() |
---|
78 | data = json.loads(jdata) |
---|
79 | if not data["finished"]: |
---|
80 | return False |
---|
81 | if self.options.get("raw"): |
---|
82 | stdout = stdout.buffer |
---|
83 | if is_printable_ascii(jdata): |
---|
84 | stdout.write(jdata) |
---|
85 | stdout.write(b"\n") |
---|
86 | stdout.flush() |
---|
87 | else: |
---|
88 | print("The JSON response contained unprintable characters:\n%s" % quote_output(jdata), file=stderr) |
---|
89 | return True |
---|
90 | self.write_results(data) |
---|
91 | return True |
---|
92 | |
---|