mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-24 15:16:41 +00:00
Handle all the status cases
This commit is contained in:
parent
91c655298a
commit
20084506b3
@ -83,6 +83,163 @@ def pretty_progress(percent, size=10, ascii=False):
|
||||
curr = int(curr)
|
||||
return '%s%s%s' % ((block_chr * curr), part, (' ' * (size - curr - 1)))
|
||||
|
||||
OP_MAP = {
|
||||
'upload': ' put ',
|
||||
'download': ' get ',
|
||||
'retrieve': 'retr ',
|
||||
'publish': ' pub ',
|
||||
'mapupdate': 'mapup',
|
||||
'unknown': ' ??? ',
|
||||
}
|
||||
|
||||
def _render_active_upload(op):
|
||||
total = (
|
||||
op['progress-hash'] +
|
||||
op['progress-ciphertext'] +
|
||||
op['progress-encode-push']
|
||||
) / 3.0 * 100.0
|
||||
return {
|
||||
u"op_type": u" put ",
|
||||
u"total": "{:3.0f}".format(total),
|
||||
u"progress_bar": u"{}".format(pretty_progress(total, size=15)),
|
||||
u"storage-index-string": op["storage-index-string"],
|
||||
u"status": op["status"],
|
||||
}
|
||||
|
||||
def _render_active_download(op):
|
||||
return {
|
||||
u"op_type": u" get ",
|
||||
u"total": op["progress"],
|
||||
u"progress_bar": u"{}".format(pretty_progress(op['progress'] * 100.0, size=15)),
|
||||
u"storage-index-string": op["storage-index-string"],
|
||||
u"status": op["status"],
|
||||
}
|
||||
|
||||
def _render_active_generic(op):
|
||||
return {
|
||||
u"op_type": OP_MAP[op["type"]],
|
||||
u"progress_bar": u"",
|
||||
u"total": u"???",
|
||||
u"storage-index-string": op["storage-index-string"],
|
||||
u"status": op["status"],
|
||||
}
|
||||
|
||||
active_renderers = {
|
||||
"upload": _render_active_upload,
|
||||
"download": _render_active_download,
|
||||
"publish": _render_active_generic,
|
||||
"retrieve": _render_active_generic,
|
||||
"mapupdate": _render_active_generic,
|
||||
"unknown": _render_active_generic,
|
||||
}
|
||||
|
||||
|
||||
def render_active(stdout, status_data):
|
||||
active = status_data.get('active', None)
|
||||
if not active:
|
||||
print(u"No active operations.", file=stdout)
|
||||
return
|
||||
|
||||
header = u"\u2553 {:<5} \u2565 {:<26} \u2565 {:<22} \u2565 {}".format(
|
||||
"type",
|
||||
"storage index",
|
||||
"progress",
|
||||
"status message",
|
||||
)
|
||||
header_bar = u"\u255f\u2500{}\u2500\u256b\u2500{}\u2500\u256b\u2500{}\u2500\u256b\u2500{}".format(
|
||||
u'\u2500' * 5,
|
||||
u'\u2500' * 26,
|
||||
u'\u2500' * 22,
|
||||
u'\u2500' * 20,
|
||||
)
|
||||
line_template = (
|
||||
u"\u2551 {op_type} "
|
||||
u"\u2551 {storage-index-string} "
|
||||
u"\u2551 {progress_bar:15} "
|
||||
u"({total}%) "
|
||||
u"\u2551 {status}"
|
||||
)
|
||||
footer_bar = u"\u2559\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}".format(
|
||||
u'\u2500' * 5,
|
||||
u'\u2500' * 26,
|
||||
u'\u2500' * 22,
|
||||
u'\u2500' * 20,
|
||||
)
|
||||
print(u"Active operations:", file=stdout)
|
||||
print(header, file=stdout)
|
||||
print(header_bar, file=stdout)
|
||||
for op in active:
|
||||
print(line_template.format(
|
||||
**active_renderers[op["type"]](op)
|
||||
))
|
||||
print(footer_bar, file=stdout)
|
||||
|
||||
def _render_recent_generic(op):
|
||||
return {
|
||||
u"op_type": OP_MAP[op["type"]],
|
||||
u"storage-index-string": op["storage-index-string"],
|
||||
u"nice_size": abbreviate_space(op["total-size"]),
|
||||
u"status": op["status"],
|
||||
}
|
||||
|
||||
def _render_recent_mapupdate(op):
|
||||
return {
|
||||
u"op_type": u"mapup",
|
||||
u"storage-index-string": op["storage-index-string"],
|
||||
u"nice_size": op["mode"],
|
||||
u"status": op["status"],
|
||||
}
|
||||
|
||||
recent_renderers = {
|
||||
"upload": _render_recent_generic,
|
||||
"download": _render_recent_generic,
|
||||
"publish": _render_recent_generic,
|
||||
"retrieve": _render_recent_generic,
|
||||
"mapupdate": _render_recent_mapupdate,
|
||||
"unknown": _render_recent_generic,
|
||||
}
|
||||
|
||||
def render_recent(verbose, stdout, status_data):
|
||||
recent = status_data.get('recent', None)
|
||||
if not recent:
|
||||
print(u"No recent operations.", file=stdout)
|
||||
|
||||
header = u"\u2553 {:<5} \u2565 {:<26} \u2565 {:<10} \u2565 {}".format(
|
||||
"type",
|
||||
"storage index",
|
||||
"size",
|
||||
"status message",
|
||||
)
|
||||
line_template = (
|
||||
u"\u2551 {op_type} "
|
||||
u"\u2551 {storage-index-string} "
|
||||
u"\u2551 {nice_size:<10} "
|
||||
u"\u2551 {status}"
|
||||
)
|
||||
footer = u"\u2559\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}".format(
|
||||
u'\u2500' * 5,
|
||||
u'\u2500' * 26,
|
||||
u'\u2500' * 10,
|
||||
u'\u2500' * 20,
|
||||
)
|
||||
non_verbose_ops = ('upload', 'download')
|
||||
recent = [op for op in status_data['recent'] if op['type'] in non_verbose_ops]
|
||||
print(u"\nRecent operations:", file=stdout)
|
||||
if len(recent) or verbose:
|
||||
print(header, file=stdout)
|
||||
|
||||
ops_to_show = status_data['recent'] if verbose else recent
|
||||
for op in ops_to_show:
|
||||
print(line_template.format(
|
||||
**recent_renderers[op["type"]](op)
|
||||
))
|
||||
if len(recent) or verbose:
|
||||
print(footer, file=stdout)
|
||||
|
||||
skipped = len(status_data['recent']) - len(ops_to_show)
|
||||
if not verbose and skipped:
|
||||
print(u" Skipped {} non-upload/download operations; use --verbose to see".format(skipped), file=stdout)
|
||||
|
||||
|
||||
def do_status(options):
|
||||
nodedir = options["node-directory"]
|
||||
@ -125,83 +282,8 @@ def do_status(options):
|
||||
print(u" downloaded {} in {} files".format(abbreviate_space(downloaded_bytes), downloaded_files), file=options.stdout)
|
||||
print(u"", file=options.stdout)
|
||||
|
||||
if status_data.get('active', None):
|
||||
print(u"Active operations:", file=options.stdout)
|
||||
print(
|
||||
u"\u2553 {:<5} \u2565 {:<26} \u2565 {:<22} \u2565 {}".format(
|
||||
"type",
|
||||
"storage index",
|
||||
"progress",
|
||||
"status message",
|
||||
), file=options.stdout
|
||||
)
|
||||
print(u"\u255f\u2500{}\u2500\u256b\u2500{}\u2500\u256b\u2500{}\u2500\u256b\u2500{}".format(u'\u2500' * 5, u'\u2500' * 26, u'\u2500' * 22, u'\u2500' * 20), file=options.stdout)
|
||||
for op in status_data['active']:
|
||||
if 'progress-hash' in op:
|
||||
op_type = ' put '
|
||||
total = (op['progress-hash'] + op['progress-ciphertext'] + op['progress-encode-push']) / 3.0
|
||||
progress_bar = u"{}".format(pretty_progress(total * 100.0, size=15))
|
||||
else:
|
||||
op_type = ' get '
|
||||
total = op['progress']
|
||||
progress_bar = u"{}".format(pretty_progress(op['progress'] * 100.0, size=15))
|
||||
print(
|
||||
u"\u2551 {op_type} \u2551 {storage-index-string} \u2551 {progress_bar} ({total:3}%) \u2551 {status}".format(
|
||||
op_type=op_type,
|
||||
progress_bar=progress_bar,
|
||||
total=int(total * 100.0),
|
||||
**op
|
||||
), file=options.stdout
|
||||
)
|
||||
|
||||
print(u"\u2559\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}".format(u'\u2500' * 5, u'\u2500' * 26, u'\u2500' * 22, u'\u2500' * 20), file=options.stdout)
|
||||
else:
|
||||
print(u"No active operations.", file=options.stdout)
|
||||
|
||||
if status_data.get('recent', None):
|
||||
non_verbose_ops = ('upload', 'download')
|
||||
recent = [op for op in status_data['recent'] if op['type'] in non_verbose_ops]
|
||||
print(u"\nRecent operations:", file=options.stdout)
|
||||
if len(recent) or options['verbose']:
|
||||
print(
|
||||
u"\u2553 {:<5} \u2565 {:<26} \u2565 {:<10} \u2565 {}".format(
|
||||
"type",
|
||||
"storage index",
|
||||
"size",
|
||||
"status message",
|
||||
), file=options.stdout
|
||||
)
|
||||
|
||||
op_map = {
|
||||
'upload': ' put ',
|
||||
'download': ' get ',
|
||||
'retrieve': 'retr ',
|
||||
'publish': ' pub ',
|
||||
'mapupdate': 'mapup',
|
||||
}
|
||||
|
||||
ops_to_show = status_data['recent'] if options['verbose'] else recent
|
||||
for op in ops_to_show:
|
||||
op_type = op_map[op.get('type', None)]
|
||||
if op['type'] == 'mapupdate':
|
||||
nice_size = op['mode']
|
||||
else:
|
||||
nice_size = abbreviate_space(op['total-size'])
|
||||
print(
|
||||
u"\u2551 {op_type} \u2551 {storage-index-string} \u2551 {nice_size:<10} \u2551 {status}".format(
|
||||
op_type=op_type,
|
||||
nice_size=nice_size,
|
||||
**op
|
||||
), file=options.stdout
|
||||
)
|
||||
|
||||
if len(recent) or options['verbose']:
|
||||
print(u"\u2559\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}\u2500\u2568\u2500{}".format(u'\u2500' * 5, u'\u2500' * 26, u'\u2500' * 10, u'\u2500' * 20), file=options.stdout)
|
||||
skipped = len(status_data['recent']) - len(ops_to_show)
|
||||
if not options['verbose'] and skipped:
|
||||
print(u" Skipped {} non-upload/download operations; use --verbose to see".format(skipped), file=options.stdout)
|
||||
else:
|
||||
print(u"No recent operations.", file=options.stdout)
|
||||
render_active(options.stdout, status_data)
|
||||
render_recent(options['verbose'], options.stdout, status_data)
|
||||
|
||||
# open question: should we return non-zero if there were no
|
||||
# operations at all to display?
|
||||
|
@ -15,9 +15,17 @@ from allmydata.scripts.tahoe_status import _get_json_for_fragment
|
||||
from allmydata.scripts.tahoe_status import _get_json_for_cap
|
||||
from allmydata.scripts.tahoe_status import pretty_progress
|
||||
from allmydata.scripts.tahoe_status import do_status
|
||||
from allmydata.web.status import marshal_json
|
||||
|
||||
from allmydata.immutable.upload import UploadStatus
|
||||
from allmydata.immutable.downloader.status import DownloadStatus
|
||||
from allmydata.mutable.publish import PublishStatus
|
||||
from allmydata.mutable.retrieve import RetrieveStatus
|
||||
from allmydata.mutable.servermap import UpdateStatus
|
||||
|
||||
from ..no_network import GridTestMixin
|
||||
from ..common_web import do_http
|
||||
from ..status import FakeStatus
|
||||
from .common import CLITestMixin
|
||||
|
||||
|
||||
@ -135,30 +143,26 @@ class CommandStatus(unittest.TestCase):
|
||||
@mock.patch('allmydata.scripts.tahoe_status.do_http')
|
||||
@mock.patch('sys.stdout', StringIO())
|
||||
def test_simple(self, http):
|
||||
recent_items = active_items = [
|
||||
UploadStatus(),
|
||||
DownloadStatus("abcd", 12345),
|
||||
PublishStatus(),
|
||||
RetrieveStatus(),
|
||||
UpdateStatus(),
|
||||
FakeStatus(),
|
||||
]
|
||||
values = [
|
||||
StringIO(json.dumps({
|
||||
"active": [
|
||||
{
|
||||
"progress": 0.5,
|
||||
"storage-index-string": "index0",
|
||||
"status": "foo",
|
||||
},
|
||||
{
|
||||
"progress-hash": 1.0,
|
||||
"progress-ciphertext": 1.0,
|
||||
"progress-encode-push": 0.5,
|
||||
"storage-index-string": "index1",
|
||||
"status": "bar",
|
||||
}
|
||||
],
|
||||
"recent": [
|
||||
{
|
||||
"type": "download",
|
||||
"total-size": 12345,
|
||||
"storage-index-string": "index1",
|
||||
"status": "bar",
|
||||
},
|
||||
]
|
||||
"active": list(
|
||||
marshal_json(item)
|
||||
for item
|
||||
in active_items
|
||||
),
|
||||
"recent": list(
|
||||
marshal_json(item)
|
||||
for item
|
||||
in recent_items
|
||||
),
|
||||
})),
|
||||
StringIO(json.dumps({
|
||||
"counters": {
|
||||
|
16
src/allmydata/test/status.py
Normal file
16
src/allmydata/test/status.py
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
class FakeStatus(object):
|
||||
def __init__(self):
|
||||
self.status = []
|
||||
|
||||
def setServiceParent(self, p):
|
||||
pass
|
||||
|
||||
def get_status(self):
|
||||
return self.status
|
||||
|
||||
def get_storage_index(self):
|
||||
return None
|
||||
|
||||
def get_size(self):
|
||||
return None
|
@ -50,6 +50,8 @@ from ..common_web import (
|
||||
)
|
||||
from allmydata.client import _Client, SecretHolder
|
||||
from .common import unknown_rwcap, unknown_rocap, unknown_immcap, FAVICON_MARKUP
|
||||
from ..status import FakeStatus
|
||||
|
||||
# create a fake uploader/downloader, and a couple of fake dirnodes, then
|
||||
# create a webserver that works against them
|
||||
|
||||
@ -111,17 +113,6 @@ class FakeUploader(service.Service):
|
||||
return (self.helper_furl, self.helper_connected)
|
||||
|
||||
|
||||
class FakeStatus(object):
|
||||
def __init__(self):
|
||||
self.status = []
|
||||
|
||||
def setServiceParent(self, p):
|
||||
pass
|
||||
|
||||
def get_status(self):
|
||||
return self.status
|
||||
|
||||
|
||||
def create_test_queued_item(relpath_u, history=[]):
|
||||
progress = mock.Mock()
|
||||
progress.progress = 100.0
|
||||
|
@ -958,23 +958,7 @@ class MapupdateStatusPage(rend.Page, RateAndTimeMixin):
|
||||
return T.li["Per-Server Response Times: ", l]
|
||||
|
||||
|
||||
|
||||
class Status(MultiFormatPage):
|
||||
docFactory = getxmlfile("status.xhtml")
|
||||
addSlash = True
|
||||
|
||||
def __init__(self, history):
|
||||
rend.Page.__init__(self, history)
|
||||
self.history = history
|
||||
|
||||
def render_JSON(self, req):
|
||||
# modern browsers now render this instead of forcing downloads
|
||||
req.setHeader("content-type", "application/json")
|
||||
data = {}
|
||||
data["active"] = active = []
|
||||
data["recent"] = recent = []
|
||||
|
||||
def _marshal_json(s):
|
||||
def marshal_json(s):
|
||||
# common item data
|
||||
item = {
|
||||
"storage-index-string": base32.b2a_or_none(s.get_storage_index()),
|
||||
@ -1010,11 +994,27 @@ class Status(MultiFormatPage):
|
||||
|
||||
return item
|
||||
|
||||
|
||||
class Status(MultiFormatPage):
|
||||
docFactory = getxmlfile("status.xhtml")
|
||||
addSlash = True
|
||||
|
||||
def __init__(self, history):
|
||||
rend.Page.__init__(self, history)
|
||||
self.history = history
|
||||
|
||||
def render_JSON(self, req):
|
||||
# modern browsers now render this instead of forcing downloads
|
||||
req.setHeader("content-type", "application/json")
|
||||
data = {}
|
||||
data["active"] = active = []
|
||||
data["recent"] = recent = []
|
||||
|
||||
for s in self._get_active_operations():
|
||||
active.append(_marshal_json(s))
|
||||
active.append(marshal_json(s))
|
||||
|
||||
for s in self._get_recent_operations():
|
||||
recent.append(_marshal_json(s))
|
||||
recent.append(marshal_json(s))
|
||||
|
||||
return json.dumps(data, indent=1) + "\n"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user