From a15e7728a429324eb80ccf13f0304e1162874036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Cuevas?= Date: Thu, 10 Jul 2014 11:11:28 +0200 Subject: [PATCH] Added functionality for listing docker containers and images --- res/stevedore.glade | 158 +++++++++++++++++++++++++++++++++++++++++--- src/docker_iface.py | 71 +++++++++++++------- src/stevedore.py | 72 +++++++++++++++++--- 3 files changed, 256 insertions(+), 45 deletions(-) diff --git a/res/stevedore.glade b/res/stevedore.glade index 0569780..c5d33b1 100644 --- a/res/stevedore.glade +++ b/res/stevedore.glade @@ -2,7 +2,18 @@ - + + + + + + + + + + + + True False @@ -18,7 +29,20 @@ False gtk-jump-to - + + + + + + + + + + + + + + True False @@ -111,6 +135,7 @@ True + True connect_action True True @@ -127,6 +152,7 @@ + True refresh_action True False @@ -152,6 +178,7 @@ + True preferences_action True False @@ -198,7 +225,6 @@ True True True - start_action PlayStockImage True @@ -216,7 +242,6 @@ True True True - stop_action True True @@ -234,7 +259,6 @@ True True True - attach_action GoToStockImage True @@ -252,7 +276,6 @@ True True True - log_action FileStockImage True @@ -280,9 +303,62 @@ True True ContainerList + 0 + False + both + + + True + Name + True + 0 + + + + 0 + + + + + + + True + Image + + + + 1 + + + + + + + True + ID + + + + 2 + + + + + + + True + Status + + + + 3 + + + + @@ -328,7 +404,6 @@ True True True - build_action GearStockImage 0.61000001430511475 True @@ -347,7 +422,6 @@ True True True - remove_image_action True True @@ -365,7 +439,6 @@ True True True - runimage_action True True @@ -391,10 +464,75 @@ True True - ImagesListStore + ImagesList + 0 + both + + + True + Repository + True + + + + 0 + + + + + + + True + Tag + True + + + + 1 + + + + + + + Size + True + + + + 2 + + + + + + + True + Virtual Size + True + + + + 3 + + + + + + + Created + True + + + + 4 + + + + diff --git a/src/docker_iface.py b/src/docker_iface.py index 9a34564..0860097 100644 --- a/src/docker_iface.py +++ b/src/docker_iface.py @@ -161,8 +161,13 @@ class DockerInterface(object): docker_url = 'unix://var/run/docker.sock', connect_timeout = 10): # Connect to the docker server, if this fails we let it go up - self.client = docker.Client(base_url = docker_url, - timeout = connect_timeout) + try: + self.client = docker.Client(base_url = docker_url, + timeout = connect_timeout) + + except Exception as e: + self.client = None + raise e # Since we're connected, let's get all the containers and images available self.update_images() @@ -174,39 +179,55 @@ class DockerInterface(object): it in ourselves for future reference. """ self.images = [] - img_list = self.client.images() - for img_info in img_list: - for repo_tags in img_info['RepoTags']: - repository_and_tags = repo_tags.split(':') - docker_image = DockerImage(repository = repository_and_tags[0], - tag = repository_and_tags[1], - img_id = img_info['Id'], - created = img_info['Created'], - size = img_info['Size'], - virtual_size = img_info['VirtualSize']) + if self.client is not None: + img_list = self.client.images() + for img_info in img_list: + for repo_tags in img_info['RepoTags']: + repository_and_tags = repo_tags.split(':') + docker_image = DockerImage(repository = repository_and_tags[0], + tag = repository_and_tags[1], + img_id = img_info['Id'], + created = img_info['Created'], + size = img_info['Size'], + virtual_size = img_info['VirtualSize']) - self.images.append(docker_image) + self.images.append(docker_image) + + else: + raise DockerNotConnectedException() def update_containers(self): """ Ask our docker server the containers created and their status """ self.containers = [] - containers = self.client.containers(all = True) - for container in containers: - container_object = DockerContainer(container_id = container['Id'], - image = container['Image'], - command = container['Command'], - created = container['Created'], - names = container['Names']) + if self.client is not None: + containers = self.client.containers(all = True) + for container in containers: + container_object = DockerContainer(container_id = container['Id'], + image = container['Image'], + command = container['Command'], + created = container['Created'], + names = container['Names']) - container_object.status_from_string(container['Status']) - self.containers.append(container_object) + container_object.status_from_string(container['Status']) + self.containers.append(container_object) + + else: + raise DockerNotConnectedException() def build(self, path, tag): """ Creates a new docker image from a Dockerfile in the specified path """ - self.client.build(path = path, tag = tag) - self.update_images() - self.update_containers() + if self.client is not None: + self.client.build(path = path, tag = tag) + self.update_images() + self.update_containers() + + else: + raise DockerNotConnectedException() + + +class DockerNotConnectedException(Exception): + pass diff --git a/src/stevedore.py b/src/stevedore.py index 4206a70..1359b4c 100644 --- a/src/stevedore.py +++ b/src/stevedore.py @@ -20,7 +20,9 @@ from gi.repository import Gtk, Gio from pkg_resources import resource_string -from docker_iface import DockerInterface, DockerImage, DockerContainer +from docker_iface import DockerInterface, DockerImage, DockerContainer, DockerNotConnectedException + +import time class MainWindow(Gtk.Application): @@ -47,12 +49,62 @@ class MainWindow(Gtk.Application): self.add_window(self.window) self.window.show() - def refresh_views(self): + def refresh_views(self, refresh_iface = True): """ Refresh the docker interface object and the reload the ListViews to get a fresh snapshot of the system """ - print "Not Implemented: refresh_views" + # If we have to refresh the Docker interface, we do so + if refresh_iface: + self.docker.update_images() + self.docker.update_containers() + + # Fetch the lists + container_view = self.builder.get_object('ContainerListView') + images_view = self.builder.get_object('ImagesListView') + + container_list = container_view.get_model() + image_list = images_view.get_model() + + # Clear them all + container_list.clear() + image_list.clear() + + # And now we populate them back + for container in self.docker.containers: + status = container.return_status_string() + container_list.append(row = [container.names[0], + unicode(container.image), + unicode(container.container_id)[:12], + status]) + + for image in self.docker.images: + size_text = self.size_to_human(image.size) + virtual_size_text = self.size_to_human(image.virtual_size) + human_date = time.ctime(int(image.created)) + image_list.append(row = [image.repository, + image.tag, + size_text, + virtual_size_text, + unicode(human_date)]) + + def size_to_human(self, size_in_bytes): + """ + Transforms the size given in bytes to a human readable form, appending + the units and making it a more manageable size + + Modified from this: http://stackoverflow.com/questions/13343700/bytes-to-human-readable-and-back-without-data-loss + """ + final_format = u"%(value).2f %(symbol)s" + symbols = (u'B', u'Kb', u'Mb', u'Gb', u'Tb', u'Pb', u'Eb', u'Zb', u'Yb') + prefix = {} + for i, s in enumerate(symbols[1:]): + prefix[s] = 1 << (i+1)*10 + for symbol in reversed(symbols[1:]): + if size_in_bytes >= prefix[symbol]: + value = float(size_in_bytes) / prefix[symbol] + return final_format % locals() + return final_format % dict(symbol = symbols[0], value = size_in_bytes) # Events and "natural" callbacks @@ -67,14 +119,14 @@ class MainWindow(Gtk.Application): Connection action has been triggered """ # TODO: Get the parameters from configuration - try: - self.docker = DockerInterface() - self.refresh_views() + # try: + self.docker = DockerInterface() + self.refresh_views(refresh_iface = False) - except Exception as e: - # FIXME: Show a nicer message with a MessageBox - print u"Error connecting to Docker Server: " + unicode(e) - self.docker = None + # except Exception as e: + # # FIXME: Show a nicer message with a MessageBox + # print u"Error connecting to Docker Server: " + unicode(e) + # self.docker = None def on_preferences_action_activate(self, obj, event = None): """