Merge PR430: add JSON welcome page

closes ticket:2476
This commit is contained in:
Brian Warner 2017-07-27 15:59:03 -07:00
commit d018a07bf0
3 changed files with 130 additions and 5 deletions

View File

@ -1850,6 +1850,40 @@ 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": [{
"nodeid": "other_nodeid",
"available_space": 123456,
"nickname": "George \u263b",
"version": "1.0",
"connection_status": "summary",
"last_received_data": 1487811257
}]
}
The above json ``introducers`` section includes a list of
introducer connectivity status messages.
The above json ``servers`` section is an array with map elements. Each map
has the following properties:
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/``
This page lists all active uploads and downloads, and contains a short list

View File

@ -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
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": "1.0"
}
def get_permutation_seed(self):
return ""
def get_announcement(self):
@ -257,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()
@ -734,6 +739,40 @@ 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
``t`` query argument.
"""
d = self.GET("/?t=json")
def _check(res):
decoded = json.loads(res)
expected = {
u'introducers': {
u'statuses': [],
},
u'servers': sorted([
{u"nodeid": u'other_nodeid',
u'available_space': 123456,
u'connection_status': u'summary',
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': 35,
u'nickname': u'disconnected_nickname \u263b',
u'version': u'1.0',
},
]),
}
self.assertEqual(expected, decoded)
d.addCallback(_check)
return d
def test_welcome(self):
d = self.GET("/")
def _check(res):

View File

@ -1,4 +1,4 @@
import time, os
import time, os, json
from twisted.web import http
from nevow import rend, url, tags as T
@ -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")
@ -182,9 +193,50 @@ 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 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 = 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(
self._describe_server(server)
for server
in broker.get_known_servers()
))
def _describe_server(self, server):
status = server.get_connection_status()
description = {
u"nodeid": server.get_serverid(),
u"connection_status": status.summary,
u"available_space": server.get_available_space(),
u"nickname": server.get_nickname(),
u"version": None,
u"last_received_data": status.last_received_time,
}
version = server.get_version()
if version is not None:
description[u"version"] = version["application-version"]
return description
def render_magic_folder(self, ctx, data):
if self.client._magic_folder is None:
return T.p()