From 7d6cc222772316f86013c34e80634b4d429125aa Mon Sep 17 00:00:00 2001 From: Tomas Tomecek Date: Sat, 28 Nov 2015 19:31:53 +0100 Subject: [PATCH] implement image info buffer Resolves #48 --- sen/docker_backend.py | 18 ++++++++++++ sen/tui/buffer.py | 14 +++++++++ sen/tui/init.py | 6 +++- sen/tui/widgets/info.py | 56 ++++++++++++++++++++++++++++++++++++ sen/tui/widgets/list/main.py | 3 ++ sen/util.py | 24 ++++++++++++++++ 6 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 sen/tui/widgets/info.py diff --git a/sen/docker_backend.py b/sen/docker_backend.py index 4916b74..d67f9e1 100644 --- a/sen/docker_backend.py +++ b/sen/docker_backend.py @@ -177,6 +177,10 @@ def short_id(self): def display_time_created(self): return humanize.naturaltime(self.created) + def display_formal_time_created(self): + # http://tools.ietf.org/html/rfc2822.html#section-3.3 + return self.created.strftime("%d %b %Y, %H:%M:%S") + def inspect(self): raise NotImplementedError() @@ -233,6 +237,20 @@ def command(self): return " ".join(cmd) return "" + @property + def size(self): + """ + Size of all layers in bytes + + :return: int + """ + return self.data["VirtualSize"] + + @property + def labels(self): + labels = self.data["Labels"] + return labels + @property def names(self): if self._names is None: diff --git a/sen/tui/buffer.py b/sen/tui/buffer.py index d49fdc7..e99cebd 100644 --- a/sen/tui/buffer.py +++ b/sen/tui/buffer.py @@ -3,6 +3,7 @@ from sen.docker_backend import DockerContainer from sen.exceptions import NotifyError from sen.tui.constants import HELP_TEXT +from sen.tui.widgets.info import ImageInfoWidget from sen.tui.widgets.list.main import MainListBox from sen.tui.widgets.list.util import get_operation_notify_widget from sen.tui.widgets.list.common import AsyncScrollableListBox, ScrollableListBox @@ -59,6 +60,19 @@ def filter(self, s): self.widget.set_body(widgets) +class ImageInfoBuffer(Buffer): + tag = "I" + + def __init__(self, docker_image, ui): + """ + :param docker_image: + :param ui: ui object so we refresh + """ + self.display_name = docker_image.short_name + self.widget = ImageInfoWidget(docker_image) + super().__init__() + + class MainListBuffer(Buffer): display_name = "Listing" tag = "M" diff --git a/sen/tui/init.py b/sen/tui/init.py index 6b7a10c..645172d 100644 --- a/sen/tui/init.py +++ b/sen/tui/init.py @@ -4,11 +4,12 @@ from sen.exceptions import NotifyError from sen.tui.commands import search, filter from sen.tui.statusbar import Footer -from sen.tui.buffer import LogsBuffer, MainListBuffer, InspectBuffer, HelpBuffer +from sen.tui.buffer import LogsBuffer, MainListBuffer, InspectBuffer, HelpBuffer, ImageInfoBuffer from sen.tui.constants import PALLETE from sen.docker_backend import DockerBackend import urwid + from sen.util import log_traceback logger = logging.getLogger(__name__) @@ -159,6 +160,9 @@ def display_and_follow_logs(self, docker_container): def inspect(self, docker_object): self.add_and_display_buffer(InspectBuffer(docker_object)) + def display_image_info(self, docker_image): + self.add_and_display_buffer(ImageInfoBuffer(docker_image, self)) + def refresh_main_buffer(self, refresh_buffer=True): assert self.main_list_buffer is not None if refresh_buffer: diff --git a/sen/tui/widgets/info.py b/sen/tui/widgets/info.py new file mode 100644 index 0000000..955e7e4 --- /dev/null +++ b/sen/tui/widgets/info.py @@ -0,0 +1,56 @@ +""" +Info widgets: + * display detailed info about an object +""" +import logging + +from sen.tui.widgets.list.base import VimMovementListBox + +import urwid + +from sen.util import humanize_bytes + +logger = logging.getLogger(__name__) + + +class ImageInfoItemWidget(urwid.AttrMap): + def __init__(self, key, value=None): + attr_map = "main_list_dg" + focus_map = "main_list_white" + if value: + w = urwid.Columns([ + urwid.AttrMap(urwid.Text(key, align="left", wrap="any"), attr_map, focus_map), + urwid.AttrMap(urwid.Text(value, align="left", wrap="any"), attr_map, focus_map), + ]) + else: + w = urwid.Text(key, align="left", wrap="any") + super().__init__(w, attr_map, focus_map=focus_map) + + +class ImageInfoWidget(VimMovementListBox): + """ + display info about image + """ + def __init__(self, docker_image): + self.docker_image = docker_image + l = self._basic_data() + self._labels() + self.walker = urwid.SimpleFocusListWalker(l) + super().__init__(self.walker) + + def _basic_data(self): + l = [ + ImageInfoItemWidget("Id", self.docker_image.image_id), + ImageInfoItemWidget("Created", "{0}, {1}".format(self.docker_image.display_formal_time_created(), + self.docker_image.display_time_created())), + ImageInfoItemWidget("Size", humanize_bytes(self.docker_image.size)), + ] + return l + + def _labels(self): + if not self.docker_image.labels: + return [] + l = [ImageInfoItemWidget("Labels")] + for label_key, label_value in self.docker_image.labels.items(): + l.append(ImageInfoItemWidget(label_key, label_value)) + + return l diff --git a/sen/tui/widgets/list/main.py b/sen/tui/widgets/list/main.py index b78875b..273d1fd 100644 --- a/sen/tui/widgets/list/main.py +++ b/sen/tui/widgets/list/main.py @@ -318,6 +318,9 @@ def do_and_report_on_fail(f, docker_object): elif key == "f": self.ui.run_in_background(do_and_report_on_fail, self.ui.display_and_follow_logs, self.focused_docker_object) return + elif key == "enter": + self.ui.run_in_background(do_and_report_on_fail, self.ui.display_image_info, self.focused_docker_object) + return elif key == "d": self.ui.run_in_background( run_and_report_on_fail, diff --git a/sen/util.py b/sen/util.py index 0f27899..82a9d15 100644 --- a/sen/util.py +++ b/sen/util.py @@ -41,3 +41,27 @@ def setup_dirs(): def get_log_file_path(): return os.path.join(setup_dirs(), LOG_FILE_NAME) + + +def humanize_bytes(bytesize, precision=2): + """ + Humanize byte size figures + + https://gist.github.com/moird/3684595 + """ + abbrevs = ( + (1 << 50, 'PB'), + (1 << 40, 'TB'), + (1 << 30, 'GB'), + (1 << 20, 'MB'), + (1 << 10, 'kB'), + (1, 'bytes') + ) + if bytesize == 1: + return '1 byte' + for factor, suffix in abbrevs: + if bytesize >= factor: + break + if factor == 1: + precision = 0 + return '%.*f %s' % (precision, bytesize / float(factor), suffix) \ No newline at end of file