Exemplo n.º 1
0
    def on_connect_action_activate(self, obj, event = None):
        """
        Connection action has been triggered
        """
        # TODO: Get the parameters from configuration
        try:
            self.set_app_status(u"Connecting to Docker server...")
            self.docker = DockerInterface()

        except Exception as e:
            # FIXME: Show a nicer message with a MessageBox
            self.set_app_status(u"Error connecting to Docker Server.")
            message_dialog = Gtk.MessageDialog(self.window, 0, 
                                               Gtk.MessageType.ERROR,
                                               Gtk.ButtonsType.OK,
                                               u"Error connecting to Docker Server:\n {ExceptionMessage}".format(ExceptionMessage = unicode(e)))
            message_dialog.run()
            message_dialog.destroy()
            self.docker = None

            return

        self.set_app_status(u"Connected to Docker server successfully. Fetching container and image data.")
        self.refresh_views(refresh_iface = False)
        self.set_app_status(u"Connected to Docker server.")
Exemplo n.º 2
0
class MainWindow(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id = "apps.gnome.stevedore",
                                 flags = Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_app_start)

    def on_app_start(self, data = None):
        
        # create widget tree ...
        self.builder = Gtk.Builder()
        self.ui_manager = Gtk.UIManager()
        try:
            self.builder.add_from_file(resource_string(__name__, 'stevedore.glade'))
            self.ui_manager.add_ui_from_file(resource_string(__name__, 'ui_builder.xml'))

        except:
            self.builder.add_from_file('../res/stevedore.glade')
            self.ui_manager.add_ui_from_file('../res/ui_builder.xml')

        # connect handlers
        self.builder.connect_signals(self)
        self.ui_manager.insert_action_group(self.builder.get_object('ContainerActionGroup'))
        self.ui_manager.insert_action_group(self.builder.get_object('ImagesActionGroup'))

        # Prepare certain widgets
        self.status_bar = self.builder.get_object('AppStatus')
        self.status_context_id = self.status_bar.get_context_id('main_app')
        self.builder.get_object('StartButton').set_sensitive(False)
        self.builder.get_object('StopButton').set_sensitive(False)
        self.builder.get_object('AttachButton').set_sensitive(False)
        self.builder.get_object('LogButton').set_sensitive(False)
        self.builder.get_object('DeleteContainerButton').set_sensitive(False)

        # Add window to the App and show it
        self.window = self.builder.get_object('MainWindow')
        self.add_window(self.window)
        self.window.show()
        self.set_app_status(u'Not connected to Docker server.')

    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
        """
        # If we have to refresh the Docker interface, we do so
        if hasattr(self, 'docker') and self.docker is not None:
            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_index in self.docker.containers:
                container = self.docker.get_container_by_id(container_index)
                status = container.return_status_string()
                container_list.append(row = [container.names[0],
                                             unicode(container.image),
                                             unicode(container.container_id)[:12],
                                             status,
                                             unicode(container.container_id)])

            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)])

        else:
            self.set_app_status("Not connected to Docker server.")
            message_dialog = Gtk.MessageDialog(self.window, 0, 
                                               Gtk.MessageType.INFO,
                                               Gtk.ButtonsType.OK,
                                               u"First connect to a Docker server.")
            message_dialog.run()
            message_dialog.destroy()

    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)

    def set_app_status(self, message):
        """
        Shows the current application status in the status bar
        """
        try:
            self.status_bar.pop(self.status_context_id)

        except:
            pass

        self.status_bar.push(self.status_context_id, message)

    # Events and "natural" callbacks

    def on_MainWindow_delete_event(self, obj, event = None):
        """
        The main window is deleted
        """
        pass

    ### Action callbacks ###

    def on_connect_action_activate(self, obj, event = None):
        """
        Connection action has been triggered
        """
        # TODO: Get the parameters from configuration
        try:
            self.set_app_status(u"Connecting to Docker server...")
            self.docker = DockerInterface()

        except Exception as e:
            # FIXME: Show a nicer message with a MessageBox
            self.set_app_status(u"Error connecting to Docker Server.")
            message_dialog = Gtk.MessageDialog(self.window, 0, 
                                               Gtk.MessageType.ERROR,
                                               Gtk.ButtonsType.OK,
                                               u"Error connecting to Docker Server:\n {ExceptionMessage}".format(ExceptionMessage = unicode(e)))
            message_dialog.run()
            message_dialog.destroy()
            self.docker = None

            return

        self.set_app_status(u"Connected to Docker server successfully. Fetching container and image data.")
        self.refresh_views(refresh_iface = False)
        self.set_app_status(u"Connected to Docker server.")

    def on_preferences_action_activate(self, obj, event = None):
        """
        We've been asked for the preferences window
        """
        # TODO
        print "Not implemented: on_preferences_action_activate"

    def on_refresh_action_activate(self, obj, event = None):
        """
        The user wants to reload the listings
        """
        self.refresh_views()

    def on_start_action_activate(self, obj, event = None):
        """
        The user demands a certain container to be started
        """
        print "Not implemented: on_start_action_activate"

    def on_stop_action_activate(self, obj, event = None):
        """
        The user wants to stop a certain container
        """
        # TODO: Remember to show a warning and a yes-no choice before proceeding!
        print "Not implemented: on_stop_action_activate"

    def on_attach_action_activate(self, obj, event = None):
        """
        Here we're requested to attach to a running container
        """
        # TODO: We should launch $TERM with the attach command
        # if the container is running. If not, we display an error or warning
        # Maybe offer the option to start it and then attach?
        print "Not implemented: on_attach_action_activate"

    def on_log_action_activate(self, obj, event = None):
        """
        User wants to see the log output of a certain container
        """
        # TODO: We should launch $TERM with the log command and the -f option
        # unless it is not running, where we remove the -f option and launch it 
        # anyway.
        print "Not implemented: on_log_action_activate"

    def on_delete_container_action_activate(self, obj, event = None):
        """
        User wants to delete a selected container
        """
        container_view = self.builder.get_object('ContainerListView')
        selection = container_view.get_selection()
        model, selected = selection.get_selected()
        # selection is a treeiter
        if selected is not None:
            selected_container_id = model[selected][4]
            container = self.docker.get_container_by_id(selected_container_id)

            if container is not None:
                message_dialog = Gtk.MessageDialog(self.window, 0, 
                                                   Gtk.MessageType.QUESTION,
                                                   Gtk.ButtonsType.YES_NO,
                                                   u"Are you sure you want to remove {name} container?".format(name = container.names[0]))
                response = message_dialog.run()
                if response == Gtk.ResponseType.YES:
                    self.docker.remove_container(container.container_id)
                    self.refresh_views()

                message_dialog.destroy()

    def on_build_action_activate(self, obj, event = None):
        """
        Command for building new images from Dockerfiles
        """
        # TODO: Open a OpenFileDialog and build the image.
        print "Not implemented: on_build_action_activate"

    def on_remove_image_action_activate(self, obj, event = None):
        """
        We've been asked to remove an existing image
        """
        print "Not implemented: on_remove_image_action_activate"

    def on_runimage_action_activate(self, obj, event = None):
        """
        Launch selected image into a running container
        """
        print "Not implemented: on_runimage_action_activate"

    def on_ContainerListView_button_press_event(self, obj, event = None):
        """
        This hook checks the mouse interaction with the ContainerListView
        """
        if event is not None:
            if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
                container_view = self.builder.get_object('ContainerListView')
                selection = container_view.get_selection()
                model, selected = selection.get_selected()
                # selection is a treeiter
                if selected is not None:
                    selected_container_id = model[selected][4]
                    container = self.docker.get_container_by_id(selected_container_id)

                    if container is not None:
                        if container.status == DockerContainer.EXITED:
                            popup = self.ui_manager.get_widget('/ContainerStoppedPopup')

                        elif container.status == DockerContainer.UP:
                            popup = self.ui_manager.get_widget('/ContainerStartedPopup')

                        else:
                            popup = self.ui_manager.get_widget('/ContainerStoppedPopup')

                        popup.popup(None, None, None, None, event.button, event.time)

                        return True

    def on_container_view_selection_changed(self, selection):
        """
        We control which element is selected on the ContainerListView to enable and disable
        certain button actions
        """
        start_button = self.builder.get_object('StartButton')
        stop_button = self.builder.get_object('StopButton')
        attach_button = self.builder.get_object('AttachButton')
        log_button = self.builder.get_object('LogButton')
        delete_container_button = self.builder.get_object('DeleteContainerButton')

        model, treeiter = selection.get_selected()
        if treeiter is not None:
            container_id = model[treeiter][4]
            container = self.docker.get_container_by_id(container_id)

            if container.status == DockerContainer.UP:
                start_button.set_sensitive(False)
                stop_button.set_sensitive(True)
                attach_button.set_sensitive(True)
                log_button.set_sensitive(True)
                delete_container_button.set_sensitive(False)

            elif container.status == DockerContainer.EXITED:
                start_button.set_sensitive(True)
                stop_button.set_sensitive(False)
                attach_button.set_sensitive(False)
                log_button.set_sensitive(True)
                delete_container_button.set_sensitive(True)

            else:
                start_button.set_sensitive(False)
                stop_button.set_sensitive(False)
                attach_button.set_sensitive(False)
                log_button.set_sensitive(True)
                delete_container_button.set_sensitive(True)

        else:
            start_button.set_sensitive(False)
            stop_button.set_sensitive(False)
            attach_button.set_sensitive(False)
            log_button.set_sensitive(False)
            delete_container_button.set_sensitive(False)