diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py index 31fc61444..e495c759a 100644 --- a/src/allmydata/interfaces.py +++ b/src/allmydata/interfaces.py @@ -417,6 +417,7 @@ class IStorageBroker(Interface): public attributes:: service_name: the type of service provided, like 'storage' + announcement_time: when we first heard about this service last_connect_time: when we last established a connection last_loss_time: when we last lost a connection diff --git a/src/allmydata/storage_client.py b/src/allmydata/storage_client.py index 81dc00427..dd9780f23 100644 --- a/src/allmydata/storage_client.py +++ b/src/allmydata/storage_client.py @@ -168,6 +168,7 @@ class NativeStorageServer: the their version information. I remember information about when we were last connected too, even if we aren't currently connected. + @ivar announcement_time: when we first heard about this service @ivar last_connect_time: when we last established a connection @ivar last_loss_time: when we last lost a connection @@ -215,6 +216,7 @@ class NativeStorageServer: self._long_description = tubid_s self._short_description = tubid_s[:6] + self.announcement_time = time.time() self.last_connect_time = None self.last_loss_time = None self.remote_host = None @@ -265,11 +267,8 @@ class NativeStorageServer: return self.last_connect_time def get_last_loss_time(self): return self.last_loss_time - def get_last_received_data_time(self): - if self.rref is None: - return None - else: - return self.rref.getDataLastReceivedAt() + def get_announcement_time(self): + return self.announcement_time def get_available_space(self): version = self.get_version() diff --git a/src/allmydata/test/common_util.py b/src/allmydata/test/common_util.py index 47dbe59a3..e51ab8b00 100644 --- a/src/allmydata/test/common_util.py +++ b/src/allmydata/test/common_util.py @@ -173,24 +173,6 @@ class TestMixin(SignalMixin): if required_to_quiesce and active: self.fail("Reactor was still active when it was required to be quiescent.") - -class TimezoneMixin(object): - - def setTimezone(self, timezone): - unset = object() - originalTimezone = os.environ.get('TZ', unset) - def restoreTimezone(): - if originalTimezone is unset: - del os.environ['TZ'] - time.tzset() - else: - os.environ['TZ'] = originalTimezone - time.tzset() - os.environ['TZ'] = timezone - time.tzset() - self.addCleanup(restoreTimezone) - - try: import win32file import win32con diff --git a/src/allmydata/test/test_util.py b/src/allmydata/test/test_util.py index 5fe2fbd03..adaec89f3 100644 --- a/src/allmydata/test/test_util.py +++ b/src/allmydata/test/test_util.py @@ -1021,40 +1021,6 @@ class TimeFormat(unittest.TestCase): test_format_time_y2038.todo = "This test is known to fail on systems with 32-bit time_t." - def test_format_delta(self): - time_1 = 1389812723 - time_5s_delta = 1389812728 - time_28m7s_delta = 1389814410 - time_1h_delta = 1389816323 - time_1d21h46m49s_delta = 1389977532 - - self.failUnlessEqual( - time_format.format_delta(time_1, time_1), '0s') - - self.failUnlessEqual( - time_format.format_delta(time_1, time_5s_delta), '5s') - self.failUnlessEqual( - time_format.format_delta(time_1, time_28m7s_delta), '28m 7s') - self.failUnlessEqual( - time_format.format_delta(time_1, time_1h_delta), '1h 0m 0s') - self.failUnlessEqual( - time_format.format_delta(time_1, time_1d21h46m49s_delta), '1d 21h 46m 49s') - - self.failUnlessEqual( - time_format.format_delta(time_1d21h46m49s_delta, time_1), '-') - - # time_1 with a decimal fraction will make the delta 1s less - time_1decimal = 1389812723.383963 - - self.failUnlessEqual( - time_format.format_delta(time_1decimal, time_5s_delta), '4s') - self.failUnlessEqual( - time_format.format_delta(time_1decimal, time_28m7s_delta), '28m 6s') - self.failUnlessEqual( - time_format.format_delta(time_1decimal, time_1h_delta), '59m 59s') - self.failUnlessEqual( - time_format.format_delta(time_1decimal, time_1d21h46m49s_delta), '1d 21h 46m 48s') - class CacheDir(unittest.TestCase): def test_basic(self): basedir = "test_util/CacheDir/test_basic" diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index 5b151ecb6..723ae7ac6 100644 --- a/src/allmydata/test/test_web.py +++ b/src/allmydata/test/test_web.py @@ -172,28 +172,21 @@ class FakeHistory: return [] class FakeDisplayableServer(StubServer): - def __init__(self, serverid, nickname, connected, - last_connect_time, last_loss_time, last_rx_time): + def __init__(self, serverid, nickname): StubServer.__init__(self, serverid) self.announcement = {"my-version": "allmydata-tahoe-fake", "service-name": "storage", "nickname": nickname} - self.connected = connected - self.last_loss_time = last_loss_time - self.last_rx_time = last_rx_time - self.last_connect_time = last_connect_time def is_connected(self): - return self.connected + return True def get_permutation_seed(self): return "" def get_remote_host(self): return "" def get_last_loss_time(self): - return self.last_loss_time - def get_last_received_data_time(self): - return self.last_rx_time - def get_last_connect_time(self): - return self.last_connect_time + return None + def get_announcement_time(self): + return None def get_announcement(self): return self.announcement def get_nickname(self): @@ -249,13 +242,7 @@ class FakeClient(Client): self.storage_broker = StorageFarmBroker(None, permute_peers=True) # fake knowledge of another server self.storage_broker.test_add_server("other_nodeid", - FakeDisplayableServer( - serverid="other_nodeid", nickname=u"other_nickname \u263B", connected = True, - last_connect_time = 10, last_loss_time = 20, last_rx_time = 30)) - self.storage_broker.test_add_server("disconnected_nodeid", - FakeDisplayableServer( - serverid="other_nodeid", nickname=u"disconnected_nickname \u263B", connected = False, - last_connect_time = 15, last_loss_time = 25, last_rx_time = 35)) + FakeDisplayableServer("other_nodeid", u"other_nickname \u263B")) self.introducer_client = None self.history = FakeHistory() self.uploader = FakeUploader() @@ -281,16 +268,14 @@ class FakeClient(Client): MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT -class WebMixin(testutil.TimezoneMixin): +class WebMixin(object): def setUp(self): - self.setTimezone('UTC-13:00') self.s = FakeClient() self.s.startService() self.staticdir = self.mktemp() self.clock = Clock() - self.fakeTime = 86460 # 1d 0h 1m 0s self.ws = webish.WebishServer(self.s, "0", staticdir=self.staticdir, - clock=self.clock, now_fn=lambda:self.fakeTime) + clock=self.clock) self.ws.setServiceParent(self.s) self.webish_port = self.ws.getPortnum() self.webish_url = self.ws.getURL() @@ -616,6 +601,7 @@ class WebMixin(testutil.TimezoneMixin): self.fail("%s was supposed to Error(302), not get '%s'" % (which, res)) + class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixin, unittest.TestCase): def test_create(self): pass @@ -633,11 +619,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi res_u = res.decode('utf-8') self.failUnlessIn(u'fake_nickname \u263A', res_u) self.failUnlessIn(u'
other_nickname \u263B
', res_u) - self.failUnlessIn(u'Connected to 1\n of 2 known storage servers', res_u) - self.failUnlessIn(u'
Connected
\n 1d\u00A00h\u00A00m\u00A050s', res_u) - self.failUnlessIn(u'
Disconnected
\n 1d\u00A00h\u00A00m\u00A035s', res_u) - self.failUnlessIn(u'1d\u00A00h\u00A00m\u00A030s', res_u) - self.failUnlessIn(u'1d\u00A00h\u00A00m\u00A025s', res_u) self.failUnlessIn(u'\u00A9 Tahoe-LAFS Software Foundation', res_u) self.failUnlessIn('

