]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/test/test_system.py
remove "announcement_distinct_hosts" from introweb JSON
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_system.py
index 4a8e40617b8fbbeaa5325b6cb56284a4a204d38f..1887c7e28b2e4fa4652a9c64c50e8e10c2a0494f 100644 (file)
@@ -1,6 +1,7 @@
-from base64 import b32encode
-import os, sys, time, simplejson
+
+import os, re, sys, time, simplejson
 from cStringIO import StringIO
+
 from twisted.trial import unittest
 from twisted.internet import defer
 from twisted.internet import threads # CLI tests use deferToThread
@@ -14,7 +15,8 @@ from allmydata.immutable.literal import LiteralFileNode
 from allmydata.immutable.filenode import ImmutableFileNode
 from allmydata.util import idlib, mathutil
 from allmydata.util import log, base32
-from allmydata.util.encodingutil import quote_output, unicode_to_argv, get_filesystem_encoding
+from allmydata.util.verlib import NormalizedVersion
+from allmydata.util.encodingutil import quote_output, unicode_to_argv
 from allmydata.util.fileutil import abspath_expanduser_unicode
 from allmydata.util.consumer import MemoryConsumer, download_to_data
 from allmydata.scripts import runner
@@ -24,7 +26,9 @@ from allmydata.monitor import Monitor
 from allmydata.mutable.common import NotWriteableError
 from allmydata.mutable import layout as mutable_layout
 from allmydata.mutable.publish import MutableData
-from foolscap.api import DeadReferenceError
+
+import foolscap
+from foolscap.api import DeadReferenceError, fireEventually
 from twisted.python.failure import Failure
 from twisted.web.client import getPage
 from twisted.web.error import Error
@@ -98,7 +102,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d = self.set_up_nodes()
         def _check_connections(res):
             for c in self.clients:
-                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 5
+                c.encoding_params['happy'] = 5
                 all_peerids = c.get_storage_broker().get_all_serverids()
                 self.failUnlessEqual(len(all_peerids), self.numclients)
                 sb = c.storage_broker
@@ -122,7 +126,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
             return d1
         d.addCallback(_do_upload)
         def _upload_done(results):
-            theuri = results.uri
+            theuri = results.get_uri()
             log.msg("upload finished: uri is %s" % (theuri,))
             self.uri = theuri
             assert isinstance(self.uri, str), self.uri
@@ -194,7 +198,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
                     facility="tahoe.tests")
             d1 = download_to_data(badnode)
             def _baduri_should_fail(res):
-                log.msg("finished downloading non-existend URI",
+                log.msg("finished downloading non-existent URI",
                         level=log.UNUSUAL, facility="tahoe.tests")
                 self.failUnless(isinstance(res, Failure))
                 self.failUnless(res.check(NoSharesError),
@@ -210,15 +214,21 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
                                                       add_to_sparent=True))
         def _added(extra_node):
             self.extra_node = extra_node
-            self.extra_node.DEFAULT_ENCODING_PARAMETERS['happy'] = 5
+            self.extra_node.encoding_params['happy'] = 5
         d.addCallback(_added)
 
+        def _has_helper():
+            uploader = self.extra_node.getServiceNamed("uploader")
+            furl, connected = uploader.get_helper_info()
+            return connected
+        d.addCallback(lambda ign: self.poll(_has_helper))
+
         HELPER_DATA = "Data that needs help to upload" * 1000
         def _upload_with_helper(res):
             u = upload.Data(HELPER_DATA, convergence=convergence)
             d = self.extra_node.upload(u)
             def _uploaded(results):
-                n = self.clients[1].create_node_from_uri(results.uri)
+                n = self.clients[1].create_node_from_uri(results.get_uri())
                 return download_to_data(n)
             d.addCallback(_uploaded)
             def _check(newdata):
@@ -232,7 +242,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
             u.debug_stash_RemoteEncryptedUploadable = True
             d = self.extra_node.upload(u)
             def _uploaded(results):
-                n = self.clients[1].create_node_from_uri(results.uri)
+                n = self.clients[1].create_node_from_uri(results.get_uri())
                 return download_to_data(n)
             d.addCallback(_uploaded)
             def _check(newdata):
