mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-20 13:33:09 +00:00
Merge pull request #729 from sajith/3316.check-results-nevow-to-twisted-web
Replace nevow with twisted.web.template in web.check_results
This commit is contained in:
commit
8f1b712a8f
1
newsfragments/3316.minor
Normal file
1
newsfragments/3316.minor
Normal file
@ -0,0 +1 @@
|
|||||||
|
Port checker result pages' rendering from nevow to twisted web templates.
|
@ -1,10 +1,25 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import os.path, shutil
|
import os.path, shutil
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
from twisted.trial import unittest
|
from twisted.trial import unittest
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
from nevow.inevow import IRequest
|
||||||
|
from zope.interface import implementer
|
||||||
|
from twisted.web.server import Request
|
||||||
|
from twisted.web.test.requesthelper import DummyChannel
|
||||||
|
from twisted.web.template import flattenString
|
||||||
|
|
||||||
from allmydata import check_results, uri
|
from allmydata import check_results, uri
|
||||||
from allmydata import uri as tahoe_uri
|
from allmydata import uri as tahoe_uri
|
||||||
|
from allmydata.interfaces import (
|
||||||
|
IServer,
|
||||||
|
ICheckResults,
|
||||||
|
ICheckAndRepairResults,
|
||||||
|
)
|
||||||
from allmydata.util import base32
|
from allmydata.util import base32
|
||||||
from allmydata.web import check_results as web_check_results
|
from allmydata.web import check_results as web_check_results
|
||||||
from allmydata.storage_client import StorageFarmBroker, NativeStorageServer
|
from allmydata.storage_client import StorageFarmBroker, NativeStorageServer
|
||||||
@ -12,18 +27,115 @@ from allmydata.storage.server import storage_index_to_dir
|
|||||||
from allmydata.monitor import Monitor
|
from allmydata.monitor import Monitor
|
||||||
from allmydata.test.no_network import GridTestMixin
|
from allmydata.test.no_network import GridTestMixin
|
||||||
from allmydata.immutable.upload import Data
|
from allmydata.immutable.upload import Data
|
||||||
from allmydata.test.common_web import WebRenderingMixin
|
|
||||||
from allmydata.mutable.publish import MutableData
|
from allmydata.mutable.publish import MutableData
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
EMPTY_CLIENT_CONFIG,
|
EMPTY_CLIENT_CONFIG,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .web.common import (
|
||||||
|
assert_soup_has_favicon,
|
||||||
|
assert_soup_has_tag_with_content,
|
||||||
|
)
|
||||||
|
|
||||||
class FakeClient(object):
|
class FakeClient(object):
|
||||||
def get_storage_broker(self):
|
def get_storage_broker(self):
|
||||||
return self.storage_broker
|
return self.storage_broker
|
||||||
|
|
||||||
class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
@implementer(IRequest)
|
||||||
|
class TestRequest(Request, object):
|
||||||
|
"""
|
||||||
|
A minimal Request class to use in tests.
|
||||||
|
|
||||||
|
XXX: We have to have this class because `common.get_arg()` expects
|
||||||
|
a `nevow.inevow.IRequest`, which `twisted.web.server.Request`
|
||||||
|
isn't. The request needs to have `args`, `fields`, `prepath`, and
|
||||||
|
`postpath` properties so that `allmydata.web.common.get_arg()`
|
||||||
|
won't complain.
|
||||||
|
"""
|
||||||
|
def __init__(self, args=None, fields=None):
|
||||||
|
super(TestRequest, self).__init__(DummyChannel())
|
||||||
|
self.args = args or {}
|
||||||
|
self.fields = fields or {}
|
||||||
|
self.prepath = [b""]
|
||||||
|
self.postpath = [b""]
|
||||||
|
|
||||||
|
|
||||||
|
@implementer(IServer)
|
||||||
|
class FakeServer(object):
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return "fake name"
|
||||||
|
|
||||||
|
def get_longname(self):
|
||||||
|
return "fake longname"
|
||||||
|
|
||||||
|
def get_nickname(self):
|
||||||
|
return "fake nickname"
|
||||||
|
|
||||||
|
|
||||||
|
@implementer(ICheckResults)
|
||||||
|
class FakeCheckResults(object):
|
||||||
|
|
||||||
|
def __init__(self, si=None,
|
||||||
|
healthy=False, recoverable=False,
|
||||||
|
summary="fake summary"):
|
||||||
|
self._storage_index = si
|
||||||
|
self._is_healthy = healthy
|
||||||
|
self._is_recoverable = recoverable
|
||||||
|
self._summary = summary
|
||||||
|
|
||||||
|
def get_storage_index(self):
|
||||||
|
return self._storage_index
|
||||||
|
|
||||||
|
def get_storage_index_string(self):
|
||||||
|
return base32.b2a_or_none(self._storage_index)
|
||||||
|
|
||||||
|
def is_healthy(self):
|
||||||
|
return self._is_healthy
|
||||||
|
|
||||||
|
def is_recoverable(self):
|
||||||
|
return self._is_recoverable
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
return self._summary
|
||||||
|
|
||||||
|
def get_corrupt_shares(self):
|
||||||
|
# returns a list of (IServer, storage_index, sharenum)
|
||||||
|
return [(FakeServer(), "<fake-si>", 0)]
|
||||||
|
|
||||||
|
|
||||||
|
@implementer(ICheckAndRepairResults)
|
||||||
|
class FakeCheckAndRepairResults(object):
|
||||||
|
|
||||||
|
def __init__(self, si=None,
|
||||||
|
repair_attempted=False,
|
||||||
|
repair_success=False):
|
||||||
|
self._storage_index = si
|
||||||
|
self._repair_attempted = repair_attempted
|
||||||
|
self._repair_success = repair_success
|
||||||
|
|
||||||
|
def get_storage_index(self):
|
||||||
|
return self._storage_index
|
||||||
|
|
||||||
|
def get_pre_repair_results(self):
|
||||||
|
return FakeCheckResults()
|
||||||
|
|
||||||
|
def get_post_repair_results(self):
|
||||||
|
return FakeCheckResults()
|
||||||
|
|
||||||
|
def get_repair_attempted(self):
|
||||||
|
return self._repair_attempted
|
||||||
|
|
||||||
|
def get_repair_successful(self):
|
||||||
|
return self._repair_success
|
||||||
|
|
||||||
|
|
||||||
|
class WebResultsRendering(unittest.TestCase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_tags(html):
|
||||||
|
return BeautifulSoup(html).get_text(separator=" ")
|
||||||
|
|
||||||
def create_fake_client(self):
|
def create_fake_client(self):
|
||||||
sb = StorageFarmBroker(True, None, EMPTY_CLIENT_CONFIG)
|
sb = StorageFarmBroker(True, None, EMPTY_CLIENT_CONFIG)
|
||||||
@ -51,34 +163,31 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
c.storage_broker = sb
|
c.storage_broker = sb
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def render_json(self, page):
|
def render_json(self, resource):
|
||||||
d = self.render1(page, args={"output": ["json"]})
|
return resource.render(TestRequest(args={"output": ["json"]}))
|
||||||
return d
|
|
||||||
|
def render_element(self, element, args=None):
|
||||||
|
d = flattenString(TestRequest(args), element)
|
||||||
|
return unittest.TestCase().successResultOf(d)
|
||||||
|
|
||||||
def test_literal(self):
|
def test_literal(self):
|
||||||
|
lcr = web_check_results.LiteralCheckResultsRendererElement()
|
||||||
|
|
||||||
|
html = self.render_element(lcr)
|
||||||
|
self.failUnlessIn("Literal files are always healthy", html)
|
||||||
|
|
||||||
|
html = self.render_element(lcr, args={"return_to": ["FOOURL"]})
|
||||||
|
self.failUnlessIn("Literal files are always healthy", html)
|
||||||
|
self.failUnlessIn('<a href="FOOURL">Return to file.</a>', html)
|
||||||
|
|
||||||
c = self.create_fake_client()
|
c = self.create_fake_client()
|
||||||
lcr = web_check_results.LiteralCheckResultsRenderer(c)
|
lcr = web_check_results.LiteralCheckResultsRenderer(c)
|
||||||
|
|
||||||
d = self.render1(lcr)
|
js = self.render_json(lcr)
|
||||||
def _check(html):
|
|
||||||
s = self.remove_tags(html)
|
|
||||||
self.failUnlessIn("Literal files are always healthy", s)
|
|
||||||
d.addCallback(_check)
|
|
||||||
d.addCallback(lambda ignored:
|
|
||||||
self.render1(lcr, args={"return_to": ["FOOURL"]}))
|
|
||||||
def _check_return_to(html):
|
|
||||||
s = self.remove_tags(html)
|
|
||||||
self.failUnlessIn("Literal files are always healthy", s)
|
|
||||||
self.failUnlessIn('<a href="FOOURL">Return to file.</a>',
|
|
||||||
html)
|
|
||||||
d.addCallback(_check_return_to)
|
|
||||||
d.addCallback(lambda ignored: self.render_json(lcr))
|
|
||||||
def _check_json(js):
|
|
||||||
j = json.loads(js)
|
j = json.loads(js)
|
||||||
self.failUnlessEqual(j["storage-index"], "")
|
self.failUnlessEqual(j["storage-index"], "")
|
||||||
self.failUnlessEqual(j["results"]["healthy"], True)
|
self.failUnlessEqual(j["results"]["healthy"], True)
|
||||||
d.addCallback(_check_json)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def test_check(self):
|
def test_check(self):
|
||||||
c = self.create_fake_client()
|
c = self.create_fake_client()
|
||||||
@ -108,8 +217,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
healthy=True, recoverable=True,
|
healthy=True, recoverable=True,
|
||||||
summary="groovy",
|
summary="groovy",
|
||||||
**data)
|
**data)
|
||||||
w = web_check_results.CheckResultsRenderer(c, cr)
|
w = web_check_results.CheckResultsRendererElement(c, cr)
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
||||||
self.failUnlessIn("Healthy : groovy", s)
|
self.failUnlessIn("Healthy : groovy", s)
|
||||||
@ -126,8 +235,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
healthy=False, recoverable=True,
|
healthy=False, recoverable=True,
|
||||||
summary="ungroovy",
|
summary="ungroovy",
|
||||||
**data)
|
**data)
|
||||||
w = web_check_results.CheckResultsRenderer(c, cr)
|
w = web_check_results.CheckResultsRendererElement(c, cr)
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
||||||
self.failUnlessIn("Not Healthy! : ungroovy", s)
|
self.failUnlessIn("Not Healthy! : ungroovy", s)
|
||||||
@ -138,22 +247,23 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
healthy=False, recoverable=False,
|
healthy=False, recoverable=False,
|
||||||
summary="rather dead",
|
summary="rather dead",
|
||||||
**data)
|
**data)
|
||||||
w = web_check_results.CheckResultsRenderer(c, cr)
|
w = web_check_results.CheckResultsRendererElement(c, cr)
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
||||||
self.failUnlessIn("Not Recoverable! : rather dead", s)
|
self.failUnlessIn("Not Recoverable! : rather dead", s)
|
||||||
self.failUnlessIn("Corrupt shares: Share ID Nickname Node ID sh#2 peer-0 00000000", s)
|
self.failUnlessIn("Corrupt shares: Share ID Nickname Node ID sh#2 peer-0 00000000", s)
|
||||||
|
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated
|
||||||
self.failUnlessIn("Not Recoverable! : rather dead", s)
|
self.failUnlessIn("Not Recoverable! : rather dead", s)
|
||||||
|
|
||||||
html = self.render2(w, args={"return_to": ["FOOURL"]})
|
html = self.render_element(w, args={"return_to": ["FOOURL"]})
|
||||||
self.failUnlessIn('<a href="FOOURL">Return to file/directory.</a>',
|
self.failUnlessIn('<a href="FOOURL">Return to file/directory.</a>',
|
||||||
html)
|
html)
|
||||||
|
|
||||||
|
w = web_check_results.CheckResultsRenderer(c, cr)
|
||||||
d = self.render_json(w)
|
d = self.render_json(w)
|
||||||
def _check_json(jdata):
|
def _check_json(jdata):
|
||||||
j = json.loads(jdata)
|
j = json.loads(jdata)
|
||||||
@ -178,15 +288,15 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
'recoverable': False,
|
'recoverable': False,
|
||||||
}
|
}
|
||||||
self.failUnlessEqual(j["results"], expected)
|
self.failUnlessEqual(j["results"], expected)
|
||||||
d.addCallback(_check_json)
|
_check_json(d)
|
||||||
d.addCallback(lambda ignored: self.render1(w))
|
|
||||||
|
w = web_check_results.CheckResultsRendererElement(c, cr)
|
||||||
|
d = self.render_element(w)
|
||||||
def _check(html):
|
def _check(html):
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
self.failUnlessIn("File Check Results for SI=2k6avp", s)
|
self.failUnlessIn("File Check Results for SI=2k6avp", s)
|
||||||
self.failUnlessIn("Not Recoverable! : rather dead", s)
|
self.failUnlessIn("Not Recoverable! : rather dead", s)
|
||||||
d.addCallback(_check)
|
_check(html)
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def test_check_and_repair(self):
|
def test_check_and_repair(self):
|
||||||
c = self.create_fake_client()
|
c = self.create_fake_client()
|
||||||
@ -244,8 +354,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
crr.post_repair_results = post_cr
|
crr.post_repair_results = post_cr
|
||||||
crr.repair_attempted = False
|
crr.repair_attempted = False
|
||||||
|
|
||||||
w = web_check_results.CheckAndRepairResultsRenderer(c, crr)
|
w = web_check_results.CheckAndRepairResultsRendererElement(c, crr)
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
|
|
||||||
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
||||||
@ -256,7 +366,7 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
|
|
||||||
crr.repair_attempted = True
|
crr.repair_attempted = True
|
||||||
crr.repair_successful = True
|
crr.repair_successful = True
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
|
|
||||||
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
||||||
@ -271,7 +381,7 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
summary="better",
|
summary="better",
|
||||||
**data)
|
**data)
|
||||||
crr.post_repair_results = post_cr
|
crr.post_repair_results = post_cr
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
|
|
||||||
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
||||||
@ -286,7 +396,7 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
summary="worse",
|
summary="worse",
|
||||||
**data)
|
**data)
|
||||||
crr.post_repair_results = post_cr
|
crr.post_repair_results = post_cr
|
||||||
html = self.render2(w)
|
html = self.render_element(w)
|
||||||
s = self.remove_tags(html)
|
s = self.remove_tags(html)
|
||||||
|
|
||||||
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s)
|
||||||
@ -294,24 +404,218 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin):
|
|||||||
self.failUnlessIn("Repair unsuccessful", s)
|
self.failUnlessIn("Repair unsuccessful", s)
|
||||||
self.failUnlessIn("Post-Repair Checker Results:", s)
|
self.failUnlessIn("Post-Repair Checker Results:", s)
|
||||||
|
|
||||||
d = self.render_json(w)
|
w = web_check_results.CheckAndRepairResultsRenderer(c, crr)
|
||||||
def _got_json(data):
|
j = json.loads(self.render_json(w))
|
||||||
j = json.loads(data)
|
|
||||||
self.failUnlessEqual(j["repair-attempted"], True)
|
self.failUnlessEqual(j["repair-attempted"], True)
|
||||||
self.failUnlessEqual(j["storage-index"],
|
self.failUnlessEqual(j["storage-index"],
|
||||||
"2k6avpjga3dho3zsjo6nnkt7n4")
|
"2k6avpjga3dho3zsjo6nnkt7n4")
|
||||||
self.failUnlessEqual(j["pre-repair-results"]["summary"], "illing")
|
self.failUnlessEqual(j["pre-repair-results"]["summary"], "illing")
|
||||||
self.failUnlessEqual(j["post-repair-results"]["summary"], "worse")
|
self.failUnlessEqual(j["post-repair-results"]["summary"], "worse")
|
||||||
d.addCallback(_got_json)
|
|
||||||
|
|
||||||
w2 = web_check_results.CheckAndRepairResultsRenderer(c, None)
|
w = web_check_results.CheckAndRepairResultsRenderer(c, None)
|
||||||
d.addCallback(lambda ignored: self.render_json(w2))
|
j = json.loads(self.render_json(w))
|
||||||
def _got_lit_results(data):
|
|
||||||
j = json.loads(data)
|
|
||||||
self.failUnlessEqual(j["repair-attempted"], False)
|
self.failUnlessEqual(j["repair-attempted"], False)
|
||||||
self.failUnlessEqual(j["storage-index"], "")
|
self.failUnlessEqual(j["storage-index"], "")
|
||||||
d.addCallback(_got_lit_results)
|
|
||||||
return d
|
|
||||||
|
def test_deep_check_renderer(self):
|
||||||
|
status = check_results.DeepCheckResults("fake-root-si")
|
||||||
|
status.add_check(
|
||||||
|
FakeCheckResults("<unhealthy/unrecoverable>", False, False),
|
||||||
|
(u"fake", u"unhealthy", u"unrecoverable")
|
||||||
|
)
|
||||||
|
status.add_check(
|
||||||
|
FakeCheckResults("<healthy/recoverable>", True, True),
|
||||||
|
(u"fake", u"healthy", u"recoverable")
|
||||||
|
)
|
||||||
|
status.add_check(
|
||||||
|
FakeCheckResults("<healthy/unrecoverable>", True, False),
|
||||||
|
(u"fake", u"healthy", u"unrecoverable")
|
||||||
|
)
|
||||||
|
status.add_check(
|
||||||
|
FakeCheckResults("<unhealthy/unrecoverable>", False, True),
|
||||||
|
(u"fake", u"unhealthy", u"recoverable")
|
||||||
|
)
|
||||||
|
|
||||||
|
monitor = Monitor()
|
||||||
|
monitor.set_status(status)
|
||||||
|
|
||||||
|
elem = web_check_results.DeepCheckResultsRendererElement(monitor)
|
||||||
|
doc = self.render_element(elem)
|
||||||
|
soup = BeautifulSoup(doc, 'html5lib')
|
||||||
|
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"title",
|
||||||
|
u"Tahoe-LAFS - Deep Check Results"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h1",
|
||||||
|
"Deep-Check Results for root SI="
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Checked: 4"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Healthy: 2"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Unhealthy: 2"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Unrecoverable: 2"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Corrupt Shares: 4"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Files/Directories That Had Problems:"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"fake/unhealthy/recoverable: fake summary"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"fake/unhealthy/unrecoverable: fake summary"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Servers on which corrupt shares were found"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Corrupt Shares"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"All Results"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_deep_check_and_repair_renderer(self):
|
||||||
|
status = check_results.DeepCheckAndRepairResults("")
|
||||||
|
|
||||||
|
status.add_check_and_repair(
|
||||||
|
FakeCheckAndRepairResults("attempted/success", True, True),
|
||||||
|
(u"attempted", u"success")
|
||||||
|
)
|
||||||
|
status.add_check_and_repair(
|
||||||
|
FakeCheckAndRepairResults("attempted/failure", True, False),
|
||||||
|
(u"attempted", u"failure")
|
||||||
|
)
|
||||||
|
status.add_check_and_repair(
|
||||||
|
FakeCheckAndRepairResults("unattempted/failure", False, False),
|
||||||
|
(u"unattempted", u"failure")
|
||||||
|
)
|
||||||
|
|
||||||
|
monitor = Monitor()
|
||||||
|
monitor.set_status(status)
|
||||||
|
|
||||||
|
elem = web_check_results.DeepCheckAndRepairResultsRendererElement(monitor)
|
||||||
|
doc = self.render_element(elem)
|
||||||
|
soup = BeautifulSoup(doc, 'html5lib')
|
||||||
|
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"title",
|
||||||
|
u"Tahoe-LAFS - Deep Check Results"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h1",
|
||||||
|
u"Deep-Check-And-Repair Results for root SI="
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Checked: 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Healthy (before repair): 0"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Unhealthy (before repair): 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Corrupt Shares (before repair): 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Repairs Attempted: 2"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Repairs Successful: 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
"Repairs Unsuccessful: 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Healthy (after repair): 0"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Objects Unhealthy (after repair): 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"li",
|
||||||
|
u"Corrupt Shares (after repair): 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Files/Directories That Had Problems:"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Files/Directories That Still Have Problems:"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Servers on which corrupt shares were found"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_soup_has_tag_with_content(
|
||||||
|
self, soup, u"h2",
|
||||||
|
u"Remaining Corrupt Shares"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BalancingAct(GridTestMixin, unittest.TestCase):
|
class BalancingAct(GridTestMixin, unittest.TestCase):
|
||||||
# test for #1115 regarding the 'count-good-share-hosts' metric
|
# test for #1115 regarding the 'count-good-share-hosts' metric
|
||||||
|
@ -5,8 +5,6 @@ unknown_rwcap = u"lafs://from_the_future_rw_\u263A".encode('utf-8')
|
|||||||
unknown_rocap = u"ro.lafs://readonly_from_the_future_ro_\u263A".encode('utf-8')
|
unknown_rocap = u"ro.lafs://readonly_from_the_future_ro_\u263A".encode('utf-8')
|
||||||
unknown_immcap = u"imm.lafs://immutable_from_the_future_imm_\u263A".encode('utf-8')
|
unknown_immcap = u"imm.lafs://immutable_from_the_future_imm_\u263A".encode('utf-8')
|
||||||
|
|
||||||
FAVICON_MARKUP = '<link href="/icon.png" rel="shortcut icon" />'
|
|
||||||
|
|
||||||
|
|
||||||
def assert_soup_has_favicon(testcase, soup):
|
def assert_soup_has_favicon(testcase, soup):
|
||||||
"""
|
"""
|
||||||
|
@ -21,7 +21,12 @@ from allmydata.mutable import publish
|
|||||||
from .. import common_util as testutil
|
from .. import common_util as testutil
|
||||||
from ..common import WebErrorMixin, ShouldFailMixin
|
from ..common import WebErrorMixin, ShouldFailMixin
|
||||||
from ..no_network import GridTestMixin
|
from ..no_network import GridTestMixin
|
||||||
from .common import unknown_rwcap, unknown_rocap, unknown_immcap, FAVICON_MARKUP
|
from .common import (
|
||||||
|
assert_soup_has_favicon,
|
||||||
|
unknown_immcap,
|
||||||
|
unknown_rocap,
|
||||||
|
unknown_rwcap,
|
||||||
|
)
|
||||||
|
|
||||||
DIR_HTML_TAG = '<html lang="en">'
|
DIR_HTML_TAG = '<html lang="en">'
|
||||||
|
|
||||||
@ -92,7 +97,9 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
|||||||
def _got_html_good(res):
|
def _got_html_good(res):
|
||||||
self.failUnlessIn("Healthy", res)
|
self.failUnlessIn("Healthy", res)
|
||||||
self.failIfIn("Not Healthy", res)
|
self.failIfIn("Not Healthy", res)
|
||||||
self.failUnlessIn(FAVICON_MARKUP, res)
|
soup = BeautifulSoup(res, 'html5lib')
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
|
|
||||||
d.addCallback(_got_html_good)
|
d.addCallback(_got_html_good)
|
||||||
d.addCallback(self.CHECK, "good", "t=check&return_to=somewhere")
|
d.addCallback(self.CHECK, "good", "t=check&return_to=somewhere")
|
||||||
def _got_html_good_return_to(res):
|
def _got_html_good_return_to(res):
|
||||||
@ -235,7 +242,9 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
|||||||
self.failUnlessIn("Healthy", res)
|
self.failUnlessIn("Healthy", res)
|
||||||
self.failIfIn("Not Healthy", res)
|
self.failIfIn("Not Healthy", res)
|
||||||
self.failUnlessIn("No repair necessary", res)
|
self.failUnlessIn("No repair necessary", res)
|
||||||
self.failUnlessIn(FAVICON_MARKUP, res)
|
soup = BeautifulSoup(res, 'html5lib')
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
|
|
||||||
d.addCallback(_got_html_good)
|
d.addCallback(_got_html_good)
|
||||||
|
|
||||||
d.addCallback(self.CHECK, "sick", "t=check&repair=true")
|
d.addCallback(self.CHECK, "sick", "t=check&repair=true")
|
||||||
|
@ -54,6 +54,9 @@ from .common import (
|
|||||||
assert_soup_has_tag_with_attributes,
|
assert_soup_has_tag_with_attributes,
|
||||||
assert_soup_has_tag_with_content,
|
assert_soup_has_tag_with_content,
|
||||||
assert_soup_has_tag_with_attributes_and_content,
|
assert_soup_has_tag_with_attributes_and_content,
|
||||||
|
unknown_rwcap,
|
||||||
|
unknown_rocap,
|
||||||
|
unknown_immcap,
|
||||||
)
|
)
|
||||||
|
|
||||||
from allmydata.interfaces import IMutableFileNode, SDMF_VERSION, MDMF_VERSION
|
from allmydata.interfaces import IMutableFileNode, SDMF_VERSION, MDMF_VERSION
|
||||||
@ -65,7 +68,6 @@ from ..common_web import (
|
|||||||
Error,
|
Error,
|
||||||
)
|
)
|
||||||
from allmydata.client import _Client, SecretHolder
|
from allmydata.client import _Client, SecretHolder
|
||||||
from .common import unknown_rwcap, unknown_rocap, unknown_immcap, FAVICON_MARKUP
|
|
||||||
|
|
||||||
# create a fake uploader/downloader, and a couple of fake dirnodes, then
|
# create a fake uploader/downloader, and a couple of fake dirnodes, then
|
||||||
# create a webserver that works against them
|
# create a webserver that works against them
|
||||||
@ -3262,13 +3264,15 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
|||||||
res = yield self.get_operation_results(None, "123", "html")
|
res = yield self.get_operation_results(None, "123", "html")
|
||||||
self.failUnlessIn("Objects Checked: <span>11</span>", res)
|
self.failUnlessIn("Objects Checked: <span>11</span>", res)
|
||||||
self.failUnlessIn("Objects Healthy: <span>11</span>", res)
|
self.failUnlessIn("Objects Healthy: <span>11</span>", res)
|
||||||
self.failUnlessIn(FAVICON_MARKUP, res)
|
soup = BeautifulSoup(res, 'html5lib')
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
|
|
||||||
res = yield self.GET("/operations/123/")
|
res = yield self.GET("/operations/123/")
|
||||||
# should be the same as without the slash
|
# should be the same as without the slash
|
||||||
self.failUnlessIn("Objects Checked: <span>11</span>", res)
|
self.failUnlessIn("Objects Checked: <span>11</span>", res)
|
||||||
self.failUnlessIn("Objects Healthy: <span>11</span>", res)
|
self.failUnlessIn("Objects Healthy: <span>11</span>", res)
|
||||||
self.failUnlessIn(FAVICON_MARKUP, res)
|
soup = BeautifulSoup(res, 'html5lib')
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
|
|
||||||
yield self.shouldFail2(error.Error, "one", "404 Not Found",
|
yield self.shouldFail2(error.Error, "one", "404 Not Found",
|
||||||
"No detailed results for SI bogus",
|
"No detailed results for SI bogus",
|
||||||
@ -3318,7 +3322,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
|||||||
self.failUnlessIn("Objects Unhealthy (after repair): <span>0</span>", res)
|
self.failUnlessIn("Objects Unhealthy (after repair): <span>0</span>", res)
|
||||||
self.failUnlessIn("Corrupt Shares (after repair): <span>0</span>", res)
|
self.failUnlessIn("Corrupt Shares (after repair): <span>0</span>", res)
|
||||||
|
|
||||||
self.failUnlessIn(FAVICON_MARKUP, res)
|
soup = BeautifulSoup(res, 'html5lib')
|
||||||
|
assert_soup_has_favicon(self, soup)
|
||||||
d.addCallback(_check_html)
|
d.addCallback(_check_html)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@ -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>
|
<head>
|
||||||
<title>Tahoe-LAFS - Check Results</title>
|
<title>Tahoe-LAFS - Check Results</title>
|
||||||
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
||||||
@ -7,17 +7,17 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1>File Check-And-Repair Results for SI=<span n:render="storage_index" /></h1>
|
<h1>File Check-And-Repair Results for SI=<span t:render="storage_index" /></h1>
|
||||||
|
|
||||||
<div n:render="summary" />
|
<div t:render="summary" />
|
||||||
|
|
||||||
<div n:render="repair_results" />
|
<div t:render="repair_results" />
|
||||||
|
|
||||||
<div n:render="post_repair_results" />
|
<div t:render="post_repair_results" />
|
||||||
|
|
||||||
<div n:render="maybe_pre_repair_results" />
|
<div t:render="maybe_pre_repair_results" />
|
||||||
|
|
||||||
<div n:render="return" />
|
<div t:render="return_to" />
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -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>
|
<head>
|
||||||
<title>Tahoe-LAFS - Check Results</title>
|
<title>Tahoe-LAFS - Check Results</title>
|
||||||
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
||||||
@ -7,17 +7,17 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1>File Check Results for SI=<span n:render="storage_index" /></h1>
|
<h1>File Check Results for SI=<span t:render="storage_index" /></h1>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span n:render="summary" />
|
<span t:render="summary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="repair" />
|
<div t:render="repair" />
|
||||||
|
|
||||||
<div n:render="results" />
|
<div t:render="results" />
|
||||||
|
|
||||||
<div n:render="return" />
|
<div t:render="return_to" />
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,12 +1,35 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
from nevow import rend, inevow, tags as T
|
|
||||||
from twisted.web import http, html
|
from twisted.web import (
|
||||||
from allmydata.web.common import getxmlfile, get_arg, get_root, WebError
|
http,
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
from twisted.python.filepath import FilePath
|
||||||
|
from twisted.web.template import (
|
||||||
|
Element,
|
||||||
|
XMLFile,
|
||||||
|
renderer,
|
||||||
|
renderElement,
|
||||||
|
tags,
|
||||||
|
)
|
||||||
|
from allmydata.web.common import (
|
||||||
|
get_arg,
|
||||||
|
get_root,
|
||||||
|
WebError,
|
||||||
|
MultiFormatResource,
|
||||||
|
SlotsSequenceElement,
|
||||||
|
)
|
||||||
from allmydata.web.operations import ReloadMixin
|
from allmydata.web.operations import ReloadMixin
|
||||||
from allmydata.interfaces import ICheckAndRepairResults, ICheckResults
|
from allmydata.interfaces import (
|
||||||
from allmydata.util import base32, dictutil
|
ICheckAndRepairResults,
|
||||||
|
ICheckResults,
|
||||||
|
)
|
||||||
|
from allmydata.util import (
|
||||||
|
base32,
|
||||||
|
dictutil,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def json_check_counts(r):
|
def json_check_counts(r):
|
||||||
@ -64,53 +87,64 @@ def json_check_and_repair_results(r):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
class ResultsBase(object):
|
class ResultsBase(object):
|
||||||
# self.client must point to the Client, so we can get nicknames and
|
# self._client must point to the Client, so we can get nicknames and
|
||||||
# determine the permuted peer order
|
# determine the permuted peer order
|
||||||
|
|
||||||
def _join_pathstring(self, path):
|
def _join_pathstring(self, path):
|
||||||
|
"""
|
||||||
|
:param tuple path: a path represented by a tuple, such as
|
||||||
|
``(u'some', u'dir', u'file')``.
|
||||||
|
|
||||||
|
:return: a string joined by path separaters, such as
|
||||||
|
``u'some/dir/file'``.
|
||||||
|
"""
|
||||||
if path:
|
if path:
|
||||||
pathstring = "/".join(self._html(path))
|
pathstring = "/".join(self._html(path))
|
||||||
else:
|
else:
|
||||||
pathstring = "<root>"
|
pathstring = "<root>"
|
||||||
return pathstring
|
return pathstring
|
||||||
|
|
||||||
def _render_results(self, ctx, cr):
|
def _render_results(self, req, cr):
|
||||||
assert ICheckResults(cr)
|
assert ICheckResults(cr)
|
||||||
c = self.client
|
c = self._client
|
||||||
sb = c.get_storage_broker()
|
sb = c.get_storage_broker()
|
||||||
r = []
|
r = []
|
||||||
def add(name, value):
|
def add(name, value):
|
||||||
r.append(T.li[name + ": ", value])
|
r.append(tags.li(name + ": ", value))
|
||||||
|
|
||||||
|
add("Report", tags.pre("\n".join(self._html(cr.get_report()))))
|
||||||
|
|
||||||
add("Report", T.pre["\n".join(self._html(cr.get_report()))])
|
|
||||||
add("Share Counts",
|
add("Share Counts",
|
||||||
"need %d-of-%d, have %d" % (cr.get_encoding_needed(),
|
"need %d-of-%d, have %d" % (cr.get_encoding_needed(),
|
||||||
cr.get_encoding_expected(),
|
cr.get_encoding_expected(),
|
||||||
cr.get_share_counter_good()))
|
cr.get_share_counter_good()))
|
||||||
add("Happiness Level", cr.get_happiness())
|
add("Happiness Level", str(cr.get_happiness()))
|
||||||
add("Hosts with good shares", cr.get_host_counter_good_shares())
|
add("Hosts with good shares", str(cr.get_host_counter_good_shares()))
|
||||||
|
|
||||||
if cr.get_corrupt_shares():
|
if cr.get_corrupt_shares():
|
||||||
badsharemap = []
|
badsharemap = []
|
||||||
for (s, si, shnum) in cr.get_corrupt_shares():
|
for (s, si, shnum) in cr.get_corrupt_shares():
|
||||||
d = T.tr[T.td["sh#%d" % shnum],
|
d = tags.tr(tags.td("sh#%d" % shnum),
|
||||||
T.td[T.div(class_="nickname")[s.get_nickname()],
|
tags.td(tags.div(s.get_nickname(), class_="nickname"),
|
||||||
T.div(class_="nodeid")[T.tt[s.get_name()]]],
|
tags.div(tags.tt(s.get_name()), class_="nodeid")),)
|
||||||
]
|
|
||||||
badsharemap.append(d)
|
badsharemap.append(d)
|
||||||
add("Corrupt shares", T.table()[
|
add("Corrupt shares",
|
||||||
T.tr[T.th["Share ID"],
|
tags.table(
|
||||||
T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]]],
|
tags.tr(tags.th("Share ID"),
|
||||||
badsharemap])
|
tags.th((tags.div("Nickname"), tags.div("Node ID", class_="nodeid")), class_="nickname-and-peerid")),
|
||||||
|
badsharemap))
|
||||||
else:
|
else:
|
||||||
add("Corrupt shares", "none")
|
add("Corrupt shares", "none")
|
||||||
|
|
||||||
add("Wrong Shares", cr.get_share_counter_wrong())
|
add("Wrong Shares", str(cr.get_share_counter_wrong()))
|
||||||
|
|
||||||
sharemap_data = []
|
sharemap_data = []
|
||||||
shares_on_server = dictutil.DictOfSets()
|
shares_on_server = dictutil.DictOfSets()
|
||||||
|
|
||||||
# FIXME: The two tables below contain nickname-and-nodeid table column markup which is duplicated with each other, introducer.xhtml, and deep-check-results.xhtml. All of these (and any other presentations of nickname-and-nodeid) should be combined.
|
# FIXME: The two tables below contain nickname-and-nodeid
|
||||||
|
# table column markup which is duplicated with each other,
|
||||||
|
# introducer.xhtml, and deep-check-results.xhtml. All of these
|
||||||
|
# (and any other presentations of nickname-and-nodeid) should be combined.
|
||||||
|
|
||||||
for shareid in sorted(cr.get_sharemap().keys()):
|
for shareid in sorted(cr.get_sharemap().keys()):
|
||||||
servers = sorted(cr.get_sharemap()[shareid],
|
servers = sorted(cr.get_sharemap()[shareid],
|
||||||
@ -119,19 +153,20 @@ class ResultsBase(object):
|
|||||||
shares_on_server.add(s, shareid)
|
shares_on_server.add(s, shareid)
|
||||||
shareid_s = ""
|
shareid_s = ""
|
||||||
if i == 0:
|
if i == 0:
|
||||||
shareid_s = shareid
|
shareid_s = str(shareid)
|
||||||
d = T.tr[T.td[shareid_s],
|
d = tags.tr(tags.td(shareid_s),
|
||||||
T.td[T.div(class_="nickname")[s.get_nickname()],
|
tags.td(tags.div(s.get_nickname(), class_="nickname"),
|
||||||
T.div(class_="nodeid")[T.tt[s.get_name()]]]
|
tags.div(tags.tt(s.get_name()), class_="nodeid")))
|
||||||
]
|
|
||||||
sharemap_data.append(d)
|
sharemap_data.append(d)
|
||||||
|
|
||||||
add("Good Shares (sorted in share order)",
|
add("Good Shares (sorted in share order)",
|
||||||
T.table()[T.tr[T.th["Share ID"], T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]]],
|
tags.table(tags.tr(tags.th("Share ID"),
|
||||||
sharemap_data])
|
tags.th(tags.div("Nickname"),
|
||||||
|
tags.div("Node ID", class_="nodeid"), class_="nickname-and-peerid")),
|
||||||
|
sharemap_data))
|
||||||
|
|
||||||
|
add("Recoverable Versions", str(cr.get_version_counter_recoverable()))
|
||||||
add("Recoverable Versions", cr.get_version_counter_recoverable())
|
add("Unrecoverable Versions", str(cr.get_version_counter_unrecoverable()))
|
||||||
add("Unrecoverable Versions", cr.get_version_counter_unrecoverable())
|
|
||||||
|
|
||||||
# this table is sorted by permuted order
|
# this table is sorted by permuted order
|
||||||
permuted_servers = [s
|
permuted_servers = [s
|
||||||
@ -144,20 +179,23 @@ class ResultsBase(object):
|
|||||||
for s in permuted_servers:
|
for s in permuted_servers:
|
||||||
shareids = list(shares_on_server.get(s, []))
|
shareids = list(shares_on_server.get(s, []))
|
||||||
shareids.reverse()
|
shareids.reverse()
|
||||||
shareids_s = [ T.tt[shareid, " "] for shareid in sorted(shareids) ]
|
shareids_s = [tags.tt(str(shareid), " ") for shareid in sorted(shareids)]
|
||||||
d = T.tr[T.td[T.div(class_="nickname")[s.get_nickname()],
|
|
||||||
T.div(class_="nodeid")[T.tt[s.get_name()]]],
|
d = tags.tr(tags.td(tags.div(s.get_nickname(), class_="nickname"),
|
||||||
T.td[shareids_s],
|
tags.div(tags.tt(s.get_name()), class_="nodeid")),
|
||||||
]
|
tags.td(shareids_s), )
|
||||||
servermap.append(d)
|
servermap.append(d)
|
||||||
num_shares_left -= len(shareids)
|
num_shares_left -= len(shareids)
|
||||||
if not num_shares_left:
|
if not num_shares_left:
|
||||||
break
|
break
|
||||||
add("Share Balancing (servers in permuted order)",
|
|
||||||
T.table()[T.tr[T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]], T.th["Share IDs"]],
|
|
||||||
servermap])
|
|
||||||
|
|
||||||
return T.ul[r]
|
add("Share Balancing (servers in permuted order)",
|
||||||
|
tags.table(tags.tr(tags.th(tags.div("Nickname"),
|
||||||
|
tags.div("Node ID", class_="nodeid"), class_="nickname-and-peerid"),
|
||||||
|
tags.th("Share IDs")),
|
||||||
|
servermap))
|
||||||
|
|
||||||
|
return tags.ul(r)
|
||||||
|
|
||||||
def _html(self, s):
|
def _html(self, s):
|
||||||
if isinstance(s, (str, unicode)):
|
if isinstance(s, (str, unicode)):
|
||||||
@ -165,91 +203,114 @@ class ResultsBase(object):
|
|||||||
assert isinstance(s, (list, tuple))
|
assert isinstance(s, (list, tuple))
|
||||||
return [html.escape(w) for w in s]
|
return [html.escape(w) for w in s]
|
||||||
|
|
||||||
def want_json(self, ctx):
|
def _render_si_link(self, req, storage_index):
|
||||||
output = get_arg(inevow.IRequest(ctx), "output", "").lower()
|
|
||||||
if output.lower() == "json":
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _render_si_link(self, ctx, storage_index):
|
|
||||||
si_s = base32.b2a(storage_index)
|
si_s = base32.b2a(storage_index)
|
||||||
req = inevow.IRequest(ctx)
|
|
||||||
ophandle = req.prepath[-1]
|
ophandle = req.prepath[-1]
|
||||||
target = "%s/operations/%s/%s" % (get_root(ctx), ophandle, si_s)
|
target = "%s/operations/%s/%s" % (get_root(req), ophandle, si_s)
|
||||||
output = get_arg(ctx, "output")
|
output = get_arg(req, "output")
|
||||||
if output:
|
if output:
|
||||||
target = target + "?output=%s" % output
|
target = target + "?output=%s" % output
|
||||||
return T.a(href=target)[si_s]
|
return tags.a(si_s, href=target)
|
||||||
|
|
||||||
class LiteralCheckResultsRenderer(rend.Page, ResultsBase):
|
|
||||||
docFactory = getxmlfile("literal-check-results.xhtml")
|
class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase):
|
||||||
|
|
||||||
|
formatArgument = "output"
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
self.client = client
|
"""
|
||||||
rend.Page.__init__(self, client)
|
:param allmydata.interfaces.IStatsProducer client: stats provider.
|
||||||
|
"""
|
||||||
|
super(LiteralCheckResultsRenderer, self).__init__()
|
||||||
|
self._client = client
|
||||||
|
|
||||||
def renderHTTP(self, ctx):
|
def render_HTML(self, req):
|
||||||
if self.want_json(ctx):
|
return renderElement(req, LiteralCheckResultsRendererElement())
|
||||||
return self.json(ctx)
|
|
||||||
return rend.Page.renderHTTP(self, ctx)
|
|
||||||
|
|
||||||
def json(self, ctx):
|
def render_JSON(self, req):
|
||||||
inevow.IRequest(ctx).setHeader("content-type", "text/plain")
|
req.setHeader("content-type", "text/plain")
|
||||||
data = json_check_results(None)
|
data = json_check_results(None)
|
||||||
return json.dumps(data, indent=1) + "\n"
|
return json.dumps(data, indent=1) + "\n"
|
||||||
|
|
||||||
def render_return(self, ctx, data):
|
|
||||||
req = inevow.IRequest(ctx)
|
class LiteralCheckResultsRendererElement(Element):
|
||||||
|
|
||||||
|
loader = XMLFile(FilePath(__file__).sibling("literal-check-results.xhtml"))
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(LiteralCheckResultsRendererElement, self).__init__()
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def return_to(self, req, tag):
|
||||||
return_to = get_arg(req, "return_to", None)
|
return_to = get_arg(req, "return_to", None)
|
||||||
if return_to:
|
if return_to:
|
||||||
return T.div[T.a(href=return_to)["Return to file."]]
|
return tags.div(tags.a("Return to file.", href=return_to))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class CheckerBase(object):
|
class CheckerBase(object):
|
||||||
|
|
||||||
def renderHTTP(self, ctx):
|
@renderer
|
||||||
if self.want_json(ctx):
|
def storage_index(self, req, tag):
|
||||||
return self.json(ctx)
|
return self._results.get_storage_index_string()
|
||||||
return rend.Page.renderHTTP(self, ctx)
|
|
||||||
|
|
||||||
def render_storage_index(self, ctx, data):
|
@renderer
|
||||||
return self.r.get_storage_index_string()
|
def return_to(self, req, tag):
|
||||||
|
|
||||||
def render_return(self, ctx, data):
|
|
||||||
req = inevow.IRequest(ctx)
|
|
||||||
return_to = get_arg(req, "return_to", None)
|
return_to = get_arg(req, "return_to", None)
|
||||||
if return_to:
|
if return_to:
|
||||||
return T.div[T.a(href=return_to)["Return to file/directory."]]
|
return tags.div(tags.a("Return to file/directory.", href=return_to))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
class CheckResultsRenderer(CheckerBase, rend.Page, ResultsBase):
|
|
||||||
docFactory = getxmlfile("check-results.xhtml")
|
class CheckResultsRenderer(MultiFormatResource):
|
||||||
|
|
||||||
|
formatArgument = "output"
|
||||||
|
|
||||||
def __init__(self, client, results):
|
def __init__(self, client, results):
|
||||||
self.client = client
|
"""
|
||||||
self.r = ICheckResults(results)
|
:param allmydata.interfaces.IStatsProducer client: stats provider.
|
||||||
rend.Page.__init__(self, results)
|
:param allmydata.interfaces.ICheckResults results: results of check/vefify operation.
|
||||||
|
"""
|
||||||
|
super(CheckResultsRenderer, self).__init__()
|
||||||
|
self._client = client
|
||||||
|
self._results = ICheckResults(results)
|
||||||
|
|
||||||
def json(self, ctx):
|
def render_HTML(self, req):
|
||||||
inevow.IRequest(ctx).setHeader("content-type", "text/plain")
|
return renderElement(req, CheckResultsRendererElement(self._client, self._results))
|
||||||
data = json_check_results(self.r)
|
|
||||||
|
def render_JSON(self, req):
|
||||||
|
req.setHeader("content-type", "text/plain")
|
||||||
|
data = json_check_results(self._results)
|
||||||
return json.dumps(data, indent=1) + "\n"
|
return json.dumps(data, indent=1) + "\n"
|
||||||
|
|
||||||
def render_summary(self, ctx, data):
|
|
||||||
|
class CheckResultsRendererElement(Element, CheckerBase, ResultsBase):
|
||||||
|
|
||||||
|
loader = XMLFile(FilePath(__file__).sibling("check-results.xhtml"))
|
||||||
|
|
||||||
|
def __init__(self, client, results):
|
||||||
|
super(CheckResultsRendererElement, self).__init__()
|
||||||
|
self._client = client
|
||||||
|
self._results = results
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def summary(self, req, tag):
|
||||||
results = []
|
results = []
|
||||||
if data.is_healthy():
|
if self._results.is_healthy():
|
||||||
results.append("Healthy")
|
results.append("Healthy")
|
||||||
elif data.is_recoverable():
|
elif self._results.is_recoverable():
|
||||||
results.append("Not Healthy!")
|
results.append("Not Healthy!")
|
||||||
else:
|
else:
|
||||||
results.append("Not Recoverable!")
|
results.append("Not Recoverable!")
|
||||||
results.append(" : ")
|
results.append(" : ")
|
||||||
results.append(self._html(data.get_summary()))
|
results.append(self._html(self._results.get_summary()))
|
||||||
return ctx.tag[results]
|
return tag(results)
|
||||||
|
|
||||||
def render_repair(self, ctx, data):
|
@renderer
|
||||||
if data.is_healthy():
|
def repair(self, req, tag):
|
||||||
|
if self._results.is_healthy():
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
#repair = T.form(action=".", method="post",
|
#repair = T.form(action=".", method="post",
|
||||||
# enctype="multipart/form-data")[
|
# enctype="multipart/form-data")[
|
||||||
# T.fieldset[
|
# T.fieldset[
|
||||||
@ -258,30 +319,52 @@ class CheckResultsRenderer(CheckerBase, rend.Page, ResultsBase):
|
|||||||
# T.input(type="submit", value="Repair"),
|
# T.input(type="submit", value="Repair"),
|
||||||
# ]]
|
# ]]
|
||||||
#return ctx.tag[repair]
|
#return ctx.tag[repair]
|
||||||
|
|
||||||
return "" # repair button disabled until we make it work correctly,
|
return "" # repair button disabled until we make it work correctly,
|
||||||
# see #622 for details
|
# see #622 for details
|
||||||
|
|
||||||
def render_results(self, ctx, data):
|
@renderer
|
||||||
cr = self._render_results(ctx, data)
|
def results(self, req, tag):
|
||||||
return ctx.tag[cr]
|
cr = self._render_results(req, self._results)
|
||||||
|
return tag(cr)
|
||||||
|
|
||||||
class CheckAndRepairResultsRenderer(CheckerBase, rend.Page, ResultsBase):
|
class CheckAndRepairResultsRenderer(MultiFormatResource):
|
||||||
docFactory = getxmlfile("check-and-repair-results.xhtml")
|
|
||||||
|
formatArgument = "output"
|
||||||
|
|
||||||
def __init__(self, client, results):
|
def __init__(self, client, results):
|
||||||
self.client = client
|
"""
|
||||||
self.r = None
|
:param allmydata.interfaces.IStatsProducer client: stats provider.
|
||||||
|
:param allmydata.interfaces.ICheckResults results: check/verify results.
|
||||||
|
"""
|
||||||
|
super(CheckAndRepairResultsRenderer, self).__init__()
|
||||||
|
self._client = client
|
||||||
|
self._results = None
|
||||||
if results:
|
if results:
|
||||||
self.r = ICheckAndRepairResults(results)
|
self._results = ICheckAndRepairResults(results)
|
||||||
rend.Page.__init__(self, results)
|
|
||||||
|
|
||||||
def json(self, ctx):
|
def render_HTML(self, req):
|
||||||
inevow.IRequest(ctx).setHeader("content-type", "text/plain")
|
elem = CheckAndRepairResultsRendererElement(self._client, self._results)
|
||||||
data = json_check_and_repair_results(self.r)
|
return renderElement(req, elem)
|
||||||
|
|
||||||
|
def render_JSON(self, req):
|
||||||
|
req.setHeader("content-type", "text/plain")
|
||||||
|
data = json_check_and_repair_results(self._results)
|
||||||
return json.dumps(data, indent=1) + "\n"
|
return json.dumps(data, indent=1) + "\n"
|
||||||
|
|
||||||
def render_summary(self, ctx, data):
|
|
||||||
cr = data.get_post_repair_results()
|
class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase):
|
||||||
|
|
||||||
|
loader = XMLFile(FilePath(__file__).sibling("check-and-repair-results.xhtml"))
|
||||||
|
|
||||||
|
def __init__(self, client, results):
|
||||||
|
super(CheckAndRepairResultsRendererElement, self).__init__()
|
||||||
|
self._client = client
|
||||||
|
self._results = results
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def summary(self, req, tag):
|
||||||
|
cr = self._results.get_post_repair_results()
|
||||||
results = []
|
results = []
|
||||||
if cr.is_healthy():
|
if cr.is_healthy():
|
||||||
results.append("Healthy")
|
results.append("Healthy")
|
||||||
@ -291,35 +374,44 @@ class CheckAndRepairResultsRenderer(CheckerBase, rend.Page, ResultsBase):
|
|||||||
results.append("Not Recoverable!")
|
results.append("Not Recoverable!")
|
||||||
results.append(" : ")
|
results.append(" : ")
|
||||||
results.append(self._html(cr.get_summary()))
|
results.append(self._html(cr.get_summary()))
|
||||||
return ctx.tag[results]
|
return tag(results)
|
||||||
|
|
||||||
def render_repair_results(self, ctx, data):
|
@renderer
|
||||||
if data.get_repair_attempted():
|
def repair_results(self, req, tag):
|
||||||
if data.get_repair_successful():
|
if self._results.get_repair_attempted():
|
||||||
return ctx.tag["Repair successful"]
|
if self._results.get_repair_successful():
|
||||||
|
return tag("Repair successful")
|
||||||
else:
|
else:
|
||||||
return ctx.tag["Repair unsuccessful"]
|
return tag("Repair unsuccessful")
|
||||||
return ctx.tag["No repair necessary"]
|
return tag("No repair necessary")
|
||||||
|
|
||||||
def render_post_repair_results(self, ctx, data):
|
@renderer
|
||||||
cr = self._render_results(ctx, data.get_post_repair_results())
|
def post_repair_results(self, req, tag):
|
||||||
return ctx.tag[T.div["Post-Repair Checker Results:"], cr]
|
cr = self._render_results(req, self._results.get_post_repair_results())
|
||||||
|
return tag(tags.div("Post-Repair Checker Results:"), cr)
|
||||||
|
|
||||||
def render_maybe_pre_repair_results(self, ctx, data):
|
@renderer
|
||||||
if data.get_repair_attempted():
|
def maybe_pre_repair_results(self, req, tag):
|
||||||
cr = self._render_results(ctx, data.get_pre_repair_results())
|
if self._results.get_repair_attempted():
|
||||||
return ctx.tag[T.div["Pre-Repair Checker Results:"], cr]
|
cr = self._render_results(req, self._results.get_pre_repair_results())
|
||||||
|
return tag(tags.div("Pre-Repair Checker Results:"), cr)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin):
|
class DeepCheckResultsRenderer(MultiFormatResource):
|
||||||
docFactory = getxmlfile("deep-check-results.xhtml")
|
|
||||||
|
formatArgument = "output"
|
||||||
|
|
||||||
def __init__(self, client, monitor):
|
def __init__(self, client, monitor):
|
||||||
self.client = client
|
"""
|
||||||
|
:param allmydata.interfaces.IStatsProducer client: stats provider.
|
||||||
|
:param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider.
|
||||||
|
"""
|
||||||
|
super(DeepCheckResultsRenderer, self).__init__()
|
||||||
|
self._client = client
|
||||||
self.monitor = monitor
|
self.monitor = monitor
|
||||||
|
|
||||||
def childFactory(self, ctx, name):
|
def getChild(self, name, req):
|
||||||
if not name:
|
if not name:
|
||||||
return self
|
return self
|
||||||
# /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
|
# /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
|
||||||
@ -327,19 +419,18 @@ class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin):
|
|||||||
si = base32.a2b(name)
|
si = base32.a2b(name)
|
||||||
r = self.monitor.get_status()
|
r = self.monitor.get_status()
|
||||||
try:
|
try:
|
||||||
return CheckResultsRenderer(self.client,
|
return CheckResultsRenderer(self._client,
|
||||||
r.get_results_for_storage_index(si))
|
r.get_results_for_storage_index(si))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise WebError("No detailed results for SI %s" % html.escape(name),
|
raise WebError("No detailed results for SI %s" % html.escape(name),
|
||||||
http.NOT_FOUND)
|
http.NOT_FOUND)
|
||||||
|
|
||||||
def renderHTTP(self, ctx):
|
def render_HTML(self, req):
|
||||||
if self.want_json(ctx):
|
elem = DeepCheckResultsRendererElement(self.monitor)
|
||||||
return self.json(ctx)
|
return renderElement(req, elem)
|
||||||
return rend.Page.renderHTTP(self, ctx)
|
|
||||||
|
|
||||||
def json(self, ctx):
|
def render_JSON(self, req):
|
||||||
inevow.IRequest(ctx).setHeader("content-type", "text/plain")
|
req.setHeader("content-type", "text/plain")
|
||||||
data = {}
|
data = {}
|
||||||
data["finished"] = self.monitor.is_finished()
|
data["finished"] = self.monitor.is_finished()
|
||||||
res = self.monitor.get_status()
|
res = self.monitor.get_status()
|
||||||
@ -361,116 +452,170 @@ class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin):
|
|||||||
data["stats"] = res.get_stats()
|
data["stats"] = res.get_stats()
|
||||||
return json.dumps(data, indent=1) + "\n"
|
return json.dumps(data, indent=1) + "\n"
|
||||||
|
|
||||||
def render_root_storage_index(self, ctx, data):
|
|
||||||
|
class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin):
|
||||||
|
|
||||||
|
loader = XMLFile(FilePath(__file__).sibling("deep-check-results.xhtml"))
|
||||||
|
|
||||||
|
def __init__(self, monitor):
|
||||||
|
super(DeepCheckResultsRendererElement, self).__init__()
|
||||||
|
self.monitor = monitor
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def root_storage_index(self, req, tag):
|
||||||
|
if not self.monitor.get_status():
|
||||||
|
return ""
|
||||||
return self.monitor.get_status().get_root_storage_index_string()
|
return self.monitor.get_status().get_root_storage_index_string()
|
||||||
|
|
||||||
def data_objects_checked(self, ctx, data):
|
def _get_monitor_counter(self, name):
|
||||||
return self.monitor.get_status().get_counters()["count-objects-checked"]
|
if not self.monitor.get_status():
|
||||||
def data_objects_healthy(self, ctx, data):
|
return ""
|
||||||
return self.monitor.get_status().get_counters()["count-objects-healthy"]
|
return str(self.monitor.get_status().get_counters().get(name))
|
||||||
def data_objects_unhealthy(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_counters()["count-objects-unhealthy"]
|
|
||||||
def data_objects_unrecoverable(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_counters()["count-objects-unrecoverable"]
|
|
||||||
|
|
||||||
def data_count_corrupt_shares(self, ctx, data):
|
@renderer
|
||||||
return self.monitor.get_status().get_counters()["count-corrupt-shares"]
|
def objects_checked(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-objects-checked")
|
||||||
|
|
||||||
def render_problems_p(self, ctx, data):
|
@renderer
|
||||||
c = self.monitor.get_status().get_counters()
|
def objects_healthy(self, req, tag):
|
||||||
if c["count-objects-unhealthy"]:
|
return self._get_monitor_counter("count-objects-healthy")
|
||||||
return ctx.tag
|
|
||||||
|
@renderer
|
||||||
|
def objects_unhealthy(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-objects-unhealthy")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def objects_unrecoverable(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-objects-unrecoverable")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def count_corrupt_shares(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-corrupt-shares")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def problems_p(self, req, tag):
|
||||||
|
if self._get_monitor_counter("count-objects-unhealthy"):
|
||||||
|
return tag
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def data_problems(self, ctx, data):
|
@renderer
|
||||||
|
def problems(self, req, tag):
|
||||||
all_objects = self.monitor.get_status().get_all_results()
|
all_objects = self.monitor.get_status().get_all_results()
|
||||||
|
problems = []
|
||||||
|
|
||||||
for path in sorted(all_objects.keys()):
|
for path in sorted(all_objects.keys()):
|
||||||
cr = all_objects[path]
|
cr = all_objects[path]
|
||||||
assert ICheckResults.providedBy(cr)
|
assert ICheckResults.providedBy(cr)
|
||||||
if not cr.is_healthy():
|
if not cr.is_healthy():
|
||||||
yield path, cr
|
|
||||||
|
|
||||||
def render_problem(self, ctx, data):
|
|
||||||
path, cr = data
|
|
||||||
summary_text = ""
|
summary_text = ""
|
||||||
summary = cr.get_summary()
|
summary = cr.get_summary()
|
||||||
if summary:
|
if summary:
|
||||||
summary_text = ": " + summary
|
summary_text = ": " + summary
|
||||||
summary_text += " [SI: %s]" % cr.get_storage_index_string()
|
summary_text += " [SI: %s]" % cr.get_storage_index_string()
|
||||||
return ctx.tag[self._join_pathstring(path), self._html(summary_text)]
|
problems.append({
|
||||||
|
# Not sure self._join_pathstring(path) is the
|
||||||
|
# right thing to use here.
|
||||||
|
"problem": self._join_pathstring(path) + self._html(summary_text),
|
||||||
|
})
|
||||||
|
|
||||||
|
return SlotsSequenceElement(tag, problems)
|
||||||
|
|
||||||
def render_servers_with_corrupt_shares_p(self, ctx, data):
|
@renderer
|
||||||
if self.monitor.get_status().get_counters()["count-corrupt-shares"]:
|
def servers_with_corrupt_shares_p(self, req, tag):
|
||||||
return ctx.tag
|
if self._get_monitor_counter("count-corrupt-shares"):
|
||||||
|
return tag
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def data_servers_with_corrupt_shares(self, ctx, data):
|
@renderer
|
||||||
|
def servers_with_corrupt_shares(self, req, tag):
|
||||||
servers = [s
|
servers = [s
|
||||||
for (s, storage_index, sharenum)
|
for (s, storage_index, sharenum)
|
||||||
in self.monitor.get_status().get_corrupt_shares()]
|
in self.monitor.get_status().get_corrupt_shares()]
|
||||||
servers.sort(key=lambda s: s.get_longname())
|
servers.sort(key=lambda s: s.get_longname())
|
||||||
return servers
|
|
||||||
|
|
||||||
def render_server_problem(self, ctx, server):
|
problems = []
|
||||||
data = [server.get_name()]
|
|
||||||
|
for server in servers:
|
||||||
|
name = [server.get_name()]
|
||||||
nickname = server.get_nickname()
|
nickname = server.get_nickname()
|
||||||
if nickname:
|
if nickname:
|
||||||
data.append(" (%s)" % self._html(nickname))
|
name.append(" (%s)" % self._html(nickname))
|
||||||
return ctx.tag[data]
|
problems.append({"problem": name})
|
||||||
|
|
||||||
|
return SlotsSequenceElement(tag, problems)
|
||||||
|
|
||||||
def render_corrupt_shares_p(self, ctx, data):
|
@renderer
|
||||||
if self.monitor.get_status().get_counters()["count-corrupt-shares"]:
|
def corrupt_shares_p(self, req, tag):
|
||||||
return ctx.tag
|
if self._get_monitor_counter("count-corrupt-shares"):
|
||||||
|
return tag
|
||||||
return ""
|
return ""
|
||||||
def data_corrupt_shares(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_corrupt_shares()
|
|
||||||
def render_share_problem(self, ctx, data):
|
|
||||||
server, storage_index, sharenum = data
|
|
||||||
nickname = server.get_nickname()
|
|
||||||
ctx.fillSlots("serverid", server.get_name())
|
|
||||||
if nickname:
|
|
||||||
ctx.fillSlots("nickname", self._html(nickname))
|
|
||||||
ctx.fillSlots("si", self._render_si_link(ctx, storage_index))
|
|
||||||
ctx.fillSlots("shnum", str(sharenum))
|
|
||||||
return ctx.tag
|
|
||||||
|
|
||||||
def render_return(self, ctx, data):
|
@renderer
|
||||||
req = inevow.IRequest(ctx)
|
def corrupt_shares(self, req, tag):
|
||||||
|
shares = self.monitor.get_status().get_corrupt_shares()
|
||||||
|
problems = []
|
||||||
|
|
||||||
|
for share in shares:
|
||||||
|
server, storage_index, sharenum = share
|
||||||
|
nickname = server.get_nickname()
|
||||||
|
problem = {
|
||||||
|
"serverid": server.get_name(),
|
||||||
|
"nickname": self._html(nickname),
|
||||||
|
"si": self._render_si_link(req, storage_index),
|
||||||
|
"shnum": str(sharenum),
|
||||||
|
}
|
||||||
|
problems.append(problem)
|
||||||
|
|
||||||
|
return SlotsSequenceElement(tag, problems)
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def return_to(self, req, tag):
|
||||||
return_to = get_arg(req, "return_to", None)
|
return_to = get_arg(req, "return_to", None)
|
||||||
if return_to:
|
if return_to:
|
||||||
return T.div[T.a(href=return_to)["Return to file/directory."]]
|
return tags.div(tags.a("Return to file/directory.", href=return_to))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def data_all_objects(self, ctx, data):
|
@renderer
|
||||||
r = self.monitor.get_status().get_all_results()
|
def all_objects(self, req, tag):
|
||||||
for path in sorted(r.keys()):
|
results = self.monitor.get_status().get_all_results()
|
||||||
yield (path, r[path])
|
objects = []
|
||||||
|
|
||||||
def render_object(self, ctx, data):
|
for path in sorted(results.keys()):
|
||||||
path, r = data
|
result = results.get(path)
|
||||||
ctx.fillSlots("path", self._join_pathstring(path))
|
storage_index = result.get_storage_index()
|
||||||
ctx.fillSlots("healthy", str(r.is_healthy()))
|
object = {
|
||||||
ctx.fillSlots("recoverable", str(r.is_recoverable()))
|
"path": self._join_pathstring(path),
|
||||||
storage_index = r.get_storage_index()
|
"healthy": str(result.is_healthy()),
|
||||||
ctx.fillSlots("storage_index", self._render_si_link(ctx, storage_index))
|
"recoverable": str(result.is_recoverable()),
|
||||||
ctx.fillSlots("summary", self._html(r.get_summary()))
|
"storage_index": self._render_si_link(req, storage_index),
|
||||||
return ctx.tag
|
"summary": self._html(result.get_summary()),
|
||||||
|
}
|
||||||
|
objects.append(object)
|
||||||
|
|
||||||
def render_runtime(self, ctx, data):
|
return SlotsSequenceElement(tag, objects)
|
||||||
req = inevow.IRequest(ctx)
|
|
||||||
|
@renderer
|
||||||
|
def runtime(self, req, tag):
|
||||||
|
runtime = 'unknown'
|
||||||
|
if hasattr(req, 'processing_started_timestamp'):
|
||||||
runtime = time.time() - req.processing_started_timestamp
|
runtime = time.time() - req.processing_started_timestamp
|
||||||
return ctx.tag["runtime: %s seconds" % runtime]
|
return tag("runtime: %s seconds" % runtime)
|
||||||
|
|
||||||
class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin):
|
|
||||||
docFactory = getxmlfile("deep-check-and-repair-results.xhtml")
|
class DeepCheckAndRepairResultsRenderer(MultiFormatResource):
|
||||||
|
|
||||||
|
formatArgument = "output"
|
||||||
|
|
||||||
def __init__(self, client, monitor):
|
def __init__(self, client, monitor):
|
||||||
self.client = client
|
"""
|
||||||
|
:param allmydata.interfaces.IStatsProducer client: stats provider.
|
||||||
|
:param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider.
|
||||||
|
"""
|
||||||
|
super(DeepCheckAndRepairResultsRenderer, self).__init__()
|
||||||
|
self._client = client
|
||||||
self.monitor = monitor
|
self.monitor = monitor
|
||||||
|
|
||||||
def childFactory(self, ctx, name):
|
def getChild(self, name, req):
|
||||||
if not name:
|
if not name:
|
||||||
return self
|
return self
|
||||||
# /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
|
# /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
|
||||||
@ -479,18 +624,17 @@ class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin):
|
|||||||
s = self.monitor.get_status()
|
s = self.monitor.get_status()
|
||||||
try:
|
try:
|
||||||
results = s.get_results_for_storage_index(si)
|
results = s.get_results_for_storage_index(si)
|
||||||
return CheckAndRepairResultsRenderer(self.client, results)
|
return CheckAndRepairResultsRenderer(self._client, results)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise WebError("No detailed results for SI %s" % html.escape(name),
|
raise WebError("No detailed results for SI %s" % html.escape(name),
|
||||||
http.NOT_FOUND)
|
http.NOT_FOUND)
|
||||||
|
|
||||||
def renderHTTP(self, ctx):
|
def render_HTML(self, req):
|
||||||
if self.want_json(ctx):
|
elem = DeepCheckAndRepairResultsRendererElement(self.monitor)
|
||||||
return self.json(ctx)
|
return renderElement(req, elem)
|
||||||
return rend.Page.renderHTTP(self, ctx)
|
|
||||||
|
|
||||||
def json(self, ctx):
|
def render_JSON(self, req):
|
||||||
inevow.IRequest(ctx).setHeader("content-type", "text/plain")
|
req.setHeader("content-type", "text/plain")
|
||||||
res = self.monitor.get_status()
|
res = self.monitor.get_status()
|
||||||
data = {}
|
data = {}
|
||||||
data["finished"] = self.monitor.is_finished()
|
data["finished"] = self.monitor.is_finished()
|
||||||
@ -531,119 +675,132 @@ class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin):
|
|||||||
data["stats"] = res.get_stats()
|
data["stats"] = res.get_stats()
|
||||||
return json.dumps(data, indent=1) + "\n"
|
return json.dumps(data, indent=1) + "\n"
|
||||||
|
|
||||||
def render_root_storage_index(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_root_storage_index_string()
|
|
||||||
|
|
||||||
def data_objects_checked(self, ctx, data):
|
class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement):
|
||||||
return self.monitor.get_status().get_counters()["count-objects-checked"]
|
"""
|
||||||
|
The page generated here has several elements common to "deep check
|
||||||
|
results" page; hence the code reuse.
|
||||||
|
"""
|
||||||
|
|
||||||
def data_objects_healthy(self, ctx, data):
|
loader = XMLFile(FilePath(__file__).sibling("deep-check-and-repair-results.xhtml"))
|
||||||
return self.monitor.get_status().get_counters()["count-objects-healthy-pre-repair"]
|
|
||||||
def data_objects_unhealthy(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_counters()["count-objects-unhealthy-pre-repair"]
|
|
||||||
def data_corrupt_shares(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"]
|
|
||||||
|
|
||||||
def data_repairs_attempted(self, ctx, data):
|
def __init__(self, monitor):
|
||||||
return self.monitor.get_status().get_counters()["count-repairs-attempted"]
|
super(DeepCheckAndRepairResultsRendererElement, self).__init__(monitor)
|
||||||
def data_repairs_successful(self, ctx, data):
|
self.monitor = monitor
|
||||||
return self.monitor.get_status().get_counters()["count-repairs-successful"]
|
|
||||||
def data_repairs_unsuccessful(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_counters()["count-repairs-unsuccessful"]
|
|
||||||
|
|
||||||
def data_objects_healthy_post(self, ctx, data):
|
@renderer
|
||||||
return self.monitor.get_status().get_counters()["count-objects-healthy-post-repair"]
|
def objects_healthy(self, req, tag):
|
||||||
def data_objects_unhealthy_post(self, ctx, data):
|
return self._get_monitor_counter("count-objects-healthy-pre-repair")
|
||||||
return self.monitor.get_status().get_counters()["count-objects-unhealthy-post-repair"]
|
|
||||||
def data_corrupt_shares_post(self, ctx, data):
|
|
||||||
return self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"]
|
|
||||||
|
|
||||||
def render_pre_repair_problems_p(self, ctx, data):
|
@renderer
|
||||||
c = self.monitor.get_status().get_counters()
|
def objects_unhealthy(self, req, tag):
|
||||||
if c["count-objects-unhealthy-pre-repair"]:
|
return self._get_monitor_counter("count-objects-unhealthy-pre-repair")
|
||||||
return ctx.tag
|
|
||||||
|
@renderer
|
||||||
|
def corrupt_shares(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-corrupt-shares-pre-repair")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def repairs_attempted(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-repairs-attempted")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def repairs_successful(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-repairs-successful")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def repairs_unsuccessful(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-repairs-unsuccessful")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def objects_healthy_post(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-objects-healthy-post-repair")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def objects_unhealthy_post(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-objects-unhealthy-post-repair")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def corrupt_shares_post(self, req, tag):
|
||||||
|
return self._get_monitor_counter("count-corrupt-shares-post-repair")
|
||||||
|
|
||||||
|
@renderer
|
||||||
|
def pre_repair_problems_p(self, req, tag):
|
||||||
|
if self._get_monitor_counter("count-objects-unhealthy-pre-repair"):
|
||||||
|
return tag
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def data_pre_repair_problems(self, ctx, data):
|
@renderer
|
||||||
|
def pre_repair_problems(self, req, tag):
|
||||||
all_objects = self.monitor.get_status().get_all_results()
|
all_objects = self.monitor.get_status().get_all_results()
|
||||||
|
problems = []
|
||||||
|
|
||||||
for path in sorted(all_objects.keys()):
|
for path in sorted(all_objects.keys()):
|
||||||
r = all_objects[path]
|
r = all_objects[path]
|
||||||
assert ICheckAndRepairResults.providedBy(r)
|
assert ICheckAndRepairResults.providedBy(r)
|
||||||
cr = r.get_pre_repair_results()
|
cr = r.get_pre_repair_results()
|
||||||
if not cr.is_healthy():
|
if not cr.is_healthy():
|
||||||
yield path, cr
|
problem = self._join_pathstring(path), ": ", self._html(cr.get_summary())
|
||||||
|
problems.append({"problem": problem})
|
||||||
|
|
||||||
def render_problem(self, ctx, data):
|
return SlotsSequenceElement(tag, problems)
|
||||||
path, cr = data
|
|
||||||
return ctx.tag[self._join_pathstring(path), ": ",
|
|
||||||
self._html(cr.get_summary())]
|
|
||||||
|
|
||||||
def render_post_repair_problems_p(self, ctx, data):
|
@renderer
|
||||||
c = self.monitor.get_status().get_counters()
|
def post_repair_problems_p(self, req, tag):
|
||||||
if (c["count-objects-unhealthy-post-repair"]
|
if (self._get_monitor_counter("count-objects-unhealthy-post-repair")
|
||||||
or c["count-corrupt-shares-post-repair"]):
|
or self._get_monitor_counter("count-corrupt-shares-post-repair")):
|
||||||
return ctx.tag
|
return tag
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def data_post_repair_problems(self, ctx, data):
|
@renderer
|
||||||
|
def post_repair_problems(self, req, tag):
|
||||||
all_objects = self.monitor.get_status().get_all_results()
|
all_objects = self.monitor.get_status().get_all_results()
|
||||||
|
problems = []
|
||||||
|
|
||||||
for path in sorted(all_objects.keys()):
|
for path in sorted(all_objects.keys()):
|
||||||
r = all_objects[path]
|
r = all_objects[path]
|
||||||
assert ICheckAndRepairResults.providedBy(r)
|
assert ICheckAndRepairResults.providedBy(r)
|
||||||
cr = r.get_post_repair_results()
|
cr = r.get_post_repair_results()
|
||||||
if not cr.is_healthy():
|
if not cr.is_healthy():
|
||||||
yield path, cr
|
problem = self._join_pathstring(path), ": ", self._html(cr.get_summary())
|
||||||
|
problems.append({"problem": problem})
|
||||||
|
|
||||||
def render_servers_with_corrupt_shares_p(self, ctx, data):
|
return SlotsSequenceElement(tag, problems)
|
||||||
if self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"]:
|
|
||||||
return ctx.tag
|
|
||||||
return ""
|
|
||||||
def data_servers_with_corrupt_shares(self, ctx, data):
|
|
||||||
return [] # TODO
|
|
||||||
def render_server_problem(self, ctx, data):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@renderer
|
||||||
def render_remaining_corrupt_shares_p(self, ctx, data):
|
def remaining_corrupt_shares_p(self, req, tag):
|
||||||
if self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"]:
|
if self._get_monitor_counter("count-corrupt-shares-post-repair"):
|
||||||
return ctx.tag
|
return tag
|
||||||
return ""
|
|
||||||
def data_post_repair_corrupt_shares(self, ctx, data):
|
|
||||||
return [] # TODO
|
|
||||||
|
|
||||||
def render_share_problem(self, ctx, data):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def render_return(self, ctx, data):
|
|
||||||
req = inevow.IRequest(ctx)
|
|
||||||
return_to = get_arg(req, "return_to", None)
|
|
||||||
if return_to:
|
|
||||||
return T.div[T.a(href=return_to)["Return to file/directory."]]
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def data_all_objects(self, ctx, data):
|
@renderer
|
||||||
r = self.monitor.get_status().get_all_results()
|
def post_repair_corrupt_shares(self, req, tag):
|
||||||
for path in sorted(r.keys()):
|
# TODO: this was not implemented before porting to
|
||||||
yield (path, r[path])
|
# twisted.web.template; leaving it as such.
|
||||||
|
#
|
||||||
|
# https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3371
|
||||||
|
corrupt = [{"share":"unimplemented"}]
|
||||||
|
return SlotsSequenceElement(tag, corrupt)
|
||||||
|
|
||||||
def render_object(self, ctx, data):
|
@renderer
|
||||||
path, r = data
|
def all_objects(self, req, tag):
|
||||||
ctx.fillSlots("path", self._join_pathstring(path))
|
results = {}
|
||||||
ctx.fillSlots("healthy_pre_repair",
|
if self.monitor.get_status():
|
||||||
str(r.get_pre_repair_results().is_healthy()))
|
results = self.monitor.get_status().get_all_results()
|
||||||
ctx.fillSlots("recoverable_pre_repair",
|
objects = []
|
||||||
str(r.get_pre_repair_results().is_recoverable()))
|
|
||||||
ctx.fillSlots("healthy_post_repair",
|
for path in sorted(results.keys()):
|
||||||
str(r.get_post_repair_results().is_healthy()))
|
result = results[path]
|
||||||
storage_index = r.get_storage_index()
|
storage_index = result.get_storage_index()
|
||||||
ctx.fillSlots("storage_index",
|
obj = {
|
||||||
self._render_si_link(ctx, storage_index))
|
"path": self._join_pathstring(path),
|
||||||
ctx.fillSlots("summary",
|
"healthy_pre_repair": str(result.get_pre_repair_results().is_healthy()),
|
||||||
self._html(r.get_pre_repair_results().get_summary()))
|
"recoverable_pre_repair": str(result.get_pre_repair_results().is_recoverable()),
|
||||||
return ctx.tag
|
"healthy_post_repair": str(result.get_post_repair_results().is_healthy()),
|
||||||
|
"storage_index": self._render_si_link(req, storage_index),
|
||||||
|
"summary": self._html(result.get_pre_repair_results().get_summary()),
|
||||||
|
}
|
||||||
|
objects.append(obj)
|
||||||
|
|
||||||
|
return SlotsSequenceElement(tag, objects)
|
||||||
|
|
||||||
def render_runtime(self, ctx, data):
|
|
||||||
req = inevow.IRequest(ctx)
|
|
||||||
runtime = time.time() - req.processing_started_timestamp
|
|
||||||
return ctx.tag["runtime: %s seconds" % runtime]
|
|
||||||
|
@ -1,95 +1,106 @@
|
|||||||
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
|
<html xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
|
||||||
<head>
|
<head>
|
||||||
<title>Tahoe-LAFS - Deep Check Results</title>
|
<title>Tahoe-LAFS - Deep Check Results</title>
|
||||||
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
||||||
<link href="/icon.png" rel="shortcut icon" />
|
<link href="/icon.png" rel="shortcut icon" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta n:render="refresh" />
|
<meta t:render="refresh" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1>Deep-Check-And-Repair Results for root
|
<h1>Deep-Check-And-Repair Results for root
|
||||||
SI=<span n:render="root_storage_index" /></h1>
|
SI=<span t:render="root_storage_index" /></h1>
|
||||||
|
|
||||||
<h2 n:render="reload" />
|
<h2 t:render="reload" />
|
||||||
|
|
||||||
<p>Counters:</p>
|
<p>Counters:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Objects Checked: <span n:render="data" n:data="objects_checked" /></li>
|
<li>Objects Checked: <span><t:transparent t:render="objects_checked" /></span></li>
|
||||||
|
|
||||||
<li>Objects Healthy (before repair): <span n:render="data" n:data="objects_healthy" /></li>
|
<li>Objects Healthy (before repair): <span><t:transparent t:render="objects_healthy" /></span></li>
|
||||||
<li>Objects Unhealthy (before repair): <span n:render="data" n:data="objects_unhealthy" /></li>
|
<li>Objects Unhealthy (before repair): <span><t:transparent t:render="objects_unhealthy" /></span></li>
|
||||||
<li>Corrupt Shares (before repair): <span n:render="data" n:data="corrupt_shares" /></li>
|
<li>Corrupt Shares (before repair): <span><t:transparent t:render="corrupt_shares" /></span></li>
|
||||||
|
|
||||||
<li>Repairs Attempted: <span n:render="data" n:data="repairs_attempted" /></li>
|
<li>Repairs Attempted: <span><t:transparent t:render="repairs_attempted" /></span></li>
|
||||||
<li>Repairs Successful: <span n:render="data" n:data="repairs_successful" /></li>
|
<li>Repairs Successful: <span><t:transparent t:render="repairs_successful" /></span></li>
|
||||||
<li>Repairs Unsuccessful: <span n:render="data" n:data="repairs_unsuccessful" /></li>
|
<li>Repairs Unsuccessful: <span><t:transparent t:render="repairs_unsuccessful" /></span></li>
|
||||||
|
|
||||||
<li>Objects Healthy (after repair): <span n:render="data" n:data="objects_healthy_post" /></li>
|
<li>Objects Healthy (after repair): <span><t:transparent t:render="objects_healthy_post" /></span></li>
|
||||||
<li>Objects Unhealthy (after repair): <span n:render="data" n:data="objects_unhealthy_post" /></li>
|
<li>Objects Unhealthy (after repair): <span><t:transparent t:render="objects_unhealthy_post" /></span></li>
|
||||||
<li>Corrupt Shares (after repair): <span n:render="data" n:data="corrupt_shares_post" /></li>
|
<li>Corrupt Shares (after repair): <span><t:transparent t:render="corrupt_shares_post" /></span></li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div n:render="pre_repair_problems_p">
|
<div t:render="pre_repair_problems_p">
|
||||||
<h2>Files/Directories That Had Problems:</h2>
|
<h2>Files/Directories That Had Problems:</h2>
|
||||||
|
|
||||||
<ul n:render="sequence" n:data="pre_repair_problems">
|
<ul t:render="pre_repair_problems">
|
||||||
<li n:pattern="item" n:render="problem"/>
|
<li t:render="item">
|
||||||
<li n:pattern="empty">None</li>
|
<t:slot name="problem" />
|
||||||
|
</li>
|
||||||
|
<li t:render="empty">None</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div n:render="post_repair_problems_p">
|
<div t:render="post_repair_problems_p">
|
||||||
<h2>Files/Directories That Still Have Problems:</h2>
|
<h2>Files/Directories That Still Have Problems:</h2>
|
||||||
<ul n:render="sequence" n:data="post_repair_problems">
|
<ul t:render="post_repair_problems">
|
||||||
<li n:pattern="item" n:render="problem"/>
|
<li t:render="item">
|
||||||
<li n:pattern="empty">None</li>
|
<t:slot name="problem" />
|
||||||
|
</li>
|
||||||
|
<li t:render="empty">None</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="servers_with_corrupt_shares_p">
|
<div t:render="servers_with_corrupt_shares_p">
|
||||||
<h2>Servers on which corrupt shares were found</h2>
|
<h2>Servers on which corrupt shares were found</h2>
|
||||||
<ul n:render="sequence" n:data="servers_with_corrupt_shares">
|
<ul t:render="servers_with_corrupt_shares">
|
||||||
<li n:pattern="item" n:render="server_problem"/>
|
<li t:render="item">
|
||||||
<li n:pattern="empty">None</li>
|
<t:slot name="problem" />
|
||||||
|
</li>
|
||||||
|
<li t:render="empty">None</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="remaining_corrupt_shares_p">
|
<div t:render="remaining_corrupt_shares_p">
|
||||||
<h2>Remaining Corrupt Shares</h2>
|
<h2>Remaining Corrupt Shares</h2>
|
||||||
<p>These shares need to be manually inspected and removed.</p>
|
<p>These shares need to be manually inspected and removed.</p>
|
||||||
<ul n:render="sequence" n:data="post_repair_corrupt_shares">
|
<ul t:render="post_repair_corrupt_shares">
|
||||||
<li n:pattern="item" n:render="share_problem"/>
|
<li t:render="item">
|
||||||
<li n:pattern="empty">None</li>
|
<t:slot name="share" />
|
||||||
|
</li>
|
||||||
|
<li t:render="empty">None</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="return" />
|
<div t:render="return_to" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<table n:render="sequence" n:data="all_objects">
|
<table t:render="all_objects">
|
||||||
<tr n:pattern="header">
|
<tr t:render="header">
|
||||||
<td>Relative Path</td>
|
<th>Relative Path</th>
|
||||||
<td>Healthy Pre-Repair</td>
|
<th>Healthy Pre-Repair</th>
|
||||||
<td>Recoverable Pre-Repair</td>
|
<th>Recoverable Pre-Repair</th>
|
||||||
<td>Healthy Post-Repair</td>
|
<th>Healthy Post-Repair</th>
|
||||||
<td>Storage Index</td>
|
<th>Storage Index</th>
|
||||||
<td>Summary</td>
|
<th>Summary</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr n:pattern="item" n:render="object">
|
<tr t:render="item">
|
||||||
<td><n:slot name="path"/></td>
|
<td><t:slot name="path"/></td>
|
||||||
<td><n:slot name="healthy_pre_repair"/></td>
|
<td><t:slot name="healthy_pre_repair"/></td>
|
||||||
<td><n:slot name="recoverable_pre_repair"/></td>
|
<td><t:slot name="recoverable_pre_repair"/></td>
|
||||||
<td><n:slot name="healthy_post_repair"/></td>
|
<td><t:slot name="healthy_post_repair"/></td>
|
||||||
<td><n:slot name="storage_index"/></td>
|
<td><t:slot name="storage_index"/></td>
|
||||||
<td><n:slot name="summary"/></td>
|
<td><t:slot name="summary"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr t:render="empty">
|
||||||
|
<td>Nothing to report yet.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="runtime" />
|
<div t:render="runtime" />
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,87 +1,93 @@
|
|||||||
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
|
<html xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
|
||||||
<head>
|
<head>
|
||||||
<title>Tahoe-LAFS - Deep Check Results</title>
|
<title>Tahoe-LAFS - Deep Check Results</title>
|
||||||
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
||||||
<link href="/icon.png" rel="shortcut icon" />
|
<link href="/icon.png" rel="shortcut icon" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta n:render="refresh" />
|
<meta t:render="refresh" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1>Deep-Check Results for root SI=<span n:render="root_storage_index" /></h1>
|
<h1>Deep-Check Results for root SI=<span t:render="root_storage_index" /></h1>
|
||||||
|
|
||||||
<h2 n:render="reload" />
|
<h2 t:render="reload" />
|
||||||
|
|
||||||
<p>Counters:</p>
|
<p>Counters:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Objects Checked: <span n:render="data" n:data="objects_checked" /></li>
|
<li>Objects Checked: <span><t:transparent t:render="objects_checked" /></span></li>
|
||||||
<li>Objects Healthy: <span n:render="data" n:data="objects_healthy" /></li>
|
<li>Objects Healthy: <span><t:transparent t:render="objects_healthy" /></span></li>
|
||||||
<li>Objects Unhealthy: <span n:render="data" n:data="objects_unhealthy" /></li>
|
<li>Objects Unhealthy: <span><t:transparent t:render="objects_unhealthy" /></span></li>
|
||||||
<li>Objects Unrecoverable: <span n:render="data" n:data="objects_unrecoverable" /></li>
|
<li>Objects Unrecoverable: <span><t:transparent t:render="objects_unrecoverable" /></span></li>
|
||||||
<li>Corrupt Shares: <span n:render="data" n:data="count_corrupt_shares" /></li>
|
<li>Corrupt Shares: <span><t:transparent t:render="count_corrupt_shares" /></span></li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div n:render="problems_p">
|
<div t:render="problems_p">
|
||||||
<h2>Files/Directories That Had Problems:</h2>
|
<h2>Files/Directories That Had Problems:</h2>
|
||||||
|
|
||||||
<ul n:render="sequence" n:data="problems">
|
<ul t:render="problems">
|
||||||
<li n:pattern="item" n:render="problem"/>
|
<li t:render="item">
|
||||||
<li n:pattern="empty">None</li>
|
<t:slot name="problem" />
|
||||||
|
</li>
|
||||||
|
<li t:render="empty">None</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div n:render="servers_with_corrupt_shares_p">
|
<div t:render="servers_with_corrupt_shares_p">
|
||||||
<h2>Servers on which corrupt shares were found</h2>
|
<h2>Servers on which corrupt shares were found</h2>
|
||||||
<ul n:render="sequence" n:data="servers_with_corrupt_shares">
|
<ul t:render="servers_with_corrupt_shares">
|
||||||
<li n:pattern="item" n:render="server_problem"/>
|
<li t:render="item">
|
||||||
<li n:pattern="empty">None</li>
|
<t:slot name="problem" />
|
||||||
|
</li>
|
||||||
|
<li t:render="empty">None</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="corrupt_shares_p">
|
<div t:render="corrupt_shares_p">
|
||||||
<h2>Corrupt Shares</h2>
|
<h2>Corrupt Shares</h2>
|
||||||
<p>If repair fails, these shares need to be manually inspected and removed.</p>
|
<p>If repair fails, these shares need to be manually inspected and removed.</p>
|
||||||
<table n:render="sequence" n:data="corrupt_shares">
|
<table t:render="corrupt_shares">
|
||||||
<tr n:pattern="header">
|
<tr t:render="header">
|
||||||
<td>Server</td>
|
<th>Server</th>
|
||||||
<td>Server Nickname</td>
|
<th>Server Nickname</th>
|
||||||
<td>Storage Index</td>
|
<th>Storage Index</th>
|
||||||
<td>Share Number</td>
|
<th>Share Number</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr n:pattern="item" n:render="share_problem">
|
<tr t:render="item">
|
||||||
<td><n:slot name="serverid"/></td>
|
<td><t:slot name="serverid"/></td>
|
||||||
<td><n:slot name="nickname"/></td>
|
<td><t:slot name="nickname"/></td>
|
||||||
<td><n:slot name="si"/></td>
|
<td><t:slot name="si"/></td>
|
||||||
<td><n:slot name="shnum"/></td>
|
<td><t:slot name="shnum"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="return" />
|
<div t:render="return_to" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2>All Results</h2>
|
<h2>All Results</h2>
|
||||||
<table n:render="sequence" n:data="all_objects">
|
<table t:render="all_objects">
|
||||||
<tr n:pattern="header">
|
<tr t:render="header">
|
||||||
<td>Relative Path</td>
|
<th>Relative Path</th>
|
||||||
<td>Healthy</td>
|
<th>Healthy</th>
|
||||||
<td>Recoverable</td>
|
<th>Recoverable</th>
|
||||||
<td>Storage Index</td>
|
<th>Storage Index</th>
|
||||||
<td>Summary</td>
|
<th>Summary</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr n:pattern="item" n:render="object">
|
<tr t:render="item">
|
||||||
<td><n:slot name="path"/></td>
|
<td><t:slot name="path"/></td>
|
||||||
<td><n:slot name="healthy"/></td>
|
<td><t:slot name="healthy"/></td>
|
||||||
<td><n:slot name="recoverable"/></td>
|
<td><t:slot name="recoverable"/></td>
|
||||||
<td><tt><n:slot name="storage_index"/></tt></td>
|
<td><tt><t:slot name="storage_index"/></tt></td>
|
||||||
<td><n:slot name="summary"/></td>
|
<td><t:slot name="summary"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr t:render="empty">
|
||||||
|
<td>Nothing to report yet.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:render="runtime" />
|
<div t:render="runtime" />
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -261,7 +261,7 @@ class MoreInfoElement(Element):
|
|||||||
@renderer
|
@renderer
|
||||||
def deep_check_form(self, req, tag):
|
def deep_check_form(self, req, tag):
|
||||||
ophandle = base32.b2a(os.urandom(16))
|
ophandle = base32.b2a(os.urandom(16))
|
||||||
deep_check = T.form(action=".", method="post",
|
deep_check = T.form(action=req.path, method="post",
|
||||||
enctype="multipart/form-data")(
|
enctype="multipart/form-data")(
|
||||||
T.fieldset(
|
T.fieldset(
|
||||||
T.input(type="hidden", name="t", value="start-deep-check"),
|
T.input(type="hidden", name="t", value="start-deep-check"),
|
||||||
@ -287,7 +287,7 @@ class MoreInfoElement(Element):
|
|||||||
@renderer
|
@renderer
|
||||||
def deep_size_form(self, req, tag):
|
def deep_size_form(self, req, tag):
|
||||||
ophandle = base32.b2a(os.urandom(16))
|
ophandle = base32.b2a(os.urandom(16))
|
||||||
deep_size = T.form(action=".", method="post",
|
deep_size = T.form(action=req.path, method="post",
|
||||||
enctype="multipart/form-data")(
|
enctype="multipart/form-data")(
|
||||||
T.fieldset(
|
T.fieldset(
|
||||||
T.input(type="hidden", name="t", value="start-deep-size"),
|
T.input(type="hidden", name="t", value="start-deep-size"),
|
||||||
@ -300,7 +300,7 @@ class MoreInfoElement(Element):
|
|||||||
@renderer
|
@renderer
|
||||||
def deep_stats_form(self, req, tag):
|
def deep_stats_form(self, req, tag):
|
||||||
ophandle = base32.b2a(os.urandom(16))
|
ophandle = base32.b2a(os.urandom(16))
|
||||||
deep_stats = T.form(action=".", method="post",
|
deep_stats = T.form(action=req.path, method="post",
|
||||||
enctype="multipart/form-data")(
|
enctype="multipart/form-data")(
|
||||||
T.fieldset(
|
T.fieldset(
|
||||||
T.input(type="hidden", name="t", value="start-deep-stats"),
|
T.input(type="hidden", name="t", value="start-deep-stats"),
|
||||||
@ -313,7 +313,7 @@ class MoreInfoElement(Element):
|
|||||||
@renderer
|
@renderer
|
||||||
def manifest_form(self, req, tag):
|
def manifest_form(self, req, tag):
|
||||||
ophandle = base32.b2a(os.urandom(16))
|
ophandle = base32.b2a(os.urandom(16))
|
||||||
manifest = T.form(action=".", method="post",
|
manifest = T.form(action=req.path, method="post",
|
||||||
enctype="multipart/form-data")(
|
enctype="multipart/form-data")(
|
||||||
T.fieldset(
|
T.fieldset(
|
||||||
T.input(type="hidden", name="t", value="start-manifest"),
|
T.input(type="hidden", name="t", value="start-manifest"),
|
||||||
|
@ -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>
|
<head>
|
||||||
<title>Tahoe-LAFS - Check Results</title>
|
<title>Tahoe-LAFS - Check Results</title>
|
||||||
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<div>Literal files are always healthy: their data is contained in the URI</div>
|
<div>Literal files are always healthy: their data is contained in the URI</div>
|
||||||
|
|
||||||
<div n:render="return" />
|
<div t:render="return_to" />
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user