Available

', res) self.failUnlessIn('123.5kB', res) diff --git a/src/allmydata/util/time_format.py b/src/allmydata/util/time_format.py index a6fe3ec72..c481adc37 100644 --- a/src/allmydata/util/time_format.py +++ b/src/allmydata/util/time_format.py @@ -66,27 +66,3 @@ def parse_date(s): # day return int(iso_utc_time_to_seconds(s + "T00:00:00")) -def format_delta(time_1, time_2): - if time_1 is None: - return "N/A" - if time_1 > time_2: - return '-' - delta = int(time_2 - time_1) - seconds = delta % 60 - delta -= seconds - minutes = (delta / 60) % 60 - delta -= minutes * 60 - hours = delta / (60*60) % 24 - delta -= hours * 24 - days = delta / (24*60*60) - if not days: - if not hours: - if not minutes: - return "%ss" % (seconds) - else: - return "%sm %ss" % (minutes, seconds) - else: - return "%sh %sm %ss" % (hours, minutes, seconds) - else: - return "%sd %sh %sm %ss" % (days, hours, minutes, seconds) - diff --git a/src/allmydata/web/common.py b/src/allmydata/web/common.py index 5cfe84d9a..b8e6d5b44 100644 --- a/src/allmydata/web/common.py +++ b/src/allmydata/web/common.py @@ -15,7 +15,7 @@ from allmydata.interfaces import ExistingChildError, NoSuchChildError, \ MustBeReadonlyError, MustNotBeUnknownRWError, SDMF_VERSION, MDMF_VERSION from allmydata.mutable.common import UnrecoverableFileError from allmydata.util import abbreviate -from allmydata.util.time_format import format_time, format_delta +from allmydata.util.time_format import format_time from allmydata.util.encodingutil import to_str, quote_output @@ -213,15 +213,9 @@ def text_plain(text, ctx): def spaces_to_nbsp(text): return unicode(text).replace(u' ', u'\u00A0') -def render_time_delta(time_1, time_2): - return spaces_to_nbsp(format_delta(time_1, time_2)) - def render_time(t): return spaces_to_nbsp(format_time(time.localtime(t))) -def render_time_attr(t): - return format_time(time.localtime(t)) - class WebError(Exception): def __init__(self, text, code=http.BAD_REQUEST): self.text = text diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index d8d789cf3..c46a6dd5f 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -14,7 +14,7 @@ from allmydata.interfaces import IFileNode from allmydata.web import filenode, directory, unlinked, status, operations from allmydata.web import storage from allmydata.web.common import abbreviate_size, getxmlfile, WebError, \ - get_arg, RenderMixin, get_format, get_mutable_type, render_time_delta, render_time, render_time_attr + get_arg, RenderMixin, get_format, get_mutable_type, render_time class URIHandler(RenderMixin, rend.Page): @@ -138,13 +138,12 @@ class Root(rend.Page): "no": "Disconnected", } - def __init__(self, client, clock=None, now_fn=None): + def __init__(self, client, clock=None): rend.Page.__init__(self, client) self.client = client # If set, clock is a twisted.internet.task.Clock that the tests # use to test ophandle expiration. self.child_operations = operations.OphandleTable(clock) - self.now_fn = now_fn try: s = client.getServiceNamed("storage") except KeyError: @@ -283,7 +282,7 @@ class Root(rend.Page): ctx.fillSlots("peerid", server.get_longname()) ctx.fillSlots("nickname", server.get_nickname()) rhost = server.get_remote_host() - if server.is_connected(): + if rhost: if nodeid == self.client.nodeid: rhost_s = "(loopback)" elif isinstance(rhost, address.IPv4Address): @@ -291,37 +290,29 @@ class Root(rend.Page): else: rhost_s = str(rhost) addr = rhost_s - service_connection_status = "yes" - last_connect_time = server.get_last_connect_time() - service_connection_status_rel_time = render_time_delta(last_connect_time, self.now_fn()) - service_connection_status_abs_time = render_time_attr(last_connect_time) + connected = "yes" + since = server.get_last_connect_time() else: addr = "N/A" - service_connection_status = "no" - last_loss_time = server.get_last_loss_time() - service_connection_status_rel_time = render_time_delta(last_loss_time, self.now_fn()) - service_connection_status_abs_time = render_time_attr(last_loss_time) - - last_received_data_time = server.get_last_received_data_time() - last_received_data_rel_time = render_time_delta(last_received_data_time, self.now_fn()) - last_received_data_abs_time = render_time_attr(last_received_data_time) - + connected = "no" + since = server.get_last_loss_time() + announced = server.get_announcement_time() announcement = server.get_announcement() version = announcement["my-version"] + service_name = announcement["service-name"] available_space = server.get_available_space() if available_space is None: available_space = "N/A" else: available_space = abbreviate_size(available_space) ctx.fillSlots("address", addr) - ctx.fillSlots("service_connection_status", service_connection_status) - ctx.fillSlots("service_connection_status_alt", self._connectedalts[service_connection_status]) + ctx.fillSlots("connected", connected) + ctx.fillSlots("connected_alt", self._connectedalts[connected]) ctx.fillSlots("connected-bool", bool(rhost)) - ctx.fillSlots("service_connection_status_abs_time", service_connection_status_abs_time) - ctx.fillSlots("service_connection_status_rel_time", service_connection_status_rel_time) - ctx.fillSlots("last_received_data_abs_time", last_received_data_abs_time) - ctx.fillSlots("last_received_data_rel_time", last_received_data_rel_time) + ctx.fillSlots("since", render_time(since)) + ctx.fillSlots("announced", render_time(announced)) ctx.fillSlots("version", version) + ctx.fillSlots("service_name", service_name) ctx.fillSlots("available_space", available_space) return ctx.tag diff --git a/src/allmydata/web/static/css/new-tahoe.css b/src/allmydata/web/static/css/new-tahoe.css index 8ab7f47d6..175c3e337 100644 --- a/src/allmydata/web/static/css/new-tahoe.css +++ b/src/allmydata/web/static/css/new-tahoe.css @@ -77,12 +77,3 @@ body { float: left; margin: 5px; } - -.nickname-and-peerid .timestamp { - float: right; -} - -a.timestamp { - color: inherit; - text-decoration:none; -} diff --git a/src/allmydata/web/welcome.xhtml b/src/allmydata/web/welcome.xhtml index ca2387672..3e23fc736 100644 --- a/src/allmydata/web/welcome.xhtml +++ b/src/allmydata/web/welcome.xhtml @@ -1,5 +1,4 @@ - + @@ -170,24 +169,27 @@

Nickname

Address

-

Last RX

+

Service

+

Since

+

Announced

Version

Available

-
img/connected-.png
-
+
img/connected-.png
- + + + - You are not presently connected to any peers + You are not presently connected to any peers diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py index b26b34da3..e2029feec 100644 --- a/src/allmydata/webish.py +++ b/src/allmydata/webish.py @@ -129,14 +129,14 @@ class WebishServer(service.MultiService): name = "webish" def __init__(self, client, webport, nodeurl_path=None, staticdir=None, - clock=None, now_fn=time.time): + clock=None): service.MultiService.__init__(self) # the 'data' argument to all render() methods default to the Client # the 'clock' argument to root.Root is, if set, a # twisted.internet.task.Clock that is provided by the unit tests # so that they can test features that involve the passage of # time in a deterministic manner. - self.root = root.Root(client, clock, now_fn) + self.root = root.Root(client, clock) self.buildServer(webport, nodeurl_path, staticdir) if self.root.child_operations: self.site.remember(self.root.child_operations, IOpHandleTable)