]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
check_grid.py: make it work, move node start/stop responsibility from Makefile to...
authorBrian Warner <warner@allmydata.com>
Thu, 19 Jun 2008 19:39:52 +0000 (12:39 -0700)
committerBrian Warner <warner@allmydata.com>
Thu, 19 Jun 2008 19:39:52 +0000 (12:39 -0700)
Makefile
src/allmydata/test/check_grid.py

index d2962d4bbc36d4a7a07364c30ac518525afbf6e6..44bbd97969692684a22f44a9c8bf79c81c14729e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -258,12 +258,7 @@ check-speed: .built
 # in src/allmydata/test/check_grid.py to see how to set this up.
 check-grid: .built
        if [ -z '$(TESTCLIENTDIR)' ]; then exit 1; fi
-       @echo "stopping any leftover client code"
-       -$(PYTHON) bin/tahoe stop $(TESTCLIENTDIR)
-       $(PYTHON) bin/tahoe start $(TESTCLIENTDIR)
-       sleep 5
-       $(PYTHON) src/allmydata/test/check_grid.py $(TESTCLIENTDIR)
-       $(PYTHON) bin/tahoe stop $(TESTCLIENTDIR)
+       $(PYTHON) src/allmydata/test/check_grid.py $(TESTCLIENTDIR) bin/tahoe
 
 # 'make repl' is a simple-to-type command to get a Python interpreter loop
 # from which you can type 'import allmydata'
index 2fb42985cfd8e93281ac1c65dc502e06f3653f78..8b2fc9fc6c709e18b8ec7c3e88479b6756d3961d 100644 (file)
@@ -9,10 +9,16 @@ This script uses a pre-established client node (configured to connect to the
 grid being tested) and a pre-established directory (stored as the 'testgrid:'
 alias in that client node's aliases file). It then performs a number of
 uploads and downloads to exercise compatibility in various directions (new
-client vs old data).
+client vs old data). All operations are performed by invoking various CLI
+commands through bin/tahoe . The script must be given two arguments: the
+client node directory, and the location of the bin/tahoe executable. Note
+that this script does not import anything from tahoe directly, so it doesn't
+matter what its PYTHONPATH is, as long as the bin/tahoe that it uses is
+functional.
 
-This script expects that the client node will be running before the script
-starts.
+This script expects that the client node will be not running when the script
+starts, but it will forcibly shut down the node just to be sure. It will shut
+down the node after the test finishes.
 
 To set up the client node, do the following:
 
@@ -20,13 +26,13 @@ To set up the client node, do the following:
   touch DIR/no_storage
   populate DIR/introducer.furl
   tahoe start DIR
-  tahoe -d DIR add-alias testgrid `tahoe -d DIR mkdir`
+  tahoe add-alias -d DIR testgrid `tahoe mkdir -d DIR`
   pick a 10kB-ish test file, compute its md5sum
-  tahoe -d DIR put FILE testgrid:old.MD5SUM
-  tahoe -d DIR put FILE testgrid:recent.MD5SUM
-  tahoe -d DIR put FILE testgrid:recentdir/recent.MD5SUM
-  echo "" | tahoe -d DIR put --mutable testgrid:log
-  echo "" | tahoe -d DIR put --mutable testgrid:recentlog
+  tahoe put -d DIR FILE testgrid:old.MD5SUM
+  tahoe put -d DIR FILE testgrid:recent.MD5SUM
+  tahoe put -d DIR FILE testgrid:recentdir/recent.MD5SUM
+  echo "" | tahoe put -d DIR --mutable testgrid:log
+  echo "" | tahoe put -d DIR --mutable testgrid:recentlog
 
 This script will perform the following steps (the kind of compatibility that
 is being tested is in [brackets]):
