Use twisted.web.template in web/storage.py

Related to ticket:3247.  Nevow usage has been removed, and generated
page looks the same as its former self, but tests are failing because
test_storage.py assumes that we're using nevow.
This commit is contained in:
Sajith Sasidharan 2020-02-04 06:46:08 -05:00
parent 34e85ac515
commit d142ccb159
2 changed files with 182 additions and 136 deletions

View File

@ -1,10 +1,10 @@
import time, json
from nevow import rend, tags as T
from twisted.python.filepath import FilePath
from twisted.web.template import tags as T, renderer, Element, renderElement, XMLFile
from allmydata.web.common import (
getxmlfile,
abbreviate_time,
MultiFormatPage,
MultiFormatResource
)
from allmydata.util.abbreviate import abbreviate_space
from allmydata.util import time_format, idlib
@ -16,91 +16,100 @@ def remove_prefix(s, prefix):
return s[len(prefix):]
class StorageStatus(MultiFormatPage):
docFactory = getxmlfile("storage_status.xhtml")
# the default 'data' argument is the StorageServer instance
class StorageStatusElement(Element):
loader = XMLFile(FilePath(__file__).sibling("storage_status.xhtml"))
def __init__(self, storage, nickname=""):
rend.Page.__init__(self, storage)
def __init__(self, storage, nickname):
super(StorageStatusElement, self).__init__()
self.storage = storage
self.nickname = nickname
self.nick = nickname
def render_JSON(self, req):
req.setHeader("content-type", "text/plain")
d = {"stats": self.storage.get_stats(),
"bucket-counter": self.storage.bucket_counter.get_state(),
"lease-checker": self.storage.lease_checker.get_state(),
"lease-checker-progress": self.storage.lease_checker.get_progress(),
}
return json.dumps(d, indent=1) + "\n"
@renderer
def nickname(self, req, tag):
return self.nick
def data_nickname(self, ctx, storage):
return self.nickname
def data_nodeid(self, ctx, storage):
@renderer
def nodeid(self, req, tag):
return idlib.nodeid_b2a(self.storage.my_nodeid)
def render_storage_running(self, ctx, storage):
if storage:
return ctx.tag
else:
return T.h1["No Storage Server Running"]
def get_stat(self, key):
return self.storage.get_stats().get(key)
def render_bool(self, ctx, data):
return {True: "Yes", False: "No"}[bool(data)]
def str(self, tag, val):
if val is None:
return tag("?")
return tag(str(val))
def render_abbrev_space(self, ctx, size):
if size is None:
return "?"
return abbreviate_space(size)
def abbr(self, tag, val):
if val is None:
return tag("?")
return tag(abbreviate_space(val))
def render_space(self, ctx, size):
if size is None:
return "?"
return "%d" % size
@renderer
def disk_total(self, req, tag):
return self.str(tag, self.get_stat("storage_server.disk_total"))
def data_stats(self, ctx, data):
# FYI: 'data' appears to be self, rather than the StorageServer
# object in self.original that gets passed to render_* methods. I
# still don't understand Nevow.
@renderer
def disk_total_abbrev(self, req, tag):
return self.abbr(tag, self.get_stat("storage_server.disk_total"))
# Nevow has nevow.accessors.DictionaryContainer: Any data= directive
# that appears in a context in which the current data is a dictionary
# will be looked up as keys in that dictionary. So if data_stats()
# returns a dictionary, then we can use something like this:
#
# <ul n:data="stats">
# <li>disk_total: <span n:render="abbrev" n:data="disk_total" /></li>
# </ul>
@renderer
def disk_used(self, req, tag):
return self.str(tag, self.get_stat("storage_server.disk_used"))
# to use get_stats()["storage_server.disk_total"] . However,
# DictionaryContainer does a raw d[] instead of d.get(), so any
# missing keys will cause an error, even if the renderer can tolerate
# None values. To overcome this, we either need a dict-like object
# that always returns None for unknown keys, or we must pre-populate
# our dict with those missing keys, or we should get rid of data_
# methods that return dicts (or find some way to override Nevow's
# handling of dictionaries).
@renderer
def disk_used_abbrev(self, req, tag):
return self.abbr(tag, self.get_stat("storage_server.disk_used"))
d = dict([ (remove_prefix(k, "storage_server."), v)
for k,v in self.storage.get_stats().items() ])
d.setdefault("disk_total", None)
d.setdefault("disk_used", None)
d.setdefault("disk_free_for_root", None)
d.setdefault("disk_free_for_nonroot", None)
d.setdefault("reserved_space", None)
d.setdefault("disk_avail", None)
return d
@renderer
def disk_free_for_root(self, req, tag):
return self.str(tag, self.get_stat("storage_server.disk_free_for_root"))
def data_last_complete_bucket_count(self, ctx, data):
@renderer
def disk_free_for_root_abbrev(self, req, tag):
return self.abbr(tag, self.get_stat("storage_server.disk_free_for_root"))
@renderer
def disk_free_for_nonroot(self, req, tag):
return self.str(tag, self.get_stat("storage_server.disk_free_for_nonroot"))
@renderer
def disk_free_for_nonroot_abbrev(self, req, tag):
return self.abbr(tag, self.get_stat("storage_server.disk_free_for_nonroot"))
@renderer
def reserved_space(self, req, tag):
return self.str(tag, self.get_stat("storage_server.reserved_space"))
@renderer
def reserved_space_abbrev(self, req, tag):
return self.abbr(tag, self.get_stat("storage_server.reserved_space"))
@renderer
def disk_avail(self, req, tag):
return self.str(tag, self.get_stat("storage_server.disk_avail"))
@renderer
def disk_avail_abbrev(self, req, tag):
return self.abbr(tag, self.get_stat("storage_server.disk_avail"))
@renderer
def accepting_immutable_shares(self, req, tag):
accepting = self.get_stat("storage_server.accepting_immutable_shares")
return {True: "Yes", False: "No"}[bool(accepting)]
@renderer
def last_complete_bucket_count(self, req, tag):
s = self.storage.bucket_counter.get_state()
count = s.get("last-complete-bucket-count")
if count is None:
return "Not computed yet"
return count
return str(count)
def render_count_crawler_status(self, ctx, storage):
@renderer
def count_crawler_status(self, req, tag):
p = self.storage.bucket_counter.get_progress()
return ctx.tag[self.format_crawler_progress(p)]
return self.format_crawler_progress(p)
def format_crawler_progress(self, p):
cycletime = p["estimated-time-per-cycle"]
@ -127,55 +136,51 @@ class StorageStatus(MultiFormatPage):
return ["Next crawl in %s" % abbreviate_time(soon),
cycletime_s]
def render_lease_expiration_enabled(self, ctx, data):
@renderer
def storage_running(self, req, tag):
if self.storage:
return tag
return tag("No Storage Server Running")
@renderer
def lease_expiration_enabled(self, req, tag):
lc = self.storage.lease_checker
if lc.expiration_enabled:
return ctx.tag["Enabled: expired leases will be removed"]
return tag("Enabled: expired leases will be removed")
else:
return ctx.tag["Disabled: scan-only mode, no leases will be removed"]
return tag("Disabled: scan-only mode, no leases will be removed")
def render_lease_expiration_mode(self, ctx, data):
@renderer
def lease_expiration_mode(self, req, tag):
lc = self.storage.lease_checker
if lc.mode == "age":
if lc.override_lease_duration is None:
ctx.tag["Leases will expire naturally, probably 31 days after "
"creation or renewal."]
tag("Leases will expire naturally, probably 31 days after "
"creation or renewal.")
else:
ctx.tag["Leases created or last renewed more than %s ago "
"will be considered expired."
% abbreviate_time(lc.override_lease_duration)]
tag("Leases created or last renewed more than %s ago "
"will be considered expired."
% abbreviate_time(lc.override_lease_duration))
else:
assert lc.mode == "cutoff-date"
localizedutcdate = time.strftime("%d-%b-%Y", time.gmtime(lc.cutoff_date))
isoutcdate = time_format.iso_utc_date(lc.cutoff_date)
ctx.tag["Leases created or last renewed before %s (%s) UTC "
"will be considered expired." % (isoutcdate, localizedutcdate, )]
tag("Leases created or last renewed before %s (%s) UTC "
"will be considered expired."
% (isoutcdate, localizedutcdate, ))
if len(lc.mode) > 2:
ctx.tag[" The following sharetypes will be expired: ",
" ".join(sorted(lc.sharetypes_to_expire)), "."]
return ctx.tag
tag(" The following sharetypes will be expired: ",
" ".join(sorted(lc.sharetypes_to_expire)), ".")
return tag
def format_recovered(self, sr, a):
def maybe(d):
if d is None:
return "?"
return "%d" % d
return "%s shares, %s buckets (%s mutable / %s immutable), %s (%s / %s)" % \
(maybe(sr["%s-shares" % a]),
maybe(sr["%s-buckets" % a]),
maybe(sr["%s-buckets-mutable" % a]),
maybe(sr["%s-buckets-immutable" % a]),
abbreviate_space(sr["%s-diskbytes" % a]),
abbreviate_space(sr["%s-diskbytes-mutable" % a]),
abbreviate_space(sr["%s-diskbytes-immutable" % a]),
)
def render_lease_current_cycle_progress(self, ctx, data):
@renderer
def lease_current_cycle_progress(self, req, tag):
lc = self.storage.lease_checker
p = lc.get_progress()
return ctx.tag[self.format_crawler_progress(p)]
return tag(self.format_crawler_progress(p))
def render_lease_current_cycle_results(self, ctx, data):
@renderer
def lease_current_cycle_results(self, req, tag):
lc = self.storage.lease_checker
p = lc.get_progress()
if not p["cycle-in-progress"]:
@ -229,10 +234,10 @@ class StorageStatus(MultiFormatPage):
T.ul[ [T.li[ ["SI %s shnum %d" % corrupt_share
for corrupt_share in so_far["corrupt-shares"] ]
]]])
return tag("Current cycle:", p)
return ctx.tag["Current cycle:", p]
def render_lease_last_cycle_results(self, ctx, data):
@renderer
def lease_last_cycle_results(self, req, tag):
lc = self.storage.lease_checker
h = lc.get_state()["history"]
if not h:
@ -240,15 +245,15 @@ class StorageStatus(MultiFormatPage):
last = h[max(h.keys())]
start, end = last["cycle-start-finish-times"]
ctx.tag["Last complete cycle (which took %s and finished %s ago)"
" recovered: " % (abbreviate_time(end-start),
abbreviate_time(time.time() - end)),
self.format_recovered(last["space-recovered"], "actual")
]
tag("Last complete cycle (which took %s and finished %s ago)"
" recovered: " % (abbreviate_time(end-start),
abbreviate_time(time.time() - end)),
self.format_recovered(last["space-recovered"], "actual"))
p = T.ul()
def add(*pieces):
p[T.li[pieces]]
p(T.li(pieces))
saw = self.format_recovered(last["space-recovered"], "examined")
add("and saw a total of ", saw)
@ -264,4 +269,37 @@ class StorageStatus(MultiFormatPage):
for corrupt_share in last["corrupt-shares"] ]
]]])
return ctx.tag[p]
return tag(p)
def format_recovered(self, sr, a):
def maybe(d):
if d is None:
return "?"
return "%d" % d
return "%s shares, %s buckets (%s mutable / %s immutable), %s (%s / %s)" % \
(maybe(sr["%s-shares" % a]),
maybe(sr["%s-buckets" % a]),
maybe(sr["%s-buckets-mutable" % a]),
maybe(sr["%s-buckets-immutable" % a]),
abbreviate_space(sr["%s-diskbytes" % a]),
abbreviate_space(sr["%s-diskbytes-mutable" % a]),
abbreviate_space(sr["%s-diskbytes-immutable" % a]),
)
class StorageStatus(MultiFormatResource):
def __init__(self, storage, nickname=""):
super(StorageStatus, self).__init__()
self.storage = storage
self.nickname = nickname
def render_HTML(self, req):
return renderElement(req, StorageStatusElement(self.storage, self.nickname))
def render_JSON(self, req):
req.setHeader("content-type", "text/plain")
d = {"stats": self.storage.get_stats(),
"bucket-counter": self.storage.bucket_counter.get_state(),
"lease-checker": self.storage.lease_checker.get_state(),
"lease-checker-progress": self.storage.lease_checker.get_progress(),
}
return json.dumps(d, indent=1) + "\n"

