2009-02-20 21:29:26 +00:00
|
|
|
|
2017-01-19 22:39:53 +00:00
|
|
|
import time, json
|
2020-02-04 11:46:08 +00:00
|
|
|
from twisted.python.filepath import FilePath
|
2020-02-05 03:59:45 +00:00
|
|
|
from twisted.web.template import tags as T, \
|
2020-02-05 04:16:48 +00:00
|
|
|
renderer, Element, renderElement, XMLFile, flattenString
|
2017-07-25 15:16:38 +00:00
|
|
|
from allmydata.web.common import (
|
|
|
|
abbreviate_time,
|
2020-02-04 11:46:08 +00:00
|
|
|
MultiFormatResource
|
2017-07-25 15:16:38 +00:00
|
|
|
)
|
2009-02-21 04:04:08 +00:00
|
|
|
from allmydata.util.abbreviate import abbreviate_space
|
2012-03-13 02:57:36 +00:00
|
|
|
from allmydata.util import time_format, idlib
|
2009-02-20 21:29:26 +00:00
|
|
|
|
2019-05-17 17:27:30 +00:00
|
|
|
|
2009-02-20 21:29:26 +00:00
|
|
|
def remove_prefix(s, prefix):
|
|
|
|
if not s.startswith(prefix):
|
|
|
|
return None
|
|
|
|
return s[len(prefix):]
|
|
|
|
|
2019-05-17 17:27:30 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
class StorageStatusElement(Element):
|
|
|
|
loader = XMLFile(FilePath(__file__).sibling("storage_status.xhtml"))
|
2009-02-20 21:29:26 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
def __init__(self, storage, nickname):
|
|
|
|
super(StorageStatusElement, self).__init__()
|
2009-02-20 21:29:26 +00:00
|
|
|
self.storage = storage
|
2020-02-04 11:46:08 +00:00
|
|
|
self.nick = nickname
|
2009-02-20 21:29:26 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def nickname(self, req, tag):
|
|
|
|
return self.nick
|
2009-03-07 05:45:17 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def nodeid(self, req, tag):
|
2012-03-13 02:57:36 +00:00
|
|
|
return idlib.nodeid_b2a(self.storage.my_nodeid)
|
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
def get_stat(self, key):
|
|
|
|
return self.storage.get_stats().get(key)
|
|
|
|
|
|
|
|
def str(self, tag, val):
|
|
|
|
if val is None:
|
|
|
|
return tag("?")
|
|
|
|
return tag(str(val))
|
|
|
|
|
|
|
|
def abbr(self, tag, val):
|
|
|
|
if val is None:
|
|
|
|
return tag("?")
|
|
|
|
return tag(abbreviate_space(val))
|
|
|
|
|
|
|
|
@renderer
|
|
|
|
def disk_total(self, req, tag):
|
|
|
|
return self.str(tag, self.get_stat("storage_server.disk_total"))
|
|
|
|
|
|
|
|
@renderer
|
|
|
|
def disk_total_abbrev(self, req, tag):
|
|
|
|
return self.abbr(tag, self.get_stat("storage_server.disk_total"))
|
|
|
|
|
|
|
|
@renderer
|
|
|
|
def disk_used(self, req, tag):
|
|
|
|
return self.str(tag, self.get_stat("storage_server.disk_used"))
|
|
|
|
|
|
|
|
@renderer
|
|
|
|
def disk_used_abbrev(self, req, tag):
|
|
|
|
return self.abbr(tag, self.get_stat("storage_server.disk_used"))
|
|
|
|
|
|
|
|
@renderer
|
|
|
|
def disk_free_for_root(self, req, tag):
|
|
|
|
return self.str(tag, self.get_stat("storage_server.disk_free_for_root"))
|
|
|
|
|
|
|
|
@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):
|
2009-02-21 04:04:08 +00:00
|
|
|
s = self.storage.bucket_counter.get_state()
|
2009-02-21 04:58:31 +00:00
|
|
|
count = s.get("last-complete-bucket-count")
|
|
|
|
if count is None:
|
2009-02-21 04:04:08 +00:00
|
|
|
return "Not computed yet"
|
2020-02-04 11:46:08 +00:00
|
|
|
return str(count)
|
2009-02-21 04:04:08 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def count_crawler_status(self, req, tag):
|
2009-03-07 05:45:17 +00:00
|
|
|
p = self.storage.bucket_counter.get_progress()
|
2020-02-04 11:46:08 +00:00
|
|
|
return self.format_crawler_progress(p)
|
2009-02-27 02:42:48 +00:00
|
|
|
|
2009-03-07 05:45:17 +00:00
|
|
|
def format_crawler_progress(self, p):
|
|
|
|
cycletime = p["estimated-time-per-cycle"]
|
2009-02-27 02:42:48 +00:00
|
|
|
cycletime_s = ""
|
|
|
|
if cycletime is not None:
|
2009-03-07 05:45:17 +00:00
|
|
|
cycletime_s = " (estimated cycle time %s)" % abbreviate_time(cycletime)
|
2009-02-27 02:42:48 +00:00
|
|
|
|
2009-03-07 05:45:17 +00:00
|
|
|
if p["cycle-in-progress"]:
|
|
|
|
pct = p["cycle-complete-percentage"]
|
|
|
|
soon = p["remaining-sleep-time"]
|
2009-02-27 02:42:48 +00:00
|
|
|
|
2009-03-07 05:45:17 +00:00
|
|
|
eta = p["estimated-cycle-complete-time-left"]
|
2009-02-27 02:42:48 +00:00
|
|
|
eta_s = ""
|
|
|
|
if eta is not None:
|
|
|
|
eta_s = " (ETA %ds)" % eta
|
|
|
|
|
2009-03-07 05:45:17 +00:00
|
|
|
return ["Current crawl %.1f%% complete" % pct,
|
|
|
|
eta_s,
|
|
|
|
" (next work in %s)" % abbreviate_time(soon),
|
|
|
|
cycletime_s,
|
|
|
|
]
|
|
|
|
else:
|
|
|
|
soon = p["remaining-wait-time"]
|
|
|
|
return ["Next crawl in %s" % abbreviate_time(soon),
|
|
|
|
cycletime_s]
|
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@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):
|
2009-03-07 05:45:17 +00:00
|
|
|
lc = self.storage.lease_checker
|
2009-03-17 05:10:41 +00:00
|
|
|
if lc.expiration_enabled:
|
2020-02-04 11:46:08 +00:00
|
|
|
return tag("Enabled: expired leases will be removed")
|
2009-02-21 04:04:08 +00:00
|
|
|
else:
|
2020-02-04 11:46:08 +00:00
|
|
|
return tag("Disabled: scan-only mode, no leases will be removed")
|
2009-03-07 05:45:17 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def lease_expiration_mode(self, req, tag):
|
2009-03-19 00:21:38 +00:00
|
|
|
lc = self.storage.lease_checker
|
|
|
|
if lc.mode == "age":
|
|
|
|
if lc.override_lease_duration is None:
|
2020-02-04 11:46:08 +00:00
|
|
|
tag("Leases will expire naturally, probably 31 days after "
|
|
|
|
"creation or renewal.")
|
2009-03-19 00:21:38 +00:00
|
|
|
else:
|
2020-02-04 11:46:08 +00:00
|
|
|
tag("Leases created or last renewed more than %s ago "
|
|
|
|
"will be considered expired."
|
|
|
|
% abbreviate_time(lc.override_lease_duration))
|
2009-03-17 05:10:41 +00:00
|
|
|
else:
|
2009-03-19 00:58:14 +00:00
|
|
|
assert lc.mode == "cutoff-date"
|
2009-04-03 22:59:04 +00:00
|
|
|
localizedutcdate = time.strftime("%d-%b-%Y", time.gmtime(lc.cutoff_date))
|
|
|
|
isoutcdate = time_format.iso_utc_date(lc.cutoff_date)
|
2020-02-04 11:46:08 +00:00
|
|
|
tag("Leases created or last renewed before %s (%s) UTC "
|
|
|
|
"will be considered expired."
|
|
|
|
% (isoutcdate, localizedutcdate, ))
|
2009-03-19 00:21:38 +00:00
|
|
|
if len(lc.mode) > 2:
|
2020-02-04 11:46:08 +00:00
|
|
|
tag(" The following sharetypes will be expired: ",
|
|
|
|
" ".join(sorted(lc.sharetypes_to_expire)), ".")
|
|
|
|
return tag
|
2009-03-07 05:45:17 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def lease_current_cycle_progress(self, req, tag):
|
2009-03-07 05:45:17 +00:00
|
|
|
lc = self.storage.lease_checker
|
|
|
|
p = lc.get_progress()
|
2020-02-04 11:46:08 +00:00
|
|
|
return tag(self.format_crawler_progress(p))
|
2009-03-07 05:45:17 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def lease_current_cycle_results(self, req, tag):
|
2009-03-07 05:45:17 +00:00
|
|
|
lc = self.storage.lease_checker
|
|
|
|
p = lc.get_progress()
|
|
|
|
if not p["cycle-in-progress"]:
|
|
|
|
return ""
|
|
|
|
s = lc.get_state()
|
|
|
|
so_far = s["cycle-to-date"]
|
|
|
|
sr = so_far["space-recovered"]
|
|
|
|
er = s["estimated-remaining-cycle"]
|
|
|
|
esr = er["space-recovered"]
|
|
|
|
ec = s["estimated-current-cycle"]
|
|
|
|
ecr = ec["space-recovered"]
|
|
|
|
|
|
|
|
p = T.ul()
|
|
|
|
def add(*pieces):
|
|
|
|
p[T.li[pieces]]
|
|
|
|
|
2009-03-07 23:02:43 +00:00
|
|
|
def maybe(d):
|
|
|
|
if d is None:
|
|
|
|
return "?"
|
|
|
|
return "%d" % d
|
2009-03-07 05:45:17 +00:00
|
|
|
add("So far, this cycle has examined %d shares in %d buckets"
|
2009-03-18 20:25:04 +00:00
|
|
|
% (sr["examined-shares"], sr["examined-buckets"]),
|
|
|
|
" (%d mutable / %d immutable)"
|
|
|
|
% (sr["examined-buckets-mutable"], sr["examined-buckets-immutable"]),
|
|
|
|
" (%s / %s)" % (abbreviate_space(sr["examined-diskbytes-mutable"]),
|
|
|
|
abbreviate_space(sr["examined-diskbytes-immutable"])),
|
|
|
|
)
|
2009-03-07 05:45:17 +00:00
|
|
|
add("and has recovered: ", self.format_recovered(sr, "actual"))
|
|
|
|
if so_far["expiration-enabled"]:
|
|
|
|
add("The remainder of this cycle is expected to recover: ",
|
|
|
|
self.format_recovered(esr, "actual"))
|
2009-03-07 23:02:43 +00:00
|
|
|
add("The whole cycle is expected to examine %s shares in %s buckets"
|
2009-03-18 20:25:04 +00:00
|
|
|
% (maybe(ecr["examined-shares"]), maybe(ecr["examined-buckets"])))
|
2009-03-07 05:45:17 +00:00
|
|
|
add("and to recover: ", self.format_recovered(ecr, "actual"))
|
|
|
|
|
|
|
|
else:
|
|
|
|
add("If expiration were enabled, we would have recovered: ",
|
2009-03-18 20:25:04 +00:00
|
|
|
self.format_recovered(sr, "configured"), " by now")
|
2009-03-07 05:45:17 +00:00
|
|
|
add("and the remainder of this cycle would probably recover: ",
|
2009-03-18 20:25:04 +00:00
|
|
|
self.format_recovered(esr, "configured"))
|
2009-03-07 05:45:17 +00:00
|
|
|
add("and the whole cycle would probably recover: ",
|
2009-03-18 20:25:04 +00:00
|
|
|
self.format_recovered(ecr, "configured"))
|
2009-03-07 05:45:17 +00:00
|
|
|
|
2009-03-19 18:48:37 +00:00
|
|
|
add("if we were strictly using each lease's default 31-day lease lifetime "
|
|
|
|
"(instead of our configured behavior), "
|
2009-03-17 05:10:41 +00:00
|
|
|
"this cycle would be expected to recover: ",
|
2009-03-18 20:25:04 +00:00
|
|
|
self.format_recovered(ecr, "original"))
|
2009-03-07 05:45:17 +00:00
|
|
|
|
2009-03-09 03:38:28 +00:00
|
|
|
if so_far["corrupt-shares"]:
|
|
|
|
add("Corrupt shares:",
|
|
|
|
T.ul[ [T.li[ ["SI %s shnum %d" % corrupt_share
|
|
|
|
for corrupt_share in so_far["corrupt-shares"] ]
|
|
|
|
]]])
|
2020-02-04 11:46:08 +00:00
|
|
|
return tag("Current cycle:", p)
|
2009-03-09 03:38:28 +00:00
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
@renderer
|
|
|
|
def lease_last_cycle_results(self, req, tag):
|
2009-03-07 05:45:17 +00:00
|
|
|
lc = self.storage.lease_checker
|
|
|
|
h = lc.get_state()["history"]
|
|
|
|
if not h:
|
|
|
|
return ""
|
|
|
|
last = h[max(h.keys())]
|
2009-03-09 03:38:28 +00:00
|
|
|
|
2009-03-07 05:45:17 +00:00
|
|
|
start, end = last["cycle-start-finish-times"]
|
2020-02-04 11:46:08 +00:00
|
|
|
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"))
|
2009-03-09 03:38:28 +00:00
|
|
|
|
|
|
|
p = T.ul()
|
2020-02-04 11:46:08 +00:00
|
|
|
|
2009-03-09 03:38:28 +00:00
|
|
|
def add(*pieces):
|
2020-02-04 11:46:08 +00:00
|
|
|
p(T.li(pieces))
|
2009-03-09 03:38:28 +00:00
|
|
|
|
2010-02-08 00:20:10 +00:00
|
|
|
saw = self.format_recovered(last["space-recovered"], "examined")
|
|
|
|
add("and saw a total of ", saw)
|
|
|
|
|
2009-03-07 05:45:17 +00:00
|
|
|
if not last["expiration-enabled"]:
|
2009-03-18 20:25:04 +00:00
|
|
|
rec = self.format_recovered(last["space-recovered"], "configured")
|
2009-03-09 03:38:28 +00:00
|
|
|
add("but expiration was not enabled. If it had been, "
|
|
|
|
"it would have recovered: ", rec)
|
|
|
|
|
|
|
|
if last["corrupt-shares"]:
|
|
|
|
add("Corrupt shares:",
|
|
|
|
T.ul[ [T.li[ ["SI %s shnum %d" % corrupt_share
|
|
|
|
for corrupt_share in last["corrupt-shares"] ]
|
|
|
|
]]])
|
|
|
|
|
2020-02-04 11:46:08 +00:00
|
|
|
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"
|
2020-02-05 03:59:45 +00:00
|
|
|
|
|
|
|
def renderSynchronously(self):
|
|
|
|
# to appease the test suite.
|
|
|
|
elem = StorageStatusElement(self.storage, self.nickname)
|
|
|
|
result = []
|
|
|
|
flattenString(None, elem).addCallback(result.append)
|
2020-02-06 03:05:33 +00:00
|
|
|
return result[0]
|
2020-02-05 03:59:45 +00:00
|
|
|
|
|
|
|
def renderHTTP(self, ctx=None):
|
|
|
|
# to appease the test suite.
|
|
|
|
self.renderSynchronously()
|