source: trunk/misc/checkers/check_grid.py

Last change on this file was b856238, checked in by Alexandre Detiste <alexandre.detiste@…>, at 2024-02-15T15:53:34Z

remove old Python2 future statements

  • Property mode set to 100644
File size: 7.4 KB
Line 
1
2"""
3Test an existing Tahoe grid, both to see if the grid is still running and to
4see if the client is still compatible with it. This script is suitable for
5running from a periodic monitoring script, perhaps by an hourly cronjob.
6
7This script uses a pre-established client node (configured to connect to the
8grid being tested) and a pre-established directory (stored as the 'testgrid:'
9alias in that client node's aliases file). It then performs a number of
10uploads and downloads to exercise compatibility in various directions (new
11client vs old data). All operations are performed by invoking various CLI
12commands through bin/tahoe . The script must be given two arguments: the
13client node directory, and the location of the bin/tahoe executable. Note
14that this script does not import anything from tahoe directly, so it doesn't
15matter what its PYTHONPATH is, as long as the bin/tahoe that it uses is
16functional.
17
18This script expects the client node to be running already.
19
20To set up the client node, do the following:
21
22  tahoe create-client --introducer=INTRODUCER_FURL DIR
23  tahoe run DIR
24  tahoe -d DIR create-alias testgrid
25  # pick a 10kB-ish test file, compute its md5sum
26  tahoe -d DIR put FILE testgrid:old.MD5SUM
27  tahoe -d DIR put FILE testgrid:recent.MD5SUM
28  tahoe -d DIR put FILE testgrid:recentdir/recent.MD5SUM
29  echo "" | tahoe -d DIR put --mutable - testgrid:log
30  echo "" | tahoe -d DIR put --mutable - testgrid:recentlog
31
32This script will perform the following steps (the kind of compatibility that
33is being tested is in [brackets]):
34
35 read old.* and check the md5sums [confirm that new code can read old files]
36 read all recent.* files and check md5sums [read recent files]
37 delete all recent.* files and verify they're gone [modify an old directory]
38 read recentdir/recent.* files and check [read recent directory]
39 delete recentdir/recent.* and verify [modify recent directory]
40 delete recentdir and verify (keep the directory from growing unboundedly)
41 mkdir recentdir
42 upload random 10kB file to recentdir/recent.MD5SUM (prepare for next time)
43 upload random 10kB file to recent.MD5SUM [new code can upload to old servers]
44 append one-line timestamp to log [read/write old mutable files]
45 append one-line timestamp to recentlog [read/write recent mutable files]
46 delete recentlog
47 upload small header to new mutable recentlog [create mutable files]
48
49This script will also keep track of speeds and latencies and will write them
50in a machine-readable logfile.
51"""
52
53import time, subprocess, md5, os.path, random
54from twisted.python import usage
55
56class GridTesterOptions(usage.Options):
57
58    optFlags = [
59        ("no", "n", "Dry run: do not run any commands, just print them."),
60        ]
61
62    def parseArgs(self, nodedir, tahoe):
63        # Note: does not support Unicode arguments.
64        self.nodedir = os.path.expanduser(nodedir)
65        self.tahoe = os.path.abspath(os.path.expanduser(tahoe))
66
67class CommandFailed(Exception):
68    pass
69
70class GridTester(object):
71    def __init__(self, config):
72        self.config = config
73        self.tahoe = config.tahoe
74        self.nodedir = config.nodedir
75
76    def command(self, *cmd, **kwargs):
77        expected_rc = kwargs.get("expected_rc", 0)
78        stdin = kwargs.get("stdin", None)
79        if self.config["no"]:
80            return
81        if stdin is not None:
82            p = subprocess.Popen(cmd,
83                                 stdin=subprocess.PIPE,
84                                 stdout=subprocess.PIPE,
85                                 stderr=subprocess.PIPE)
86            (stdout,stderr) = p.communicate(stdin)
87        else:
88            p = subprocess.Popen(cmd,
89                                 stdout=subprocess.PIPE,
90                                 stderr=subprocess.PIPE)
91            (stdout,stderr) = p.communicate()
92        rc = p.returncode
93        if expected_rc != None and rc != expected_rc:
94            if stderr:
95                print("STDERR:")
96                print(stderr)
97            raise CommandFailed("command '%s' failed: rc=%d" % (cmd, rc))
98        return stdout, stderr
99
100    def cli(self, cmd, *args, **kwargs):
101        print("tahoe", cmd, " ".join(args))
102        stdout, stderr = self.command(self.tahoe, "-d", self.nodedir, cmd,
103                                      *args, **kwargs)
104        if not kwargs.get("ignore_stderr", False) and stderr != "":
105            raise CommandFailed("command '%s' had stderr: %s" % (" ".join(args),
106                                                                 stderr))
107        return stdout
108
109    def read_and_check(self, f):
110        expected_md5_s = f[f.find(".")+1:]
111        out = self.cli("get", "testgrid:" + f)
112        got_md5_s = md5.new(out).hexdigest()
113        if got_md5_s != expected_md5_s:
114            raise CommandFailed("%s had md5sum of %s" % (f, got_md5_s))
115
116    def delete_and_check(self, dirname, f):
117        oldfiles = self.listdir(dirname)
118        if dirname:
119            absfilename = "testgrid:" + dirname + "/" + f
120        else:
121            absfilename = "testgrid:" + f
122        if f not in oldfiles:
123            raise CommandFailed("um, '%s' was supposed to already be in %s"
124                                % (f, dirname))
125        self.cli("unlink", absfilename)
126        newfiles = self.listdir(dirname)
127        if f in newfiles:
128            raise CommandFailed("failed to remove '%s' from %s" % (f, dirname))
129
130    def listdir(self, dirname):
131        out = self.cli("ls", "testgrid:"+dirname).strip().split("\n")
132        files = [f.strip() for f in out]
133        print(" ", files)
134        return files
135
136    def do_test(self):
137        files = self.listdir("")
138        for f in files:
139            if f.startswith("old.") or f.startswith("recent."):
140                self.read_and_check("" + f)
141        for f in files:
142            if f.startswith("recent."):
143                self.delete_and_check("", f)
144        files = self.listdir("recentdir")
145        for f in files:
146            if f.startswith("old.") or f.startswith("recent."):
147                self.read_and_check("recentdir/" + f)
148        for f in files:
149            if f.startswith("recent."):
150                self.delete_and_check("recentdir", f)
151        self.delete_and_check("", "recentdir")
152
153        self.cli("mkdir", "testgrid:recentdir")
154        fn, data = self.makefile("recent")
155        self.put("recentdir/"+fn, data)
156        files = self.listdir("recentdir")
157        if fn not in files:
158            raise CommandFailed("failed to put %s in recentdir/" % fn)
159        fn, data = self.makefile("recent")
160        self.put(fn, data)
161        files = self.listdir("")
162        if fn not in files:
163            raise CommandFailed("failed to put %s in testgrid:" % fn)
164
165        self.update("log")
166        self.update("recentlog")
167        self.delete_and_check("", "recentlog")
168        self.put_mutable("recentlog", "Recent Mutable Log Header\n\n")
169
170    def put(self, fn, data):
171        self.cli("put", "-", "testgrid:"+fn, stdin=data, ignore_stderr=True)
172
173    def put_mutable(self, fn, data):
174        self.cli("put", "--mutable", "-", "testgrid:"+fn,
175                 stdin=data, ignore_stderr=True)
176
177    def update(self, fn):
178        old = self.cli("get", "testgrid:"+fn)
179        new = old + time.ctime() + "\n"
180        self.put(fn, new)
181
182    def makefile(self, prefix):
183        size = random.randint(10001, 10100)
184        data = os.urandom(size)
185        md5sum = md5.new(data).hexdigest()
186        fn = prefix + "." + md5sum
187        return fn, data
188
189def main():
190    config = GridTesterOptions()
191    config.parseOptions()
192    gt = GridTester(config)
193    gt.do_test()
194
195if __name__ == "__main__":
196    main()
Note: See TracBrowser for help on using the repository browser.