@@ -244,17 +254,22 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         if convergence is not None:
             d.addCallback(_upload_duplicate_with_helper)
 
+        d.addCallback(fireEventually)
+
         def _upload_resumable(res):
             DATA = "Data that needs help to upload and gets interrupted" * 1000
             u1 = CountingDataUploadable(DATA, convergence=convergence)
             u2 = CountingDataUploadable(DATA, convergence=convergence)
 
             # we interrupt the connection after about 5kB by shutting down
-            # the helper, then restartingit.
+            # the helper, then restarting it.
             u1.interrupt_after = 5000
             u1.interrupt_after_d = defer.Deferred()
-            u1.interrupt_after_d.addCallback(lambda res:
-                                             self.bounce_client(0))
+            bounced_d = defer.Deferred()
+            def _do_bounce(res):
+                d = self.bounce_client(0)
+                d.addBoth(bounced_d.callback)
+            u1.interrupt_after_d.addCallback(_do_bounce)
 
             # sneak into the helper and reduce its chunk size, so that our
             # debug_interrupt will sever the connection on about the fifth
@@ -265,34 +280,27 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
             # this same test run, but I'm not currently worried about it.
             offloaded.CHKCiphertextFetcher.CHUNK_SIZE = 1000
 
-            d = self.extra_node.upload(u1)
-
-            def _should_not_finish(res):
-                self.fail("interrupted upload should have failed, not finished"
-                          " with result %s" % (res,))
-            def _interrupted(f):
-                f.trap(DeadReferenceError)
-
-                # make sure we actually interrupted it before finishing the
-                # file
-                self.failUnless(u1.bytes_read < len(DATA),
-                                "read %d out of %d total" % (u1.bytes_read,
-                                                             len(DATA)))
-
-                log.msg("waiting for reconnect", level=log.NOISY,
-                        facility="tahoe.test.test_system")
-                # now, we need to give the nodes a chance to notice that this
-                # connection has gone away. When this happens, the storage
-                # servers will be told to abort their uploads, removing the
-                # partial shares. Unfortunately this involves TCP messages
-                # going through the loopback interface, and we can't easily
-                # predict how long that will take. If it were all local, we
-                # could use fireEventually() to stall. Since we don't have
-                # the right introduction hooks, the best we can do is use a
-                # fixed delay. TODO: this is fragile.
-                u1.interrupt_after_d.addCallback(self.stall, 2.0)
-                return u1.interrupt_after_d
-            d.addCallbacks(_should_not_finish, _interrupted)
+            upload_d = self.extra_node.upload(u1)
+            # The upload will start, and bounce_client() will be called after
+            # about 5kB. bounced_d will fire after bounce_client() finishes
+            # shutting down and restarting the node.
+            d = bounced_d
+            def _bounced(ign):
+                # By this point, the upload should have failed because of the
+                # interruption. upload_d will fire in a moment
+                def _should_not_finish(res):
+                    self.fail("interrupted upload should have failed, not"
+                              " finished with result %s" % (res,))
+                def _interrupted(f):
+                    f.trap(DeadReferenceError)
+                    # make sure we actually interrupted it before finishing
+                    # the file
+                    self.failUnless(u1.bytes_read < len(DATA),
+                                    "read %d out of %d total" %
+                                    (u1.bytes_read, len(DATA)))
+                upload_d.addCallbacks(_should_not_finish, _interrupted)
+                return upload_d
+            d.addCallback(_bounced)
 
             def _disconnected(res):
                 # check to make sure the storage servers aren't still hanging
@@ -306,13 +314,12 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
                     self.failIf(os.path.exists(incdir) and os.listdir(incdir))
             d.addCallback(_disconnected)
 
