From 822ed78b132a39bd64156c844833a4a7fb6cbbc8 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Wed, 11 Jan 2017 02:25:42 +0000 Subject: [PATCH 01/23] Add rough sketch of json welcome page --- src/allmydata/web/root.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 17041243c..806060558 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -1,7 +1,8 @@ import time, os +import simplejson from twisted.web import http -from nevow import rend, url, tags as T +from nevow import rend, inevow, url, tags as T from nevow.inevow import IRequest from nevow.static import File as nevow_File # TODO: merge with static.File? from nevow.util import resource_filename @@ -182,9 +183,28 @@ class Root(rend.Page): def render_my_nodeid(self, ctx, data): tubid_s = "TubID: "+self.client.get_long_tubid() return T.td(title=tubid_s)[self.client.get_long_nodeid()] + def data_my_nickname(self, ctx, data): return self.client.nickname + def renderHTTP(self, ctx): + req = inevow.IRequest(ctx) + t = get_arg(req, "t") + if t == "json": + return self.json_welcome(ctx) + return rend.Page.renderHTTP(self, ctx) + + def json_welcome(self, ctx): + connected_count = self.data_connected_introducers( ctx, None ) + inevow.IRequest(ctx).setHeader("content-type", "text/plain") + data = { + "introducers": { + "connected_count": connected_count, + }, + "servers": {}, + } + return simplejson.dumps(data, indent=1) + "\n" + def render_magic_folder(self, ctx, data): if self.client._magic_folder is None: return T.p() From 32110bfc68da892b00cca5e7f44527747e49cbe1 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Thu, 12 Jan 2017 23:14:04 +0000 Subject: [PATCH 02/23] Add storage and introducer summaries to json welcome output --- src/allmydata/web/root.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 806060558..0708bab21 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -195,13 +195,20 @@ class Root(rend.Page): return rend.Page.renderHTTP(self, ctx) def json_welcome(self, ctx): - connected_count = self.data_connected_introducers( ctx, None ) inevow.IRequest(ctx).setHeader("content-type", "text/plain") + intro_summaries = [s.summary for s in self.client.introducer_connection_statuses()] + sb = self.client.get_storage_broker() + storage_summaries = [] + for s in sb.get_connected_servers(): + status = s.get_connection_status() + storage_summaries.append(status.summary) data = { "introducers": { - "connected_count": connected_count, + "statuses": intro_summaries, + }, + "servers": { + "statuses": storage_summaries, }, - "servers": {}, } return simplejson.dumps(data, indent=1) + "\n" From b2d4374976b09ac009f2cabaad7a8cb15046e945 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Sat, 14 Jan 2017 01:14:58 +0000 Subject: [PATCH 03/23] Add more server info to welcome json page --- src/allmydata/web/root.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 0708bab21..2c1b28e2c 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -198,17 +198,20 @@ class Root(rend.Page): inevow.IRequest(ctx).setHeader("content-type", "text/plain") intro_summaries = [s.summary for s in self.client.introducer_connection_statuses()] sb = self.client.get_storage_broker() - storage_summaries = [] - for s in sb.get_connected_servers(): - status = s.get_connection_status() - storage_summaries.append(status.summary) + servers = {} + for server in sb.get_known_servers(): + server_id = server.get_serverid() + servers[server_id] = {} + servers[server_id]["connection_status"] = server.get_connection_status().summary + servers[server_id]["available_space"] = server.get_available_space() + servers[server_id]["nickname"] = server.get_nickname() + servers[server_id]["version"] = server.get_version()["application-version"] + servers[server_id]["last_received_data"] = server.rref.getDataLastReceivedAt() data = { "introducers": { "statuses": intro_summaries, }, - "servers": { - "statuses": storage_summaries, - }, + "servers": servers } return simplejson.dumps(data, indent=1) + "\n" From 180975b0afd0affffe3563247ac9aa8514e37f93 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Sat, 14 Jan 2017 17:38:47 +0000 Subject: [PATCH 04/23] Make sure version is not None --- src/allmydata/web/root.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 2c1b28e2c..d24cafe70 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -205,7 +205,10 @@ class Root(rend.Page): servers[server_id]["connection_status"] = server.get_connection_status().summary servers[server_id]["available_space"] = server.get_available_space() servers[server_id]["nickname"] = server.get_nickname() - servers[server_id]["version"] = server.get_version()["application-version"] + if server.get_version() is not None: + servers[server_id]["version"] = server.get_version()["application-version"] + else: + servers[server_id]["version"] = "" servers[server_id]["last_received_data"] = server.rref.getDataLastReceivedAt() data = { "introducers": { From ad66caeaff392ef71e86ac7af894e481ed1a4c67 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Sat, 14 Jan 2017 19:11:01 +0000 Subject: [PATCH 05/23] Make sure server.rref is not None --- src/allmydata/web/root.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index d24cafe70..8fdb4473a 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -209,8 +209,11 @@ class Root(rend.Page): servers[server_id]["version"] = server.get_version()["application-version"] else: servers[server_id]["version"] = "" - servers[server_id]["last_received_data"] = server.rref.getDataLastReceivedAt() - data = { + if server.rref is not None: + servers[server_id]["last_received_data"] = server.rref.getDataLastReceivedAt() + else: + servers[server_id]["last_received_data"] = "" + data = { "introducers": { "statuses": intro_summaries, }, From 876a7b6602c2348c785b7e83cc2f54844b9fa626 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Thu, 19 Jan 2017 22:32:11 +0000 Subject: [PATCH 06/23] Fix indention --- src/allmydata/web/root.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 8fdb4473a..908300da9 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -214,11 +214,11 @@ class Root(rend.Page): else: servers[server_id]["last_received_data"] = "" data = { - "introducers": { - "statuses": intro_summaries, - }, - "servers": servers - } + "introducers": { + "statuses": intro_summaries, + }, + "servers": servers + } return simplejson.dumps(data, indent=1) + "\n" def render_magic_folder(self, ctx, data): From ecb6651926d0e329c9d6bdcbd646df5523e09a84 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Fri, 20 Jan 2017 08:49:35 +0000 Subject: [PATCH 07/23] Add basic unit test for json welcome page --- src/allmydata/test/web/test_web.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 1961f99ed..bff0dc001 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -184,10 +184,15 @@ class FakeDisplayableServer(StubServer): self.last_loss_time = last_loss_time self.last_rx_time = last_rx_time self.last_connect_time = last_connect_time + self.rref = None def on_status_changed(self, cb): # TODO: try to remove me cb(self) def is_connected(self): # TODO: remove me return self.connected + def get_version(self): + return { + "application-version": "" + } def get_permutation_seed(self): return "" def get_announcement(self): @@ -734,6 +739,15 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi def test_create(self): pass + def test_welcome_json(self): + d = self.GET("/?t=json") + def _check(res): + decoded = simplejson.loads(res) + self.failUnless("introducers" in decoded.keys()) + self.failUnless("servers" in decoded.keys()) + d.addCallback(_check) + return d + def test_welcome(self): d = self.GET("/") def _check(res): From a87822e66be2b7bbc21c98b9794a8a7bdcc1d468 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Fri, 20 Jan 2017 08:57:22 +0000 Subject: [PATCH 08/23] Add note about json welcome page to api docs --- docs/frontends/webapi.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index 7fbb44168..c17d5fabc 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1850,6 +1850,27 @@ This is the "Welcome Page", and contains a few distinct sections:: Grid status: introducer information, helper information, connected storage servers. +``GET /?t=json`` (the json welcome page) + +This is the "json Welcome Page", and contains connectivity status +of the introducer(s) and storage server(s), here's an example:: + + { + "introducers": { + "statuses": [] + }, + "servers": { + "other_nodeid": { + "available_space": 123456, + "nickname": "other_nickname \u263b", + "version": "", + "connection_status": "summary", + "last_received_data": "" + } + } + } + + ``GET /status/`` This page lists all active uploads and downloads, and contains a short list From 16e56611f312ae3d9a7113f679691dae1381fa16 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Thu, 23 Feb 2017 00:25:52 +0000 Subject: [PATCH 09/23] web: set json welcome content type to json utf8 --- src/allmydata/web/root.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 908300da9..b8888d79b 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -195,7 +195,7 @@ class Root(rend.Page): return rend.Page.renderHTTP(self, ctx) def json_welcome(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "text/plain") + inevow.IRequest(ctx).setHeader("content-type", "application/json; charset=utf-8") intro_summaries = [s.summary for s in self.client.introducer_connection_statuses()] sb = self.client.get_storage_broker() servers = {} From 923363c0d6486f60ea2b21ba35abcf3be6bf7b70 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Thu, 23 Feb 2017 00:47:48 +0000 Subject: [PATCH 10/23] web test: test for server key in json welcome --- src/allmydata/test/web/test_web.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index bff0dc001..78b9f3197 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -743,6 +743,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi d = self.GET("/?t=json") def _check(res): decoded = simplejson.loads(res) + self.failUnless("other_nodeid" in decoded['servers']) self.failUnless("introducers" in decoded.keys()) self.failUnless("servers" in decoded.keys()) d.addCallback(_check) From cd97f8519b277c4220c92eb6782e84feb9a6e15d Mon Sep 17 00:00:00 2001 From: David Stainton Date: Thu, 23 Feb 2017 00:50:53 +0000 Subject: [PATCH 11/23] web test: get_version should use a version string --- src/allmydata/test/web/test_web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 78b9f3197..599192574 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -191,7 +191,7 @@ class FakeDisplayableServer(StubServer): return self.connected def get_version(self): return { - "application-version": "" + "application-version": "1.0" } def get_permutation_seed(self): return "" From 2006e6ae41c6c439c7cac3bbeda036ae1c54e0a8 Mon Sep 17 00:00:00 2001 From: David Stainton Date: Thu, 23 Feb 2017 01:01:39 +0000 Subject: [PATCH 12/23] docs: add explaination of json welcome fields --- docs/frontends/webapi.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index c17d5fabc..5f4b4e7d4 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1862,15 +1862,29 @@ of the introducer(s) and storage server(s), here's an example:: "servers": { "other_nodeid": { "available_space": 123456, - "nickname": "other_nickname \u263b", - "version": "", + "nickname": "George \u263b", + "version": "1.0", "connection_status": "summary", - "last_received_data": "" + "last_received_data": 1487811257, } } } +The abos json ``introducers`` section includes a list of +introducer connectivity status messages. + +The above json ``servers`` section above is a map +where the key is a storage server node ID and the value +is a map of properties with the following usage for each key: + +1. available_space - the available space in bytes expressed as an integer +2. nickname - the storage server nickname +3. version - the storage server Tahoe-LAFS version +4. connection_status - connectivity status +5. last_received_data - the time when data was last received, + expressed in seconds since epoch + ``GET /status/`` This page lists all active uploads and downloads, and contains a short list From cc0e6ea6f9be841f4a3238f5e632f4cca34da04a Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 24 Jul 2017 15:32:42 -0400 Subject: [PATCH 13/23] typo fix --- docs/frontends/webapi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index 5f4b4e7d4..c13032366 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1871,7 +1871,7 @@ of the introducer(s) and storage server(s), here's an example:: } -The abos json ``introducers`` section includes a list of +The above json ``introducers`` section includes a list of introducer connectivity status messages. The above json ``servers`` section above is a map From 7b88cf376b4a81cab4411421e19e907f7a9e51e5 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 24 Jul 2017 15:33:11 -0400 Subject: [PATCH 14/23] remove duplicate "above" --- docs/frontends/webapi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index c13032366..5da1e8eec 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1874,7 +1874,7 @@ of the introducer(s) and storage server(s), here's an example:: The above json ``introducers`` section includes a list of introducer connectivity status messages. -The above json ``servers`` section above is a map +The above json ``servers`` section is a map where the key is a storage server node ID and the value is a map of properties with the following usage for each key: From 780a932122471920813a8a8352abc3556fc14198 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 24 Jul 2017 15:34:03 -0400 Subject: [PATCH 15/23] consistent markup --- docs/frontends/webapi.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index 5da1e8eec..95602bb57 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1878,11 +1878,11 @@ The above json ``servers`` section is a map where the key is a storage server node ID and the value is a map of properties with the following usage for each key: -1. available_space - the available space in bytes expressed as an integer -2. nickname - the storage server nickname -3. version - the storage server Tahoe-LAFS version -4. connection_status - connectivity status -5. last_received_data - the time when data was last received, +1. ``available_space`` - the available space in bytes expressed as an integer +2. ``nickname`` - the storage server nickname +3. ``version`` - the storage server Tahoe-LAFS version +4. ``connection_status`` - connectivity status +5. ``last_received_data`` - the time when data was last received, expressed in seconds since epoch ``GET /status/`` From e3e49632a5f9218e81bbc5df4c18fb66726a71ad Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 25 Jul 2017 08:11:45 -0400 Subject: [PATCH 16/23] use preferred spelling for new assertions --- src/allmydata/test/web/test_web.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 599192574..25fdc9fa3 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -743,9 +743,9 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi d = self.GET("/?t=json") def _check(res): decoded = simplejson.loads(res) - self.failUnless("other_nodeid" in decoded['servers']) - self.failUnless("introducers" in decoded.keys()) - self.failUnless("servers" in decoded.keys()) + self.assertIn("other_nodeid", decoded['servers']) + self.assertIn("introducers", decoded) + self.assertIn("servers", decoded) d.addCallback(_check) return d From e339130e635a64bf6c393d9725661f40d4be3ba1 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 25 Jul 2017 08:29:52 -0400 Subject: [PATCH 17/23] Just make an assertion against the full value --- src/allmydata/test/web/test_web.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 25fdc9fa3..c835bee95 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -740,12 +740,26 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi pass def test_welcome_json(self): + """ + There is a JSON version of the welcome page which can be selected with the + ``t`` query argument. + """ d = self.GET("/?t=json") def _check(res): decoded = simplejson.loads(res) - self.assertIn("other_nodeid", decoded['servers']) - self.assertIn("introducers", decoded) - self.assertIn("servers", decoded) + expected = { + 'introducers': {'statuses': []}, + 'servers': { + 'other_nodeid': { + 'available_space': 123456, + 'connection_status': 'summary', + 'last_received_data': u'', + 'nickname': u'other_nickname \u263b', + 'version': '1.0', + }, + }, + } + self.assertEqual(expected, decoded) d.addCallback(_check) return d From f518c2a5212b412c873a8d5919a70bd63cabfdd9 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 25 Jul 2017 11:35:54 -0400 Subject: [PATCH 18/23] Switch over to stdlib json --- src/allmydata/test/web/test_web.py | 2 +- src/allmydata/web/root.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index c835bee95..9f1ab3733 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -746,7 +746,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi """ d = self.GET("/?t=json") def _check(res): - decoded = simplejson.loads(res) + decoded = json.loads(res) expected = { 'introducers': {'statuses': []}, 'servers': { diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index b8888d79b..2b0a8ac63 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -1,5 +1,4 @@ -import time, os -import simplejson +import time, os, json from twisted.web import http from nevow import rend, inevow, url, tags as T @@ -219,7 +218,7 @@ class Root(rend.Page): }, "servers": servers } - return simplejson.dumps(data, indent=1) + "\n" + return json.dumps(data, indent=1) + "\n" def render_magic_folder(self, ctx, data): if self.client._magic_folder is None: From 754304e4d5ebe6a4724d4485897101d650401ffe Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 25 Jul 2017 11:36:06 -0400 Subject: [PATCH 19/23] Switch Root over to MultiFormatPage --- src/allmydata/web/root.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 2b0a8ac63..17d7a3820 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -1,7 +1,7 @@ import time, os, json from twisted.web import http -from nevow import rend, inevow, url, tags as T +from nevow import rend, url, tags as T from nevow.inevow import IRequest from nevow.static import File as nevow_File # TODO: merge with static.File? from nevow.util import resource_filename @@ -12,8 +12,19 @@ from allmydata.util import log from allmydata.interfaces import IFileNode from allmydata.web import filenode, directory, unlinked, status, operations from allmydata.web import storage, magic_folder -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 +from allmydata.web.common import ( + abbreviate_size, + getxmlfile, + WebError, + get_arg, + MultiFormatPage, + RenderMixin, + get_format, + get_mutable_type, + render_time_delta, + render_time, + render_time_attr, +) class URIHandler(RenderMixin, rend.Page): @@ -126,7 +137,7 @@ class IncidentReporter(RenderMixin, rend.Page): SPACE = u"\u00A0"*2 -class Root(rend.Page): +class Root(MultiFormatPage): addSlash = True docFactory = getxmlfile("welcome.xhtml") @@ -186,15 +197,8 @@ class Root(rend.Page): def data_my_nickname(self, ctx, data): return self.client.nickname - def renderHTTP(self, ctx): - req = inevow.IRequest(ctx) - t = get_arg(req, "t") - if t == "json": - return self.json_welcome(ctx) - return rend.Page.renderHTTP(self, ctx) - - def json_welcome(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "application/json; charset=utf-8") + def render_JSON(self, req): + req.setHeader("content-type", "application/json; charset=utf-8") intro_summaries = [s.summary for s in self.client.introducer_connection_statuses()] sb = self.client.get_storage_broker() servers = {} From 7027547ecc2d9c79653f61dd2e431dd9a64977da Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 25 Jul 2017 11:56:42 -0400 Subject: [PATCH 20/23] fix buggy test; restructure data scared about duplicate nodeids in the data getting lost, switch to a list --- src/allmydata/test/web/test_web.py | 34 ++++++++++++----- src/allmydata/web/root.py | 60 +++++++++++++++++++----------- 2 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 9f1ab3733..c6717e49b 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -262,7 +262,7 @@ class FakeClient(Client): 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, + serverid="disconnected_nodeid", nickname=u"disconnected_nickname \u263B", connected = False, last_connect_time = None, last_loss_time = 25, last_rx_time = 35)) self.introducer_client = None self.history = FakeHistory() @@ -739,6 +739,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi def test_create(self): pass + maxDiff = None def test_welcome_json(self): """ There is a JSON version of the welcome page which can be selected with the @@ -748,16 +749,29 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi def _check(res): decoded = json.loads(res) expected = { - 'introducers': {'statuses': []}, - 'servers': { - 'other_nodeid': { - 'available_space': 123456, - 'connection_status': 'summary', - 'last_received_data': u'', - 'nickname': u'other_nickname \u263b', - 'version': '1.0', - }, + u'introducers': { + u'statuses': [], }, + u'servers': sorted([ + {u"nodeid": u'other_nodeid', + u"description": { + u'available_space': 123456, + u'connection_status': u'summary', + u'last_received_data': None, + u'nickname': u'other_nickname \u263b', + u'version': u'1.0', + }, + }, + {u"nodeid": u'disconnected_nodeid', + u"description": { + u'available_space': 123456, + u'connection_status': u'summary', + u'last_received_data': None, + u'nickname': u'disconnected_nickname \u263b', + u'version': u'1.0', + }, + }, + ]), } self.assertEqual(expected, decoded) d.addCallback(_check) diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index 17d7a3820..f2a23c1e1 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -197,32 +197,50 @@ class Root(MultiFormatPage): def data_my_nickname(self, ctx, data): return self.client.nickname + def render_JSON(self, req): req.setHeader("content-type", "application/json; charset=utf-8") intro_summaries = [s.summary for s in self.client.introducer_connection_statuses()] sb = self.client.get_storage_broker() - servers = {} - for server in sb.get_known_servers(): - server_id = server.get_serverid() - servers[server_id] = {} - servers[server_id]["connection_status"] = server.get_connection_status().summary - servers[server_id]["available_space"] = server.get_available_space() - servers[server_id]["nickname"] = server.get_nickname() - if server.get_version() is not None: - servers[server_id]["version"] = server.get_version()["application-version"] - else: - servers[server_id]["version"] = "" - if server.rref is not None: - servers[server_id]["last_received_data"] = server.rref.getDataLastReceivedAt() - else: - servers[server_id]["last_received_data"] = "" - data = { - "introducers": { - "statuses": intro_summaries, - }, - "servers": servers + servers = self._describe_known_servers(sb) + result = { + "introducers": { + "statuses": intro_summaries, + }, + "servers": servers + } + return json.dumps(result, indent=1) + "\n" + + + def _describe_known_servers(self, broker): + return sorted(list( + { + u"nodeid": server.get_serverid(), + u"description": self._describe_server(server), } - return json.dumps(data, indent=1) + "\n" + for server + in broker.get_known_servers() + )) + + + def _describe_server(self, server): + description = { + u"connection_status": server.get_connection_status().summary, + u"available_space": server.get_available_space(), + u"nickname": server.get_nickname(), + u"version": None, + u"last_received_data": None, + } + version = server.get_version() + if version is not None: + description[u"version"] = version["application-version"] + + rref = server.rref + if rref is not None: + description[u"last_received_data"] = rref.getDataLastReceivedAt() + + return description + def render_magic_folder(self, ctx, data): if self.client._magic_folder is None: From e0ee726d22d3d4c4c0c20923a03ed4af4884e6eb Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 27 Jul 2017 15:28:29 -0400 Subject: [PATCH 21/23] Update to the format the code now actually produces --- docs/frontends/webapi.rst | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index 95602bb57..1769a05e1 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1859,15 +1859,16 @@ of the introducer(s) and storage server(s), here's an example:: "introducers": { "statuses": [] }, - "servers": { - "other_nodeid": { - "available_space": 123456, - "nickname": "George \u263b", - "version": "1.0", - "connection_status": "summary", - "last_received_data": 1487811257, - } - } + "servers": [{ + "nodeid": "other_nodeid", + "description": { + "available_space": 123456, + "nickname": "George \u263b", + "version": "1.0", + "connection_status": "summary", + "last_received_data": 1487811257 + } + }] } From 1f16a173f0c8d8eea52bd84ab175c3e7ce402c4c Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 27 Jul 2017 15:32:47 -0400 Subject: [PATCH 22/23] flatten the result structure flat is better than nested --- docs/frontends/webapi.rst | 28 +++++++++++++--------------- src/allmydata/test/web/test_web.py | 24 ++++++++++-------------- src/allmydata/web/root.py | 6 ++---- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst index 1769a05e1..2ee348080 100644 --- a/docs/frontends/webapi.rst +++ b/docs/frontends/webapi.rst @@ -1861,13 +1861,11 @@ of the introducer(s) and storage server(s), here's an example:: }, "servers": [{ "nodeid": "other_nodeid", - "description": { - "available_space": 123456, - "nickname": "George \u263b", - "version": "1.0", - "connection_status": "summary", - "last_received_data": 1487811257 - } + "available_space": 123456, + "nickname": "George \u263b", + "version": "1.0", + "connection_status": "summary", + "last_received_data": 1487811257 }] } @@ -1875,15 +1873,15 @@ of the introducer(s) and storage server(s), here's an example:: The above json ``introducers`` section includes a list of introducer connectivity status messages. -The above json ``servers`` section is a map -where the key is a storage server node ID and the value -is a map of properties with the following usage for each key: +The above json ``servers`` section is an array with map elements. Each map +has the following properties: -1. ``available_space`` - the available space in bytes expressed as an integer -2. ``nickname`` - the storage server nickname -3. ``version`` - the storage server Tahoe-LAFS version -4. ``connection_status`` - connectivity status -5. ``last_received_data`` - the time when data was last received, +1. ``nodeid`` - an identifier derived from the node's public key +2. ``available_space`` - the available space in bytes expressed as an integer +3. ``nickname`` - the storage server nickname +4. ``version`` - the storage server Tahoe-LAFS version +5. ``connection_status`` - connectivity status +6. ``last_received_data`` - the time when data was last received, expressed in seconds since epoch ``GET /status/`` diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index c6717e49b..9ceb818bb 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -754,22 +754,18 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi }, u'servers': sorted([ {u"nodeid": u'other_nodeid', - u"description": { - u'available_space': 123456, - u'connection_status': u'summary', - u'last_received_data': None, - u'nickname': u'other_nickname \u263b', - u'version': u'1.0', - }, + u'available_space': 123456, + u'connection_status': u'summary', + u'last_received_data': None, + u'nickname': u'other_nickname \u263b', + u'version': u'1.0', }, {u"nodeid": u'disconnected_nodeid', - u"description": { - u'available_space': 123456, - u'connection_status': u'summary', - u'last_received_data': None, - u'nickname': u'disconnected_nickname \u263b', - u'version': u'1.0', - }, + u'available_space': 123456, + u'connection_status': u'summary', + u'last_received_data': None, + u'nickname': u'disconnected_nickname \u263b', + u'version': u'1.0', }, ]), } diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index f2a23c1e1..f4f38d6e1 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -214,10 +214,7 @@ class Root(MultiFormatPage): def _describe_known_servers(self, broker): return sorted(list( - { - u"nodeid": server.get_serverid(), - u"description": self._describe_server(server), - } + self._describe_server(server) for server in broker.get_known_servers() )) @@ -225,6 +222,7 @@ class Root(MultiFormatPage): def _describe_server(self, server): description = { + u"nodeid": server.get_serverid(), u"connection_status": server.get_connection_status().summary, u"available_space": server.get_available_space(), u"nickname": server.get_nickname(), From e32f581d071776c16b2f4f2cdac3d105280c0cb0 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 27 Jul 2017 16:31:41 -0400 Subject: [PATCH 23/23] Get rid of the reliance on rref It provides information more correctly obtained from get_connection_status(). --- src/allmydata/test/web/test_web.py | 6 +++--- src/allmydata/web/root.py | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 9ceb818bb..fc7155d39 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -184,7 +184,7 @@ class FakeDisplayableServer(StubServer): self.last_loss_time = last_loss_time self.last_rx_time = last_rx_time self.last_connect_time = last_connect_time - self.rref = None + def on_status_changed(self, cb): # TODO: try to remove me cb(self) def is_connected(self): # TODO: remove me @@ -756,14 +756,14 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi {u"nodeid": u'other_nodeid', u'available_space': 123456, u'connection_status': u'summary', - u'last_received_data': None, + u'last_received_data': 30, u'nickname': u'other_nickname \u263b', u'version': u'1.0', }, {u"nodeid": u'disconnected_nodeid', u'available_space': 123456, u'connection_status': u'summary', - u'last_received_data': None, + u'last_received_data': 35, u'nickname': u'disconnected_nickname \u263b', u'version': u'1.0', }, diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py index f4f38d6e1..dae013a26 100644 --- a/src/allmydata/web/root.py +++ b/src/allmydata/web/root.py @@ -221,22 +221,19 @@ class Root(MultiFormatPage): def _describe_server(self, server): + status = server.get_connection_status() description = { u"nodeid": server.get_serverid(), - u"connection_status": server.get_connection_status().summary, + u"connection_status": status.summary, u"available_space": server.get_available_space(), u"nickname": server.get_nickname(), u"version": None, - u"last_received_data": None, + u"last_received_data": status.last_received_time, } version = server.get_version() if version is not None: description[u"version"] = version["application-version"] - rref = server.rref - if rref is not None: - description[u"last_received_data"] = rref.getDataLastReceivedAt() - return description