Added functionality for listing docker containers and images
This commit is contained in:
parent
82d970defb
commit
a15e7728a4
3 changed files with 256 additions and 45 deletions
|
@ -2,7 +2,18 @@
|
|||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkListStore" id="ContainerList"/>
|
||||
<object class="GtkListStore" id="ContainerList">
|
||||
<columns>
|
||||
<!-- column-name Name -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Image -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name ContainerID -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Status -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkImage" id="FileStockImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -18,7 +29,20 @@
|
|||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-jump-to</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="ImagesListStore"/>
|
||||
<object class="GtkListStore" id="ImagesList">
|
||||
<columns>
|
||||
<!-- column-name Repository -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Tag -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Size -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Virtual -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name Created -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkImage" id="PlayStockImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -111,6 +135,7 @@
|
|||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="ToolConnect">
|
||||
<property name="use_action_appearance">True</property>
|
||||
<property name="related_action">connect_action</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
|
@ -127,6 +152,7 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="Refresh">
|
||||
<property name="use_action_appearance">True</property>
|
||||
<property name="related_action">refresh_action</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -152,6 +178,7 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="Preferences">
|
||||
<property name="use_action_appearance">True</property>
|
||||
<property name="related_action">preferences_action</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -198,7 +225,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">start_action</property>
|
||||
<property name="image">PlayStockImage</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
|
@ -216,7 +242,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">stop_action</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
|
@ -234,7 +259,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">attach_action</property>
|
||||
<property name="image">GoToStockImage</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
|
@ -252,7 +276,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">log_action</property>
|
||||
<property name="image">FileStockImage</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
|
@ -280,9 +303,62 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">ContainerList</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="show_expanders">False</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ContainerName">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="clickable">True</property>
|
||||
<property name="sort_column_id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ContainerNameText"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ContainerImage">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Image</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ContainerImageText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ContainerID">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">ID</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ContainerIDText"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ContainerStatus">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Status</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ContainerStatusText"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -328,7 +404,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">build_action</property>
|
||||
<property name="image">GearStockImage</property>
|
||||
<property name="yalign">0.61000001430511475</property>
|
||||
<property name="always_show_image">True</property>
|
||||
|
@ -347,7 +422,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">remove_image_action</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
|
@ -365,7 +439,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">runimage_action</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
|
@ -391,10 +464,75 @@
|
|||
<object class="GtkTreeView" id="ImagesListView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">ImagesListStore</property>
|
||||
<property name="model">ImagesList</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection2"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ImagesRepo">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Repository</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="RepositoryText"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ImageTag">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Tag</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="TagText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ImageSize">
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="SizeText"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ImageVirtualSize">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Virtual Size</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="VirtualSizeText"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ImageCreated">
|
||||
<property name="title" translatable="yes">Created</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="CreatedText"/>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue