From 0eb6b324a4fcda2f9309b26281250793f95421f1 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@allmydata.com>
Date: Fri, 21 Nov 2008 17:43:52 -0700
Subject: [PATCH] #538: add remote_get_version() to four main Referenceable
 objects: Introducer Service, Storage Server, Helper, CHK Upload Helper.
 Remove unused storage-server get_versions().

---
 src/allmydata/control.py               |  2 +-
 src/allmydata/interfaces.py            | 32 +++++++++++++-------------
 src/allmydata/introducer/interfaces.py |  4 +++-
 src/allmydata/introducer/server.py     |  8 +++++++
 src/allmydata/offloaded.py             | 15 ++++++++++++
 src/allmydata/storage.py               | 13 +++++------
 src/allmydata/test/test_client.py      |  5 ++--
 7 files changed, 52 insertions(+), 27 deletions(-)

diff --git a/src/allmydata/control.py b/src/allmydata/control.py
index 28e5c615..b566231f 100644
--- a/src/allmydata/control.py
+++ b/src/allmydata/control.py
@@ -81,7 +81,7 @@ class ControlServer(Referenceable, service.Service):
             return results
         peerid, connection = everyone_left.pop(0)
         start = time.time()
-        d = connection.callRemote("get_versions")
+        d = connection.callRemote("get_version")
         def _done(ignored):
             stop = time.time()
             elapsed = stop - start
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index 798b4baf..bcfbf626 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -1,7 +1,7 @@
 
 from zope.interface import Interface
 from foolscap.schema import StringConstraint, ListOf, TupleOf, SetOf, DictOf, \
-     ChoiceOf, IntegerConstraint
+     ChoiceOf, IntegerConstraint, Any
 from foolscap import RemoteInterface, Referenceable
 
 HASH_SIZE=32
@@ -86,21 +86,11 @@ ReadData = ListOf(ShareData)
 class RIStorageServer(RemoteInterface):
     __remote_name__ = "RIStorageServer.tahoe.allmydata.com"
 
-    def get_versions():
+    def get_version():
         """
-        Return a tuple of (my_version, oldest_supported) strings.  Each string can be parsed by
-        a pyutil.version_class.Version instance or a distutils.version.LooseVersion instance,
-        and then compared. The first goal is to make sure that nodes are not confused by
-        speaking to an incompatible peer. The second goal is to enable the development of
-        backwards-compatibility code.
-
-        The meaning of the oldest_supported element is that if you treat this storage server as
-        though it were of that version, then you will not be disappointed.
-
-        The precise meaning of this method might change in incompatible ways until we get the
-        whole compatibility scheme nailed down.
+        Return a dictionary of version information.
         """
-        return TupleOf(str, str)
+        return DictOf(str, Any())
 
     def allocate_buckets(storage_index=StorageIndex,
                          renew_secret=LeaseRenewSecret,
@@ -351,8 +341,6 @@ class IStorageBucketReader(Interface):
 
 
 # hm, we need a solution for forward references in schemas
-from foolscap.schema import Any
-
 FileNode_ = Any() # TODO: foolscap needs constraints on copyables
 DirectoryNode_ = Any() # TODO: same
 AnyNode_ = ChoiceOf(FileNode_, DirectoryNode_)
@@ -2077,6 +2065,12 @@ class RIEncryptedUploadable(RemoteInterface):
 class RICHKUploadHelper(RemoteInterface):
     __remote_name__ = "RIUploadHelper.tahoe.allmydata.com"
 
+    def get_version():
+        """
+        Return a dictionary of version information.
+        """
+        return DictOf(str, Any())
+
     def upload(reader=RIEncryptedUploadable):
         return UploadResults
 
@@ -2084,6 +2078,12 @@ class RICHKUploadHelper(RemoteInterface):
 class RIHelper(RemoteInterface):
     __remote_name__ = "RIHelper.tahoe.allmydata.com"
 
+    def get_version():
+        """
+        Return a dictionary of version information.
+        """
+        return DictOf(str, Any())
+
     def upload_chk(si=StorageIndex):
         """See if a file with a given storage index needs uploading. The
         helper will ask the appropriate storage servers to see if the file
diff --git a/src/allmydata/introducer/interfaces.py b/src/allmydata/introducer/interfaces.py
index d084f498..56b87b16 100644
--- a/src/allmydata/introducer/interfaces.py
+++ b/src/allmydata/introducer/interfaces.py
@@ -1,6 +1,6 @@
 
 from zope.interface import Interface
-from foolscap.schema import StringConstraint, TupleOf, SetOf
+from foolscap.schema import StringConstraint, TupleOf, SetOf, DictOf, Any
 from foolscap import RemoteInterface
 FURL = StringConstraint(1000)
 
@@ -68,6 +68,8 @@ class RIIntroducerSubscriberService(RemoteInterface):
 
 class RIIntroducerPublisherAndSubscriberService(RemoteInterface):
     __remote_name__ = "RIIntroducerPublisherAndSubscriberService.tahoe.allmydata.com"
+    def get_version():
+        return DictOf(str, Any())
     def publish(announcement=Announcement):
         return None
     def subscribe(subscriber=RIIntroducerSubscriberClient, service_name=str):
diff --git a/src/allmydata/introducer/server.py b/src/allmydata/introducer/server.py
index c71237a4..3005b54d 100644
--- a/src/allmydata/introducer/server.py
+++ b/src/allmydata/introducer/server.py
@@ -3,6 +3,7 @@ import time, os.path
 from zope.interface import implements
 from twisted.application import service
 from foolscap import Referenceable
+import allmydata
 from allmydata import node
 from allmydata.util import log
 from allmydata.introducer.interfaces import \
@@ -46,6 +47,10 @@ class IntroducerNode(node.Node):
 class IntroducerService(service.MultiService, Referenceable):
     implements(RIIntroducerPublisherAndSubscriberService)
     name = "introducer"
+    VERSION = { "http://allmydata.org/tahoe/protocols/introducer/v1":
+                 { },
+                "application-version": str(allmydata.__version__),
+                }
 
     def __init__(self, basedir="."):
         service.MultiService.__init__(self)
@@ -64,6 +69,9 @@ class IntroducerService(service.MultiService, Referenceable):
     def get_subscribers(self):
         return self._subscribers
 
+    def remote_get_version(self):
+        return self.VERSION
+
     def remote_publish(self, announcement):
         self.log("introducer: announcement published: %s" % (announcement,) )
         index = make_index(announcement)
diff --git a/src/allmydata/offloaded.py b/src/allmydata/offloaded.py
index 52a9f4fe..048203d7 100644
--- a/src/allmydata/offloaded.py
+++ b/src/allmydata/offloaded.py
@@ -5,6 +5,7 @@ from twisted.application import service
 from twisted.internet import defer
 from foolscap import Referenceable, DeadReferenceError
 from foolscap.eventual import eventually
+import allmydata
 from allmydata import interfaces, storage, uri
 from allmydata.immutable import upload
 from allmydata.immutable.layout import ReadBucketProxy
@@ -131,6 +132,10 @@ class CHKUploadHelper(Referenceable, upload.CHKUploader):
     remote AssistedUploader.
     """
     implements(interfaces.RICHKUploadHelper)
+    VERSION = { "http://allmydata.org/tahoe/protocols/helper/chk-upload/v1" :
+                 { },
+                "application-version": str(allmydata.__version__),
+                }
 
     def __init__(self, storage_index, helper,
                  incoming_file, encoding_file,
@@ -186,6 +191,9 @@ class CHKUploadHelper(Referenceable, upload.CHKUploader):
         self.log("no ciphertext yet", level=log.NOISY)
         return (self._results, self)
 
+    def remote_get_version(self):
+        return self.VERSION
+
     def remote_upload(self, reader):
         # reader is an RIEncryptedUploadable. I am specified to return an
         # UploadResults dictionary.
@@ -482,6 +490,10 @@ class Helper(Referenceable, service.MultiService):
     # helper at random.
 
     name = "helper"
+    VERSION = { "http://allmydata.org/tahoe/protocols/helper/v1" :
+                 { },
+                "application-version": str(allmydata.__version__),
+                }
     chk_upload_helper_class = CHKUploadHelper
     MAX_UPLOAD_STATUSES = 10
 
@@ -554,6 +566,9 @@ class Helper(Referenceable, service.MultiService):
         stats.update(self._counters)
         return stats
 
+    def remote_get_version(self):
+        return self.VERSION
+
     def remote_upload_chk(self, storage_index):
         self.count("chk_upload_helper.upload_requests")
         r = upload.UploadResults()
diff --git a/src/allmydata/storage.py b/src/allmydata/storage.py
index 7387daef..526de307 100644
--- a/src/allmydata/storage.py
+++ b/src/allmydata/storage.py
@@ -1,5 +1,4 @@
 import os, re, weakref, stat, struct, time
-from distutils.version import LooseVersion
 
 from foolscap import Referenceable
 from twisted.application import service
@@ -764,10 +763,10 @@ def create_mutable_sharefile(filename, my_nodeid, write_enabler, parent):
 class StorageServer(service.MultiService, Referenceable):
     implements(RIStorageServer, IStatsProducer)
     name = 'storage'
-
-    # This means that if a client treats me as though I were a 1.0.0 storage server, they will
-    # not be disappointed.
-    OLDEST_SUPPORTED_VERSION = LooseVersion("1.0.0")
+    VERSION = { "http://allmydata.org/tahoe/protocols/storage/v1" :
+                 { "maximum-immutable-share-size": 2**32 },
+                "application-version": str(allmydata.__version__),
+                }
 
     def __init__(self, storedir, sizelimit=None,
                  discard_storage=False, readonly_storage=False,
@@ -899,8 +898,8 @@ class StorageServer(service.MultiService, Referenceable):
             space += bw.allocated_size()
         return space
 
-    def remote_get_versions(self):
-        return (str(allmydata.__version__), str(self.OLDEST_SUPPORTED_VERSION))
+    def remote_get_version(self):
+        return self.VERSION
 
     def remote_allocate_buckets(self, storage_index,
                                 renew_secret, cancel_secret,
diff --git a/src/allmydata/test/test_client.py b/src/allmydata/test/test_client.py
index 836cd74f..f38cb32a 100644
--- a/src/allmydata/test/test_client.py
+++ b/src/allmydata/test/test_client.py
@@ -153,8 +153,9 @@ class Basic(unittest.TestCase):
         open(os.path.join(basedir, "vdrive.furl"), "w").write("")
         c = client.Client(basedir)
         ss = c.getServiceNamed("storage")
-        mine, oldest = ss.remote_get_versions()
-        self.failUnlessEqual(mine, str(allmydata.__version__))
+        verdict = ss.remote_get_version()
+        self.failUnlessEqual(verdict["application-version"],
+                             str(allmydata.__version__))
         self.failIfEqual(str(allmydata.__version__), "unknown")
         self.failUnless("." in str(allmydata.__version__),
                         "non-numeric version in '%s'" % allmydata.__version__)
-- 
2.45.2