src/gui/modules/installupdate.py
author Padraig O'Briain <padraig.obriain@sun.com>
Tue, 10 Nov 2009 14:37:37 +0000
changeset 1480 05ad339c1b13
parent 1469 30f9e05c523d
child 1481 2db11049f8b1
permissions -rw-r--r--
12346 Add icons for High Contrast theme

#!/usr/bin/python2.4
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

import errno
import os
import sys
import time
import pango
import datetime
import traceback
from threading import Thread
try:
        import gobject
        import gtk
        import gtk.glade
        import pygtk
        pygtk.require("2.0")
except ImportError:
        sys.exit(1)
nobe = False
try:
        import libbe as be
except ImportError:
        nobe = True
import pkg
import pkg.gui.progress as progress
import pkg.misc
import pkg.client.history as history
import pkg.client.api_errors as api_errors
import pkg.gui.beadmin as beadm
import pkg.gui.misc as gui_misc
import pkg.gui.enumerations as enumerations

ERROR_FORMAT = "<span color = \"red\">%s</span>"


class InstallUpdate(progress.GuiProgressTracker):
        def __init__(self, list_of_packages, parent, image_directory,
            ips_update = False, action = -1, be_name = None,
            parent_name = "", pkg_list = None, main_window = None,
            icon_confirm_dialog = None, title = None, web_install = False,
            skip_be_dialog = False):
                if action == -1:
                        return
                progress.GuiProgressTracker.__init__(self)
                self.web_install = web_install
                self.web_updates_list = None
                self.parent = parent
                self.api_o = gui_misc.get_api_object(image_directory,
                    self, main_window)
                if self.api_o == None:
                        return
                self.be_list = None
                self.be_name = be_name
                self.parent_name = parent_name
                self.ipkg_ipkgui_list = pkg_list
                self.icon_confirm_dialog = icon_confirm_dialog
                self.title = title
                self.w_main_window = main_window
                self.ips_update = ips_update
                self.list_of_packages = list_of_packages
                self.action = action
                self.canceling = False
                self.current_stage_name = None
                self.ip = None
                self.operations_done = False
                self.prev_ind_phase = None
                self.prev_pkg = None
                self.progress_stop_timer_running = False
                self.proposed_be_name = None
                self.pylint_stub = None
                self.stages = {
                          1:[_("Preparing..."), _("Preparation")],
                          2:[_("Downloading..."), _("Download")],
                          3:[_("Installing..."), _("Install")],
                         }
                self.stop_progress_bouncing = False
                self.stopped_bouncing_progress = True
                self.update_list = {}
                gladefile = os.path.join(self.parent.application_dir,
                    "usr/share/package-manager/packagemanager.glade")
                w_tree_dialog = gtk.glade.XML(gladefile, "createplandialog")
                w_tree_uaconfirm = gtk.glade.XML(gladefile, "ua_confirm_dialog")
                w_tree_removeconfirm = \
                    gtk.glade.XML(gladefile, "removeconfirmation")
                self.w_dialog = w_tree_dialog.get_widget("createplandialog")
                self.w_expander = w_tree_dialog.get_widget("expander3")
                self.w_cancel_button = w_tree_dialog.get_widget("cancelcreateplan")
                self.w_progressbar = w_tree_dialog.get_widget("createplanprogress")
                self.w_details_textview = w_tree_dialog.get_widget("createplantextview")
                self.w_removeconfirm_dialog = \
                    w_tree_removeconfirm.get_widget("removeconfirmation")
                self.w_removeconfirm_dialog.set_icon(self.parent.window_icon)
                w_removeproceed_button = w_tree_removeconfirm.get_widget("remove_proceed")
                w_remove_treeview = w_tree_removeconfirm.get_widget("removetreeview")
                w_stage2 = w_tree_dialog.get_widget("stage2")
                self.w_stages_box = w_tree_dialog.get_widget("stages_box")
                self.w_stage1_label = w_tree_dialog.get_widget("label_stage1")
                self.w_stage1_icon = w_tree_dialog.get_widget("icon_stage1")
                self.w_stage2_label = w_tree_dialog.get_widget("label_stage2")
                self.w_stage2_icon = w_tree_dialog.get_widget("icon_stage2")
                self.w_stage3_label = w_tree_dialog.get_widget("label_stage3")
                self.w_stage3_icon = w_tree_dialog.get_widget("icon_stage3")
                self.w_stages_label = w_tree_dialog.get_widget("label_stages")
                self.w_stages_icon = w_tree_dialog.get_widget("icon_stages")
                self.current_stage_label = self.w_stage1_label
                self.current_stage_icon = self.w_stage1_icon
                self.current_stage_label_done = None

                self.done_icon = gui_misc.get_icon(
                    self.parent.icon_theme, "progress_checkmark")
                blank_icon = gui_misc.get_icon(
                    self.parent.icon_theme, "progress_blank")

                self.w_stage1_icon.set_from_pixbuf(blank_icon)
                self.w_stage2_icon.set_from_pixbuf(blank_icon)
                self.w_stage3_icon.set_from_pixbuf(blank_icon)

                infobuffer = self.w_details_textview.get_buffer()
                infobuffer.create_tag("bold", weight=pango.WEIGHT_BOLD)
                infobuffer.create_tag("level1", left_margin=30, right_margin=10)
                infobuffer.create_tag("level2", left_margin=50, right_margin=10)
                self.w_ua_dialog = w_tree_uaconfirm.get_widget("ua_confirm_dialog")
                self.w_ua_dialog.set_icon(self.parent.window_icon)
                self.w_ua_error_label = w_tree_uaconfirm.get_widget(
                    "ua_confirm_error_label")
                self.w_ua_proceed_button = w_tree_uaconfirm.get_widget(
                    "ua_proceed_button")
                self.w_ua_be_name_entry = w_tree_uaconfirm.get_widget(
                    "ua_be_name_entry")
                self.w_ua_be_name_box = w_tree_uaconfirm.get_widget(
                    "ua_be_name_box")

                w_ua_proceed_button = w_tree_uaconfirm.get_widget("ua_proceed_button")
                self.w_progressbar.set_pulse_step(0.02)
                try:
                        dic_createplan = \
                            {
                                "on_cancelcreateplan_clicked": \
                                    self.__on_cancelcreateplan_clicked,
                                "on_createplandialog_delete_event": \
                                    self.__on_createplandialog_delete,
                            }
                        dic_uaconfirm = \
                            {
                                "on_ua_cancel_button_clicked": \
                                    self.__on_ua_cancel_button_clicked,
                                "on_ua_proceed_button_clicked": \
                                self.__on_ua_proceed_button_clicked,
                                "on_ua_be_name_entry_changed": \
                                self.__on_ua_be_name_entry_changed,
                                "on_ua_help_button_clicked": \
                                self.__on_ua_help_button_clicked,
                            }
                        dic_removeconfirm = \
                            {
                                "on_proceed_button_clicked": \
                                    self.__on_remove_proceed_button_clicked,
                                "on_cancel_button_clicked": \
                                self.__on_remove_cancel_button_clicked,
                            }
                        w_tree_dialog.signal_autoconnect(dic_createplan)
                        w_tree_uaconfirm.signal_autoconnect(dic_uaconfirm)
                        w_tree_removeconfirm.signal_autoconnect(dic_removeconfirm)
                except AttributeError, error:
                        print _("GUI will not respond to any event! %s. "
                            "Check installupdate.py signals") \
                            % error


                self.w_dialog.set_transient_for(self.w_main_window)
                self.w_ua_dialog.set_transient_for(self.w_main_window)
                if self.icon_confirm_dialog != None:
                        self.w_ua_dialog.set_icon(self.icon_confirm_dialog)
                else:
                        self.w_ua_dialog.set_icon(
                            self.w_main_window.get_icon())

                if self.action == enumerations.REMOVE:
                        #We are not showing the download stage in the main stage list
                        self.stages[3] = [_("Removing..."), _("Remove")]
                        self.w_stage3_label.set_text(self.stages[3][1])
                        w_stage2.hide()
                        self.w_dialog.set_title(_("Remove"))
                        w_removeproceed_button.grab_focus()
                        cell = gtk.CellRendererText()
                        remove_column = gtk.TreeViewColumn('Removed')
                        remove_column.pack_start(cell, True)
                        remove_column.add_attribute(cell, 'text', 0)
                        w_remove_treeview.append_column(remove_column)

                        liststore = gtk.ListStore(str)
                        for sel_pkg in list_of_packages:
                                liststore.append([sel_pkg])
                        w_remove_treeview.set_model(liststore)
                        w_remove_treeview.expand_all()
                        self.w_removeconfirm_dialog.show()

                elif self.action == enumerations.IMAGE_UPDATE:
                        self.w_dialog.set_title(_("Update All"))
                        w_ua_proceed_button.grab_focus()
                        if not self.be_name and not skip_be_dialog:
                                if nobe or not "beVerifyBEName" in be.__dict__:
                                        self.w_ua_be_name_box.set_property(
                                            "visible", False)
                                else:
                                        self.__setup_be_list()
                                self.w_ua_dialog.show()
                        else:
                                self.proposed_be_name = self.be_name
                                self.__proceed_with_stages()
                else:
                        if self.title != None:
                                self.w_dialog.set_title(self.title)
                        else:
                                self.w_dialog.set_title(_("Install/Update"))
                        self.__proceed_with_stages()


        def __on_createplandialog_delete(self, widget, event):
                self.__on_cancelcreateplan_clicked(None)
                return True

        def __on_cancelcreateplan_clicked(self, widget):
                '''Handler for signal send by cancel button, which user might press during
                evaluation stage - while the dialog is creating plan'''
                if self.api_o.can_be_canceled():
                        self.canceling = True
                        Thread(target = self.api_o.cancel, args = ()).start()
                        cancel_txt = _("Canceling...")
                        txt = "<b>" + self.current_stage_label_done + " - " \
                            + cancel_txt + "</b>"
                        gobject.idle_add(self.current_stage_label.set_markup, txt)
                        gobject.idle_add(self.current_stage_icon.set_from_stock,
                            gtk.STOCK_CANCEL, gtk.ICON_SIZE_MENU)
                        gobject.idle_add(self.w_stages_label.set_markup, cancel_txt)
                        self.w_cancel_button.set_sensitive(False)
                if self.operations_done:
                        self.w_dialog.hide()
                        if self.web_install:
                                gobject.idle_add(self.parent.update_package_list,
                                    self.web_updates_list)
                                return
                        gobject.idle_add(self.parent.update_package_list, None)


        @staticmethod
        def __on_ua_help_button_clicked(widget):
                gui_misc.display_help("update_all")

        def __on_ua_cancel_button_clicked(self, widget):
                self.w_ua_dialog.hide()
                if self.web_install:
                        gobject.idle_add(self.parent.update_package_list,
                            self.web_updates_list)
                        return
                gobject.idle_add(self.parent.update_package_list, None)

        def __on_ua_proceed_button_clicked(self, widget):
                proposed_be_name = self.w_ua_be_name_entry.get_text()
                if proposed_be_name != "":
                        self.proposed_be_name = proposed_be_name
                self.w_ua_dialog.hide()
                self.__proceed_with_stages()

        def __setup_be_list(self):
                be_list = be.beList()
                error_code = None
                if len(be_list) > 1 and type(be_list[0]) == type(-1):
                        error_code = be_list[0]
                if error_code != None and error_code == 0:
                        self.be_list = be_list[1]
                elif error_code == None:
                        self.be_list = be_list

                # Now set proposed name in entry field.
                active_name = None
                for bee in self.be_list:
                        name = bee.get("orig_be_name")
                        if name:
                                if bee.get("active"):
                                        active_name = name
                                        break
                if active_name != None:
                        proposed_name = None
                        name_list = active_name.rsplit('-', 1)
                        if len(name_list) == 1:
                                proposed_name = self.__construct_be_name(
                                    active_name, 0)
                        else:
                                try:
                                        i = int(name_list[1])
                                        proposed_name = self.__construct_be_name(
                                            name_list[0], i)
                                except ValueError:
                                        proposed_name = self.__construct_be_name(
                                            active_name, 0)

                        if proposed_name != None:
                                self.w_ua_be_name_entry.set_text(proposed_name)


        def __construct_be_name(self, name, i):
                in_use = True
                proposed_name = None
                while in_use:
                        i += 1
                        proposed_name = name + '-'  + str(i)
                        in_use = self.__is_be_name_in_use(proposed_name)
                return proposed_name

        def __is_be_name_in_use(self, name):
                in_use = False
                if name == "":
                        return in_use
                for bee in self.be_list:
                        be_name = bee.get("orig_be_name")
                        if be_name == name:
                                in_use = True
                                break
                return in_use

        @staticmethod
        def __is_be_name_valid( name):
                if name == "":
                        return True
                return be.beVerifyBEName(name) == 0

        def __validate_be_name(self, widget):
                name = widget.get_text()
                is_name_valid = self.__is_be_name_valid(name)
                self.w_ua_error_label.hide()
                error_str = None
                if is_name_valid:
                        is_name_in_use = self.__is_be_name_in_use(name)
                        if is_name_in_use:
                                error_str = ERROR_FORMAT % _("BE name is in use")
                else:
                        error_str = ERROR_FORMAT % _("BE name is invalid")
                if error_str != None:
                        self.w_ua_error_label.set_markup(error_str)
                        self.w_ua_error_label.show()

                self.w_ua_proceed_button.set_sensitive(error_str == None)

        def __on_ua_be_name_entry_changed(self, widget):
                self.__validate_be_name(widget)

        def __on_remove_cancel_button_clicked(self, widget):
                self.w_removeconfirm_dialog.hide()

        def __on_remove_proceed_button_clicked(self, widget):
                self.w_removeconfirm_dialog.hide()
                self.__proceed_with_stages()

        def __ipkg_ipkgui_uptodate(self):
                if self.ipkg_ipkgui_list == None:
                        return True
                upgrade_needed = self.api_o.plan_install(
                    self.ipkg_ipkgui_list, filters = [])
                return not upgrade_needed

        def __proceed_with_stages(self):
                self.__start_stage_one()
                gui_misc.set_modal_and_transient(self.w_dialog,
                    self.w_main_window)
                self.w_dialog.show()
                Thread(target = self.__proceed_with_stages_thread_ex,
                    args = ()).start()

        def __proceed_with_stages_thread_ex(self):
                try:
                        try:
                                if self.action == enumerations.IMAGE_UPDATE:
                                        self.__start_substage(
                                            _("Ensuring %s is up to date...") %
                                            self.parent_name,
                                            bounce_progress=True)
                                        opensolaris_image = True
                                        ips_uptodate = True
                                        notfound = self.__installed_fmris_from_args(
                                            ["SUNWipkg", "SUNWcs"])
                                        if notfound:
                                                opensolaris_image = False
                                        if opensolaris_image:
                                                ips_uptodate = \
                                                    self.__ipkg_ipkgui_uptodate()
                                        if not ips_uptodate:
                                        #Do the stuff with installing pkg pkg-gui
                                        #and restart in the special mode
                                                self.ips_update = True
                                                self.__proceed_with_ipkg_thread()
                                                return
                                        else:
                                                self.api_o.reset()
                                self.__proceed_with_stages_thread()
                        except (MemoryError, EnvironmentError), __e:
                                if isinstance(__e, EnvironmentError) and \
                                    __e.errno != errno.ENOMEM:
                                        raise
                                msg = pkg.misc.out_of_memory()
                                self.__g_error_stage(msg)
                                return

                except api_errors.InventoryException, e:
                        msg = _("Inventory exception:\n")
                        if e.illegal:
                                for i in e.illegal:
                                        msg += "\tpkg:\t" + i +"\n"
                        else:
                                msg = "%s" % e
                        self.__g_error_stage(msg)
                        return
                except api_errors.CatalogRefreshException, e:
                        msg = _("Please check the network "
                            "connection.\nIs the repository accessible?")
                        if e.message and len(e.message) > 0:
                                msg = e.message
                        self.__g_error_stage(msg)
                        return
                except api_errors.TransportError, ex:
                        msg = _("Please check the network "
                            "connection.\nIs the repository accessible?\n\n"
                            "%s") % str(ex)
                        self.__g_error_stage(msg)
                        return
                except api_errors.InvalidDepotResponseException, e:
                        msg = _("\nUnable to contact a valid package depot. "
                            "Please check your network\nsettings and "
                            "attempt to contact the server using a web "
                            "browser.\n\n%s") % str(e)
                        self.__g_error_stage(msg)
                        return
                except api_errors.IpkgOutOfDateException:
                        msg = _("pkg(5) appears to be out of "
                            "date and should be\nupdated before running "
                            "Update All.\nPlease update SUNWipkg package")
                        self.__g_error_stage(msg)
                        return
                except api_errors.NonLeafPackageException, nlpe:
                        msg = _("Cannot remove:\n\t%s\n"
                                "Due to the following packages that "
                                "depend on it:\n") % nlpe[0].get_name()
                        for pkg_a in nlpe[1]:
                                msg += "\t" + pkg_a.get_name() + "\n"
                        self.__g_error_stage(msg)
                        return
                except api_errors.ProblematicPermissionsIndexException, err:
                        msg = str(err)
                        msg += _("\nFailure of consistent use of pfexec or gksu when "
                            "running\n%s is often a source of this problem.") % \
                            self.parent_name
                        msg += _("\nTo rebuild index, please use the terminal command:")
                        msg += _("\n\tpfexec pkg rebuild-index")
                        self.__g_error_stage(msg)
                        return
                except api_errors.CorruptedIndexException:
                        msg = _("There was an error during installation. The search\n"
                            "index is corrupted. You might want try to fix this\n"
                            "problem by running command:\n"
                            "\tpfexec pkg rebuild-index")
                        self.__g_error_stage(msg)
                        return
                except api_errors.ImageUpdateOnLiveImageException:
                        msg = _("This is an Live Image. The install"
                            "\noperation can't be performed.")
                        self.__g_error_stage(msg)
                        return
                except api_errors.RebootNeededOnLiveImageException:
                        msg = _("The requested operation would affect files that cannot"
                        "be modified in the Live Image.\n"
                        "Please retry this operation on an alternate boot environment.")
                        self.__g_error_stage(msg)
                        return
                except api_errors.PlanMissingException:
                        msg = _("There was an error during installation.\n"
                            "The Plan of the operation is missing and the operation\n"
                            "can't be finished. You might want try to fix this\n"
                            "problem by restarting %s\n") % self.parent_name
                        self.__g_error_stage(msg)
                        return
                except api_errors.ImageplanStateException:
                        msg = _("There was an error during installation.\n"
                            "The State of the image is incorrect and the operation\n"
                            "can't be finished. You might want try to fix this\n"
                            "problem by restarting %s\n") % self.parent_name
                        self.__g_error_stage(msg)
                        return
                except api_errors.CanceledException:
                        gobject.idle_add(self.w_dialog.hide)
                        self.stop_bouncing_progress()
                        return
                except api_errors.BENamingNotSupported:
                        msg = _("Specifying BE Name not supported.\n")
                        self.__g_error_stage(msg)
                        return
                except api_errors.InvalidBENameException:
                        msg = _("Invalid BE Name: %s.\n") % self.proposed_be_name
                        self.__g_error_stage(msg)
                        return
                except (api_errors.UnableToCopyBE,
                    api_errors.UnableToMountBE,
                    api_errors.UnableToRenameBE,
                    api_errors.PermissionsException,
                    api_errors.PlanCreationException,
                    api_errors.CertificateError), ex:
                        msg = str(ex)
                        self.__g_error_stage(msg)
                        return
                except api_errors.BENameGivenOnDeadBE, ex:
                        # 9363. We will try again to perform image-update
                        # but this time without passing be_name to the api
                        # if this fails for the second time, we will print error.
                        if self.proposed_be_name != None:
                                self.proposed_be_name = None
                                self.__proceed_with_stages_thread_ex()
                        else:
                                msg = str(ex)
                                self.__g_error_stage(msg)
                                return
                # We do want to prompt user to load BE admin if there is
                # not enough disk space. This error can either come as an
                # error within API exception, see bug #7642 or as a standalone
                # error, that is why we need to check for both situations.
                except EnvironmentError, uex:
                        if uex.errno in (errno.EDQUOT, errno.ENOSPC):
                                self.__handle_nospace_error()
                        else:
                                self.__handle_error()
                        return
                except history.HistoryStoreException, uex:
                        if (isinstance(uex.error, EnvironmentError) and
                           uex.error.errno in (errno.EDQUOT, errno.ENOSPC)):
                                self.__handle_nospace_error()
                        else:
                                self.__handle_error()
                        return
                except Exception:
                        self.__handle_error()
                        return

        def __handle_nospace_error(self):
                gobject.idle_add(self.__prompt_to_load_beadm)
                gobject.idle_add(self.w_dialog.hide)
                self.stop_bouncing_progress()

        def __handle_error(self):
                traceback_lines = traceback.format_exc().splitlines()
                traceback_str = ""
                for line in traceback_lines:
                        traceback_str += line + "\n"
                self.__g_exception_stage(traceback_str)
                sys.exc_clear()

        def __proceed_with_ipkg_thread(self):
                self.__start_substage(_("Updating %s") % self.parent_name,
                    bounce_progress=True)
                self.__afterplan_information()
                self.prev_pkg = None
                self.__start_substage(_("Downloading..."), bounce_progress=False)
                self.api_o.prepare()
                self.__start_substage(_("Executing..."), bounce_progress=False)
                self.api_o.execute_plan()
                gobject.idle_add(self.__operations_done)


        def __proceed_with_stages_thread(self):
                self.__start_substage(
                    _("Gathering package information, please wait..."))
                stuff_todo = self.__plan_stage()
                if stuff_todo:
                        self.__afterplan_information()
                        self.prev_pkg = None
                        # The api.prepare() mostly is downloading the files so we are
                        # Not showing this stage in the main stage dialog. If download
                        # is necessary, then we are showing it in the details view
                        if not self.action == enumerations.REMOVE:
                                self.__start_stage_two()
                                self.__start_substage(None,
                                    bounce_progress=False)
                        self.api_o.prepare()
                        self.__start_stage_three()
                        self.__start_substage(None,
                            bounce_progress=False)
                        self.api_o.execute_plan()
                        gobject.idle_add(self.__operations_done)
                else:
                        if self.web_install:
                                gobject.idle_add(self.w_expander.hide)
                                gobject.idle_add(self.__operations_done,
                                    _("All packages already installed."))
                                return

                        msg = None
                        if self.action == enumerations.INSTALL_UPDATE:
                                msg = _("Selected package(s) cannot be updated on "
                                "their own.\nClick Update All to update all packages.")
                        elif self.action == enumerations.IMAGE_UPDATE:
                                msg = _("Your system has already been updated.")
                        self.__g_error_stage(msg)

        def __start_stage_one(self):
                self.current_stage_label = self.w_stage1_label
                self.current_stage_icon = self.w_stage1_icon
                self.__start_stage(self.stages.get(1))
                self.update_details_text(self.stages.get(1)[0]+"\n", "bold")

        def __start_stage_two(self):
                # End previous stage
                self.__end_stage()
                self.current_stage_label = self.w_stage2_label
                self.current_stage_icon = self.w_stage2_icon
                self.__start_stage(self.stages.get(2))
                self.update_details_text(self.stages.get(2)[0]+"\n", "bold")

        def __start_stage_three(self):
                self.__end_stage()
                self.current_stage_label = self.w_stage3_label
                self.current_stage_icon = self.w_stage3_icon
                self.__start_stage(self.stages.get(3))
                self.update_details_text(self.stages.get(3)[0]+"\n", "bold")

        def __start_stage(self, stage_text):
                self.current_stage_label_done = stage_text[1]
                gobject.idle_add(self.current_stage_label.set_markup,
                    "<b>"+stage_text[0]+"</b>")
                gobject.idle_add(self.current_stage_icon.set_from_stock,
                    gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_MENU)

        def __end_stage(self):
                gobject.idle_add(self.current_stage_label.set_text,
                    self.current_stage_label_done)
                gobject.idle_add(self.current_stage_icon.set_from_pixbuf, self.done_icon)

        def __g_error_stage(self, msg):
                if msg == None or len(msg) == 0:
                        msg = _("No futher information available")
                self.operations_done = True
                self.stop_bouncing_progress()
                self.update_details_text(_("\nError:\n"), "bold")
                self.update_details_text("%s" % msg, "level1")
                self.update_details_text("\n")
                txt = "<b>" + self.current_stage_label_done + _(" - Failed </b>")
                gobject.idle_add(self.current_stage_label.set_markup, txt)
                gobject.idle_add(self.current_stage_icon.set_from_stock,
                    gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU)
                gobject.idle_add(self.w_expander.set_expanded, True)
                gobject.idle_add(self.w_cancel_button.set_sensitive, True)

        def __g_exception_stage(self, tracebk):
                self.operations_done = True
                self.stop_bouncing_progress()
                txt = "<b>" + self.current_stage_label_done + _(" - Failed </b>")
                gobject.idle_add(self.current_stage_label.set_markup, txt)
                gobject.idle_add(self.current_stage_icon.set_from_stock,
                    gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU)
                msg_1 = _("An unknown error occurred in the %s stage.\n"
                    "Please let the developers know about this problem by\n"
                    "filing a bug together with the error details listed below at:\n"
                    ) % self.current_stage_name
                msg_2 = "http://defect.opensolaris.org\n\n"
                self.update_details_text(_("\nError:\n"), "bold")
                self.update_details_text("%s" % msg_1, "level1")
                self.update_details_text("%s" % msg_2, "bold", "level2")
                if tracebk:
                        msg = _("Exception traceback:\n")
                        self.update_details_text("%s" % msg,
                            "bold","level1")
                        self.update_details_text("%s\n" % tracebk, "level2")
                else:
                        msg = _("No futher information available")
                        self.update_details_text("%s\n" % msg, "level2")
                msg_3 = _("pkg version: ")
                self.update_details_text("%s" % msg_3,
                    "bold","level1")
                self.update_details_text("%s\n\n" % pkg.VERSION, "level2")
                publisher_header = _("List of configured publishers:")
                self.update_details_text("%s" % publisher_header,
                    "bold","level1")
                pref_pub = self.api_o.get_preferred_publisher()
                fmt = "\n%s\t%s\t%s (%s)"
                publisher_str = ""
                for pub in self.api_o.get_publishers():
                        pstatus = " "
                        if pub == pref_pub:
                                # Preferred
                                pstatus = "P"
                        elif pub.disabled:
                                # Disabled
                                pstatus = "D"
                        else:
                                # Enabled, but not preferred
                                pstatus = "E"
                        r = pub.selected_repository
                        for uri in r.origins:
                                # Origin
                                publisher_str += fmt % (pstatus, "O", pub.prefix, uri)
                        for uri in r.mirrors:
                                # Mirror
                                publisher_str += fmt % (pstatus, "M", pub.prefix, uri)
                self.update_details_text("%s\n" % publisher_str,
                    "level2")
                gobject.idle_add(self.w_expander.set_expanded, True)
                gobject.idle_add(self.w_cancel_button.set_sensitive, True)

        def __start_substage(self, text, bounce_progress=True):
                if text:
                        self.update_label_text(text)
                        self.update_details_text(text + "\n")
                if bounce_progress:
                        if self.stopped_bouncing_progress:
                                self.start_bouncing_progress()
                else:
                        self.stop_bouncing_progress()

        def update_label_text(self, markup_text):
                gobject.idle_add(self.__stages_label_set_markup, markup_text)

        def __stages_label_set_markup(self, markup_text):
                if not self.canceling == True:
                        self.w_stages_label.set_markup(markup_text)

        def start_bouncing_progress(self):
                self.stop_progress_bouncing = False
                self.stopped_bouncing_progress = False
                Thread(target =
                    self.__g_progressdialog_progress_pulse).start()

        def __g_progressdialog_progress_pulse(self):
                while not self.stop_progress_bouncing:
                        gobject.idle_add(self.w_progressbar.pulse)
                        time.sleep(0.1)
                self.stopped_bouncing_progress = True

        def is_progress_bouncing(self):
                return not self.stopped_bouncing_progress

        def stop_bouncing_progress(self):
                if self.is_progress_bouncing():
                        self.stop_progress_bouncing = True

        def update_details_text(self, text, *tags):
                gobject.idle_add(self.__update_details_text, text, *tags)

        def __update_details_text(self, text, *tags):
                buf = self.w_details_textview.get_buffer()
                textiter = buf.get_end_iter()
                if tags:
                        buf.insert_with_tags_by_name(textiter, text, *tags)
                else:
                        buf.insert(textiter, text)
                self.w_details_textview.scroll_to_iter(textiter, 0.0)

        def update_progress(self, current, total):
                prog = float(current)/total
                gobject.idle_add(self.w_progressbar.set_fraction, prog)

        def __plan_stage(self):
                '''Function which plans the image'''
                stuff_to_do = False
                if self.action == enumerations.INSTALL_UPDATE:
                        stuff_to_do = self.api_o.plan_install(
                            self.list_of_packages, refresh_catalogs = False,
                            filters = [])
                elif self.action == enumerations.REMOVE:
                        plan_uninstall = self.api_o.plan_uninstall
                        stuff_to_do = \
                            plan_uninstall(self.list_of_packages, False, False)
                elif self.action == enumerations.IMAGE_UPDATE:
                        # we are passing force, since we already checked if the
                        # SUNWipkg and SUNWipkg-gui are up to date.
                        stuff_to_do, opensolaris_image = \
                            self.api_o.plan_update_all(sys.argv[0],
                            refresh_catalogs = False,
                            noexecute = False, force = True,
                            be_name = self.proposed_be_name)
                        self.pylint_stub = opensolaris_image
                return stuff_to_do

        def __operations_done(self, alternate_done_txt = None):
                done_txt = _("Installation completed successfully.")
                if self.action == enumerations.REMOVE:
                        done_txt = _("Packages removed successfully.")
                elif self.action == enumerations.IMAGE_UPDATE:
                        done_txt = _("Packages updated successfully.")
                if alternate_done_txt != None:
                        done_txt = alternate_done_txt
                self.w_stages_box.hide()
                self.w_stages_icon.set_from_stock(
                    gtk.STOCK_OK, gtk.ICON_SIZE_DND)
                self.w_stages_icon.show()
                self.__stages_label_set_markup(done_txt)
                self.__update_details_text("\n"+ done_txt, "bold")
                self.w_cancel_button.set_label("gtk-close")
                self.w_cancel_button.grab_focus()
                self.w_progressbar.hide()
                self.stop_bouncing_progress()
                self.operations_done = True
                if self.parent != None:
                        if not self.web_install and not self.ips_update \
                            and not self.action == enumerations.IMAGE_UPDATE:
                                self.parent.update_package_list(self.update_list)
                        if self.web_install:
                                self.web_updates_list = self.update_list
                if self.ips_update:
                        self.w_dialog.hide()
                        self.parent.restart_after_ips_update(self.proposed_be_name)
                elif self.action == enumerations.IMAGE_UPDATE:
                        self.w_dialog.hide()
                        self.parent.shutdown_after_image_update()

        def __prompt_to_load_beadm(self):
                msgbox = gtk.MessageDialog(parent = self.w_main_window,
                    buttons = gtk.BUTTONS_OK_CANCEL, flags = gtk.DIALOG_MODAL,
                    type = gtk.MESSAGE_ERROR,
                    message_format = _(
                        "Not enough disk space, the selected action cannot "
                        "be performed.\n\n"
                        "Click OK to manage your existing BEs and free up disk space or "
                        "Cancel to cancel the action."))
                msgbox.set_title(_("Not Enough Disk Space"))
                result = msgbox.run()
                msgbox.destroy()
                if result == gtk.RESPONSE_OK:
                        beadm.Beadmin(self.parent)

        def __afterplan_information(self):
                install_iter = None
                update_iter = None
                remove_iter = None
                plan = self.api_o.describe().get_changes()
                self.update_details_text("\n")
                for pkg_plan in plan:
                        origin_fmri = pkg_plan[0]
                        destination_fmri = pkg_plan[1]
                        if origin_fmri and destination_fmri:
                                if not update_iter:
                                        update_iter = True
                                        txt = _("Packages To Be Updated:\n")
                                        self.update_details_text(txt, "bold")
                                pkg_a = self.__get_pkgstr_from_pkginfo(destination_fmri)
                                self.update_details_text(pkg_a+"\n", "level1")
                        elif not origin_fmri and destination_fmri:
                                if not install_iter:
                                        install_iter = True
                                        txt = _("Packages To Be Installed:\n")
                                        self.update_details_text(txt, "bold")
                                pkg_a = self.__get_pkgstr_from_pkginfo(destination_fmri)
                                self.update_details_text(pkg_a+"\n", "level1")
                        elif origin_fmri and not destination_fmri:
                                if not remove_iter:
                                        remove_iter = True
                                        txt = _("Packages To Be Removed:\n")
                                        self.update_details_text(txt, "bold")
                                pkg_a = self.__get_pkgstr_from_pkginfo(origin_fmri)
                                self.update_details_text(pkg_a+"\n", "level1")
                self.update_details_text("\n")

        def __get_pkgstr_from_pkginfo(self, pkginfo):
                dt_str = self.get_datetime(pkginfo.packaging_date)
                if not dt_str:
                        dt_str = ""
                s_ver = pkginfo.version
                s_bran = pkginfo.branch
                pkg_name = pkginfo.pkg_stem
                pkg_publisher = pkginfo.publisher
                if not pkg_publisher in self.update_list:
                        self.update_list[pkg_publisher] = []
                pub_list = self.update_list.get(pkg_publisher)
                if not pkg_name in pub_list:
                        pub_list.append(pkg_name)
                l_ver = 0
                version_pref = ""
                while l_ver < len(s_ver) -1:
                        version_pref += "%d%s" % (s_ver[l_ver],".")
                        l_ver += 1
                version_pref += "%d%s" % (s_ver[l_ver],"-")
                l_ver = 0
                version_suf = ""
                if s_bran != None:
                        while l_ver < len(s_bran) -1:
                                version_suf += "%d%s" % (s_bran[l_ver],".")
                                l_ver += 1
                        version_suf += "%d" % s_bran[l_ver]
                pkg_version = version_pref + version_suf + dt_str
                return pkg_name + "@" + pkg_version

        @staticmethod
        def get_datetime(date_time):
                '''Support function for getting date from the API.'''
                date_tmp = None
                try:
                        date_tmp = time.strptime(date_time, "%a %b %d %H:%M:%S %Y")
                except ValueError:
                        return None
                if date_tmp:
                        date_tmp2 = datetime.datetime(*date_tmp[0:5])
                        return date_tmp2.strftime(":%m%d")
                return None

        def __installed_fmris_from_args(self, args_f):
                found = []
                notfound = []
                try:
                        for m in self.api_o.img.inventory(args_f):
                                found.append(m[0])
                except api_errors.InventoryException, e:
                        notfound = e.notfound
                return notfound