-            # then we need to give the reconnector a chance to
-            # reestablish the connection to the helper.
             d.addCallback(lambda res:
-                          log.msg("wait_for_connections", level=log.NOISY,
+                          log.msg("wait_for_helper", level=log.NOISY,
                                   facility="tahoe.test.test_system"))
-            d.addCallback(lambda res: self.wait_for_connections())
-
+            # then we need to wait for the extra node to reestablish its
+            # connection to the helper.
+            d.addCallback(lambda ign: self.poll(_has_helper))
 
             d.addCallback(lambda res:
                           log.msg("uploading again", level=log.NOISY,
@@ -320,7 +327,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
             d.addCallback(lambda res: self.extra_node.upload(u2))
 
             def _uploaded(results):
-                cap = results.uri
+                cap = results.get_uri()
                 log.msg("Second upload complete", level=log.NOISY,
                         facility="tahoe.test.test_system")
 
@@ -717,7 +724,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d = self.set_up_nodes(use_stats_gatherer=True)
         def _new_happy_semantics(ign):
             for c in self.clients:
-                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+                c.encoding_params['happy'] = 1
         d.addCallback(_new_happy_semantics)
         d.addCallback(self._test_introweb)
         d.addCallback(self.log, "starting publish")
@@ -805,8 +812,6 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
                                      {"storage": 5})
                 self.failUnlessEqual(data["announcement_summary"],
                                      {"storage": 5})
-                self.failUnlessEqual(data["announcement_distinct_hosts"],
-                                     {"storage": 1})
             except unittest.FailTest:
                 print
                 print "GET %s?t=json output was:" % self.introweb_url
@@ -1090,16 +1095,15 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         public = "uri/" + self._root_directory_uri
         d = getPage(base)
         def _got_welcome(page):
-            # XXX This test is oversensitive to formatting
-            expected = "Connected to <span>%d</span>\n     of <span>%d</span> known storage servers:" % (self.numclients, self.numclients)
-            self.failUnless(expected in page,
-                            "I didn't see the right 'connected storage servers'"
-                            " message in: %s" % page
-                            )
-            expected = "<th>My nodeid:</th> <td class=\"nodeid mine data-chars\">%s</td>" % (b32encode(self.clients[0].nodeid).lower(),)
-            self.failUnless(expected in page,
-                            "I didn't see the right 'My nodeid' message "
-                            "in: %s" % page)
+            html = page.replace('\n', ' ')
+            connected_re = r'Connected to <span>%d</span>\s*of <span>%d</span> known storage servers' % (self.numclients, self.numclients)
+            self.failUnless(re.search(connected_re, html),
+                            "I didn't see the right '%s' message in:\n%s" % (connected_re, page))
+            # nodeids/tubids don't have any regexp-special characters
+            nodeid_re = r'<th>Node ID:</th>\s*<td title="TubID: %s">%s</td>' % (
+                self.clients[0].get_long_tubid(), self.clients[0].get_long_nodeid())
+            self.failUnless(re.search(nodeid_re, html),
+                            "I didn't see the right '%s' message in:\n%s" % (nodeid_re, page))
             self.failUnless("Helper: 0 active uploads" in page)
         d.addCallback(_got_welcome)
         d.addCallback(self.log, "done with _got_welcome")
@@ -1107,9 +1111,9 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         # get the welcome page from the node that uses the helper too
         d.addCallback(lambda res: getPage(self.helper_webish_url))
         def _got_welcome_helper(page):
-            self.failUnless("Connected to helper?: <span>yes</span>" in page,
-                            page)
-            self.failUnless("Not running helper" in page)
+            html = page.replace('\n', ' ')
+            self.failUnless(re.search('<img src="img/connected-yes.png" alt="Connected" />', html), page)
+            self.failUnlessIn("Not running helper", page)
         d.addCallback(_got_welcome_helper)
 
         d.addCallback(lambda res: getPage(base + public))
@@ -1165,7 +1169,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         def _new_happy_semantics(ign):
             for c in self.clients:
                 # these get reset somewhere? Whatever.
-                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+                c.encoding_params['happy'] = 1
         d.addCallback(_new_happy_semantics)
         d.addCallback(lambda res: self.PUT(public + "/subdir3/big.txt",
                                            "big" * 500000)) # 1.5MB
@@ -1218,8 +1222,12 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
             return self.GET("status/publish-%d" % self._publish_status)
         d.addCallback(_got_update)
         def _got_publish(res):
+            self.failUnlessIn("Publish Results", res)
             return self.GET("status/retrieve-%d" % self._retrieve_status)
         d.addCallback(_got_publish)
+        def _got_retrieve(res):
+            self.failUnlessIn("Retrieve Results", res)
+        d.addCallback(_got_retrieve)
 
         # check that the helper status page exists
         d.addCallback(lambda res:
@@ -1263,7 +1271,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         # itself) doesn't explode when you ask for its status
         d.addCallback(lambda res: getPage(self.helper_webish_url + "status/"))
         def _got_non_helper_status(res):
-            self.failUnless("Upload and Download Status" in res)
+            self.failUnlessIn("Recent and Active Operations", res)
         d.addCallback(_got_non_helper_status)
 
         # or for helper status with t=json
@@ -1277,8 +1285,8 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         # see if the statistics page exists
         d.addCallback(lambda res: self.GET("statistics"))
         def _got_stats(res):
-            self.failUnless("Node Statistics" in res)
-            self.failUnless("  'downloader.files_downloaded': 5," in res, res)
+            self.failUnlessIn("Operational Statistics", res)
+            self.failUnlessIn("  'downloader.files_downloaded': 5,", res)
         d.addCallback(_got_stats)
         d.addCallback(lambda res: self.GET("statistics?t=json"))
         def _got_stats_json(res):
@@ -1389,18 +1397,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d.addCallback(self._test_control2, control_furl_file)
         return d
     def _test_control2(self, rref, filename):
-        d = rref.callRemote("upload_from_file_to_uri",
-                            filename.encode(get_filesystem_encoding()), convergence=None)
-        downfile = os.path.join(self.basedir, "control.downfile").encode(get_filesystem_encoding())
-        d.addCallback(lambda uri:
-                      rref.callRemote("download_from_uri_to_file",
-                                      uri, downfile))
-        def _check(res):
-            self.failUnlessEqual(res, downfile)
-            data = open(downfile, "r").read()
-            expected_data = open(filename, "r").read()
-            self.failUnlessEqual(data, expected_data)
-        d.addCallback(_check)
+        d = defer.succeed(None)
         d.addCallback(lambda res: rref.callRemote("speed_test", 1, 200, False))
         if sys.platform in ("linux2", "linux3"):
             d.addCallback(lambda res: rref.callRemote("get_memory_usage"))
@@ -1431,7 +1428,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
 
         def run(ignored, verb, *args, **kwargs):
             stdin = kwargs.get("stdin", "")
-            newargs = [verb] + nodeargs + list(args)
+            newargs = nodeargs + [verb] + list(args)
             return self._run_cli(newargs, stdin=stdin)
 
         def _check_ls((out,err), expected_children, unexpected_children=[]):
@@ -1675,7 +1672,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         open(os.path.join(sdn2, "rfile5"), "wb").write("rfile5")
 
         # from disk into tahoe
-        d.addCallback(run, "cp", "-r", dn, "tahoe:dir1")
+        d.addCallback(run, "cp", "-r", dn, "tahoe:")
         d.addCallback(run, "ls")
         d.addCallback(_check_ls, ["dir1"])
         d.addCallback(run, "ls", "dir1")
@@ -1693,7 +1690,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         def _check_cp_r_out((out,err)):
             def _cmp(name):
                 old = open(os.path.join(dn, name), "rb").read()
-                newfn = os.path.join(dn_copy, name)
+                newfn = os.path.join(dn_copy, "dir1", name)
                 self.failUnless(os.path.exists(newfn))
                 new = open(newfn, "rb").read()
                 self.failUnlessEqual(old, new)
@@ -1712,7 +1709,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d.addCallback(run, "cp", "-r", "--caps-only", "tahoe:dir1", dn_copy2)
         def _check_capsonly((out,err)):
             # these should all be LITs
-            x = open(os.path.join(dn_copy2, "subdir2", "rfile4")).read()
+            x = open(os.path.join(dn_copy2, "dir1", "subdir2", "rfile4")).read()
             y = uri.from_string_filenode(x)
             self.failUnlessEqual(y.data, "rfile4")
         d.addCallback(_check_capsonly)
@@ -1721,13 +1718,13 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d.addCallback(run, "cp", "-r", "tahoe:dir1", "tahoe:dir1-copy")
         d.addCallback(run, "ls")
         d.addCallback(_check_ls, ["dir1", "dir1-copy"])
-        d.addCallback(run, "ls", "dir1-copy")
+        d.addCallback(run, "ls", "dir1-copy/dir1")
         d.addCallback(_check_ls, ["rfile1", "rfile2", "rfile3", "subdir2"],
                       ["rfile4", "rfile5"])
-        d.addCallback(run, "ls", "tahoe:dir1-copy/subdir2")
+        d.addCallback(run, "ls", "tahoe:dir1-copy/dir1/subdir2")
         d.addCallback(_check_ls, ["rfile4", "rfile5"],
                       ["rfile1", "rfile2", "rfile3"])
-        d.addCallback(run, "get", "dir1-copy/subdir2/rfile4")
+        d.addCallback(run, "get", "dir1-copy/dir1/subdir2/rfile4")
         d.addCallback(_check_stdout_against, data="rfile4")
 
         # and copy it a second time, which ought to overwrite the same files
@@ -1736,7 +1733,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         # tahoe_ls doesn't currently handle the error correctly: it tries to
         # JSON-parse a traceback.
 ##         def _ls_missing(res):
-##             argv = ["ls"] + nodeargs + ["bogus"]
+##             argv = nodeargs + ["ls", "bogus"]
 ##             return self._run_cli(argv)
 ##         d.addCallback(_ls_missing)
 ##         def _check_ls_missing((out,err)):
@@ -1754,13 +1751,13 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
         d = self.set_up_nodes()
         def _new_happy_semantics(ign):
             for c in self.clients:
-                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+                c.encoding_params['happy'] = 1
         d.addCallback(_new_happy_semantics)
 
         def _run_in_subprocess(ignored, verb, *args, **kwargs):
             stdin = kwargs.get("stdin")
             env = kwargs.get("env")
-            newargs = [verb, "--node-directory", self.getdir("client0")] + list(args)
+            newargs = ["--node-directory", self.getdir("client0"), verb] + list(args)
             return self.run_bintahoe(newargs, stdin=stdin, env=env)
 
         def _check_succeeded(res, check_stderr=True):
@@ -1876,3 +1873,39 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
             return d
         d.addCallback(_got_lit_filenode)
         return d
+
+
+class Connections(SystemTestMixin, unittest.TestCase):
+    def test_rref(self):
+        if NormalizedVersion(foolscap.__version__) < NormalizedVersion('0.6.4'):
+            raise unittest.SkipTest("skipped due to http://foolscap.lothar.com/trac/ticket/196 "
+                                    "(which does not affect normal usage of Tahoe-LAFS)")
+
+        self.basedir = "system/Connections/rref"
+        d = self.set_up_nodes(2)
+        def _start(ign):
+            self.c0 = self.clients[0]
+            nonclients = [s for s in self.c0.storage_broker.get_connected_servers()
+                          if s.get_serverid() != self.c0.nodeid]
+            self.failUnlessEqual(len(nonclients), 1)
+
+            self.s1 = nonclients[0]  # s1 is the server, not c0
+            self.s1_rref = self.s1.get_rref()
+            self.failIfEqual(self.s1_rref, None)
+            self.failUnless(self.s1.is_connected())
+        d.addCallback(_start)
+
+        # now shut down the server
+        d.addCallback(lambda ign: self.clients[1].disownServiceParent())
+        # and wait for the client to notice
+        def _poll():
+            return len(self.c0.storage_broker.get_connected_servers()) < 2
+        d.addCallback(lambda ign: self.poll(_poll))
+
+        def _down(ign):
+            self.failIf(self.s1.is_connected())
+            rref = self.s1.get_rref()
+            self.failUnless(rref)
+            self.failUnlessIdentical(rref, self.s1_rref)
+        d.addCallback(_down)
+        return d