@@ -50,3 +56,164 @@ in a machine-readable logfile.
 
 """
 
+import time, subprocess, md5, os.path, random
+from twisted.python import usage
+
+class GridTesterOptions(usage.Options):
+
+    optFlags = [
+        ("no", "n", "Dry run: do not run any commands, just print them."),
+        ]
+
+    def parseArgs(self, nodedir, tahoe):
+        self.nodedir = nodedir
+        self.tahoe = os.path.abspath(tahoe)
+
+class CommandFailed(Exception):
+    pass
+
+class GridTester:
+    def __init__(self, config):
+        self.config = config
+        self.tahoe = config.tahoe
+        self.nodedir = config.nodedir
+
+    def command(self, *cmd, **kwargs):
+        expected_rc = kwargs.get("expected_rc", None)
+        stdin = kwargs.get("stdin", None)
+        if self.config["no"]:
+            return
+        if stdin is not None:
+            p = subprocess.Popen(cmd,
+                                 stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+            (stdout,stderr) = p.communicate(stdin)
+        else:
+            p = subprocess.Popen(cmd,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+            (stdout,stderr) = p.communicate()
+        rc = p.returncode
+        if expected_rc != None and rc != expected_rc:
+            raise CommandFailed("command '%s' failed: rc=%d" % (cmd, rc))
+        return stdout, stderr
+
+    def cli(self, cmd, *args, **kwargs):
+        print "tahoe", cmd, " ".join(args)
+        stdout, stderr = self.command(self.tahoe, cmd, "-d", self.nodedir,
+                                      *args, **kwargs)
+        if not kwargs.get("ignore_stderr", False) and stderr != "":
+            raise CommandFailed("command '%s' had stderr: %s" % (" ".join(args),
+                                                                 stderr))
+        return stdout
+
+    def stop_old_node(self):
+        print "tahoe stop", self.nodedir, "(force)"
+        self.command(self.tahoe, "stop", self.nodedir, expected_rc=None)
+
+    def start_node(self):
+        print "tahoe start", self.nodedir
+        self.command(self.tahoe, "start", self.nodedir)
+        time.sleep(5)
+
+    def stop_node(self):
+        print "tahoe stop", self.nodedir
+        self.command(self.tahoe, "stop", self.nodedir)
+
+    def read_and_check(self, f):
+        expected_md5_s = f[f.find(".")+1:]
+        out = self.cli("get", "testgrid:" + f)
+        got_md5_s = md5.new(out).hexdigest()
+        if got_md5_s != expected_md5_s:
+            raise CommandFailed("%s had md5sum of %s" % (f, got_md5_s))
+
+    def delete_and_check(self, dirname, f):
+        oldfiles = self.listdir(dirname)
+        if dirname:
+            absfilename = "testgrid:" + dirname + "/" + f
+        else:
+            absfilename = "testgrid:" + f
+        if f not in oldfiles:
+            raise CommandFailed("um, '%s' was supposed to already be in %s"
+                                % (f, dirname))
+        self.cli("rm", absfilename)
+        newfiles = self.listdir(dirname)
+        if f in newfiles:
+            raise CommandFailed("failed to remove '%s' from %s" % (f, dirname))
+
+    def listdir(self, dirname):
+        out = self.cli("ls", "testgrid:"+dirname).strip().split("\n")
+        files = [f.strip() for f in out]
+        print " ", files
+        return files
+
+    def do_test(self):
+        files = self.listdir("")
+        for f in files:
+            if f.startswith("old.") or f.startswith("recent."):
+                self.read_and_check("" + f)
+        for f in files:
+            if f.startswith("recent."):
+                self.delete_and_check("", f)
+        files = self.listdir("recentdir")
+        for f in files:
+            if f.startswith("old.") or f.startswith("recent."):
+                self.read_and_check("recentdir/" + f)
+        for f in files:
+            if f.startswith("recent."):
+                self.delete_and_check("recentdir", f)
+        self.delete_and_check("", "recentdir")
+
+        self.cli("mkdir", "testgrid:recentdir")
+        fn, data = self.makefile("recent")
+        self.put("recentdir/"+fn, data)
+        files = self.listdir("recentdir")
+        if fn not in files:
+            raise CommandFailed("failed to put %s in recentdir/" % fn)
+        fn, data = self.makefile("recent")
+        self.put(fn, data)
+        files = self.listdir("")
+        if fn not in files:
+            raise CommandFailed("failed to put %s in testgrid:" % fn)
+
+        self.update("log")
+        self.update("recentlog")
+        self.delete_and_check("", "recentlog")
+        self.put_mutable("recentlog", "Recent Mutable Log Header\n\n")
+
+    def put(self, fn, data):
+        self.cli("put", "testgrid:"+fn, stdin=data, ignore_stderr=True)
+
+    def put_mutable(self, fn, data):
+        self.cli("put", "--mutable", "testgrid:"+fn,
+                 stdin=data, ignore_stderr=True)
+
+    def update(self, fn):
+        old = self.cli("get", "testgrid:"+fn)
+        new = old + time.ctime() + "\n"
+        self.put(fn, new)
+
+    def makefile(self, prefix):
+        size = random.randint(10001, 10100)
+        data = os.urandom(size)
+        md5sum = md5.new(data).hexdigest()
+        fn = prefix + "." + md5sum
+        return fn, data
+
+    def run(self):
+        self.stop_old_node()
+        self.start_node()
+        try:
+            self.do_test()
+        finally:
+            self.stop_node()
+
+def main():
+    config = GridTesterOptions()
+    config.parseOptions()
+    gt = GridTester(config)
+    gt.run()
+
+if __name__ == "__main__":
+    main()