View File

@ -1,4 +1,4 @@
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
<html xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
<head>
<title>Tahoe-LAFS - Storage Server Status</title>
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
@ -7,19 +7,19 @@
</head>
<body>
<div n:render="storage_running">
<div t:render="storage_running">
<h1>Storage Server Status</h1>
<table n:data="stats">
<table class="storage_status">
<tr><td>Total disk space:</td>
<td><span n:render="abbrev_space" n:data="disk_total" /></td>
<td>(<span n:render="space" n:data="disk_total" />)</td>
<td><span t:render="disk_total_abbrev" /></td>
<td>(<span t:render="disk_total" />)</td>
<td />
</tr>
<tr><td>Disk space used:</td>
<td>- <span n:render="abbrev_space" n:data="disk_used" /></td>
<td>(<span n:render="space" n:data="disk_used" />)</td>
<td>- <span t:render="disk_used_abbrev" /></td>
<td>(<span t:render="disk_used" />)</td>
<td />
</tr>
<tr><td />
@ -28,18 +28,18 @@
<td />
</tr>
<tr><td>Disk space free (root):</td>
<td><span n:render="abbrev_space" n:data="disk_free_for_root"/></td>
<td>(<span n:render="space" n:data="disk_free_for_root"/>)</td>
<td><span t:render="disk_free_for_root_abbrev"/></td>
<td>(<span t:render="disk_free_for_root"/>)</td>
<td>[see 1]</td>
</tr>
<tr><td>Disk space free (non-root):</td>
<td><span n:render="abbrev_space" n:data="disk_free_for_nonroot" /></td>
<td>(<span n:render="space" n:data="disk_free_for_nonroot" />)</td>
<td><span t:render="disk_free_for_nonroot_abbrev" /></td>
<td>(<span t:render="disk_free_for_nonroot" />)</td>
<td>[see 2]</td>
</tr>
<tr><td>Reserved space:</td>
<td>- <span n:render="abbrev_space" n:data="reserved_space" /></td>
<td>(<span n:render="space" n:data="reserved_space" />)</td>
<td>- <span t:render="reserved_space_abbrev" /></td>
<td>(<span t:render="reserved_space" />)</td>
<td />
</tr>
<tr><td />
@ -48,23 +48,31 @@
<td />
</tr>
<tr><td>Space Available to Tahoe:</td>
<td><span n:render="abbrev_space" n:data="disk_avail" /></td>
<td>(<span n:render="space" n:data="disk_avail" />)</td>
<td><span t:render="disk_avail_abbrev" /></td>
<td>(<span t:render="disk_avail" />)</td>
<td />
</tr>
</table>
<ul>
<li>Server Nickname: <span class="nickname mine" n:render="data" n:data="nickname" /></li>
<li>Server Nodeid: <span class="nodeid mine data-chars" n:render="string" n:data="nodeid" /></li>
<li n:data="stats">Accepting new shares:
<span n:render="bool" n:data="accepting_immutable_shares" /></li>
<li>Server Nickname:
<span class="nickname mine">
<span t:render="nickname" />
</span>
</li>
<li>Server Nodeid:
<span class="nodeid mine data-chars">
<span t:render="nodeid" />
</span>
</li>
<li t:data="stats">Accepting new shares:
<span t:render="accepting_immutable_shares" /></li>
<li>Total buckets:
<span n:render="string" n:data="last_complete_bucket_count" />
<span t:render="last_complete_bucket_count" />
(the number of files and directories for which this server is holding
a share)
<ul>
<li n:render="count_crawler_status" />
<li><span t:render="count_crawler_status" /></li>
</ul>
</li>
</ul>
@ -72,11 +80,11 @@
<h2>Lease Expiration Crawler</h2>
<ul>
<li>Expiration <span n:render="lease_expiration_enabled" /></li>
<li n:render="lease_expiration_mode" />
<li n:render="lease_current_cycle_progress" />
<li n:render="lease_current_cycle_results" />
<li n:render="lease_last_cycle_results" />
<li>Expiration <span t:render="lease_expiration_enabled" /></li>
<li t:render="lease_expiration_mode" />
<li t:render="lease_current_cycle_progress" />
<li t:render="lease_current_cycle_results" />
<li t:render="lease_last_cycle_results" />
</ul>
<hr />