src/gui/modules/uarenamebe.py
author Rich Burridge <rich.burridge@sun.com>
Mon, 30 Nov 2009 13:01:40 -0800
changeset 1516 8c950a3b4171
parent 1487 2c36e9347c4f
child 1587 e46d4e51d02f
permissions -rw-r--r--
10485 move pkg(5) to Python 2.6 10482 upgrade to cherrypy 3.1.2 11836 shebang line for python modules should be python version-agnostic 11950 ldtp used by pkg build process not setup to easily use Python 2.6 11989 pkg python dependency analysis tests fail

#!/usr/bin/python
#
# 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 pkg.gui.misc as gui_misc
import os
import pkg.misc as misc
from threading import Thread
import time

try:
        import gobject
        import gtk
        import gtk.glade
        import pygtk
        pygtk.require("2.0")
except ImportError:
        import sys
        sys.exit(1)

try:
        import libbe as be
        nobe = False
except ImportError:
        # All actions are disabled when libbe can't be imported. 
        nobe = True

VALID_BE_NAME = 0
INVALID_BE_NAME = -1
DUPLICATE_BE_NAME = -2
ACTIVATED_BE_NAME = -3          #If the be was not changed by the user
ERROR_FORMAT = "<span color = \"red\">%s</span>"

class RenameBeAfterUpdateAll:
        def __init__(self, parent, dialog_icon, parent_window):
                if nobe:
                        msg = _("The <b>libbe</b> library was not "
                            "found on your system.")
                        msgbox = gtk.MessageDialog(
                            buttons = gtk.BUTTONS_CLOSE,
                            flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_INFO,
                            message_format = None)
                        msgbox.set_markup(msg)
                        msgbox.set_title(_("Rename BE"))
                        msgbox.run()
                        msgbox.destroy()
                        return

                # Before performing update all (image-update) task, we are storing
                # the active on reboot be name. If the be name after update is different
                # it means that new BE was created and we can show BE rename dialog
                # otherwise we can show update completed dialog.
                # Also we need to store original BE name to work-around the bug: 6472202
                self.active_be_before_update_all = self.__get_activated_be_name()

                self.parent = parent
                self.updated_packages_list = None
                self.stop_progress_bouncing = False
                self.stopped_bouncing_progress = True
                gladefile = os.path.join(self.parent.application_dir,
                    "usr/share/package-manager/packagemanager.glade")
                w_tree_ua_completed = \
                    gtk.glade.XML(gladefile, "ua_completed_dialog")
                w_xmltree_progress = gtk.glade.XML(gladefile, "progressdialog")

                self.w_ua_completed_dialog = \
                    w_tree_ua_completed.get_widget("ua_completed_dialog")
                self.w_ua_be_entry = \
                    w_tree_ua_completed.get_widget("ua_be_entry")
                self.w_ua_release_notes_button = \
                    w_tree_ua_completed.get_widget("ua_release_notes_button")
                self.w_be_error_label = \
                    w_tree_ua_completed.get_widget("be_error_label")
                self.w_ua_restart_later_button = \
                    w_tree_ua_completed.get_widget("ua_restart_later_button")
                self.w_ua_restart_now_button = \
                    w_tree_ua_completed.get_widget("ua_restart_now_button")
                self.w_ua_ok_image = \
                    w_tree_ua_completed.get_widget("ua_ok_image")

                self.w_progress_dialog = w_xmltree_progress.get_widget("progressdialog")
                self.w_progressinfo_label = w_xmltree_progress.get_widget("progressinfo")
                self.w_progress_cancel = w_xmltree_progress.get_widget("progresscancel")
                self.w_progressbar = w_xmltree_progress.get_widget("progressbar")
                self.w_progress_dialog.connect('delete-event', lambda stub1, stub2: True)
                self.w_progress_cancel.set_sensitive(False)

                self.w_progress_dialog.set_title(_("Rename BE"))
                self.w_progressinfo_label.set_text(_("Renaming BE, please wait..."))

                self.w_progress_dialog.set_icon(dialog_icon)
                self.w_ua_completed_dialog.set_icon(dialog_icon)

                checkmark_icon = gui_misc.get_icon(
                    self.parent.icon_theme, "pm-check", 24)

                self.w_ua_ok_image.set_from_pixbuf(checkmark_icon)

                gui_misc.set_modal_and_transient(self.w_progress_dialog, parent_window)
                gui_misc.set_modal_and_transient(self.w_ua_completed_dialog,
                    parent_window)

                try:
                        dic_be_rename = \
                            {
                                "on_ua_help_button_clicked" : \
                                    self.__on_ua_help_button_clicked,
                                "on_ua_restart_later_button_clicked" : \
                                    self.__on_ua_restart_later_button_clicked,
                                "on_ua_restart_now_button_clicked" : \
                                    self.__on_ua_restart_now_button_clicked,
                                "on_ua_dialog_close" : \
                                    self.__on_ua_dialog_close,
                                "on_ua_completed_dialog_delete_event" : \
                                    self.__on_ua_completed_dialog_delete_event,
                                "on_ua_be_entry_changed" : \
                                    self.__on_ua_be_entry_changed,
                            }
                        w_tree_ua_completed.signal_autoconnect(dic_be_rename)
                except AttributeError, error:
                        print _(
                            "GUI will not respond to any event! %s. "
                            "Check declare_signals()") \
                            % error

        def show_rename_dialog(self, updated_packages_list):
                '''Returns False if no BE rename is needed'''
                self.updated_packages_list = updated_packages_list
                self.__set_release_notes_url()
                self.__setup_be_list()
                orig_name = self.__get_activated_be_name()
                if orig_name == self.active_be_before_update_all:
                        self.w_ua_completed_dialog.hide()
                        self.parent.update_package_list(self.updated_packages_list)
                        return False
                else:
                        self.w_ua_completed_dialog.show_all()
                        return True

        def __set_release_notes_url(self):
                info_url = misc.get_release_notes_url()
                if info_url and len(info_url) == 0:
                        info_url = gui_misc.RELEASE_URL
                self.w_ua_release_notes_button.set_uri(info_url)

        def __on_ua_restart_later_button_clicked(self, widget):
                self.__proceed_after_update()

        def __on_ua_restart_now_button_clicked(self, widget):
                self.__proceed_after_update(True)

        def __on_ua_dialog_close(self, widget):
                self.__proceed_after_update()

        @staticmethod
        def __on_ua_completed_dialog_delete_event(widget, event):
                return True

        def __proceed_after_update(self, reboot=False):
                orig_name = self.__get_activated_be_name()
                new_name = self.w_ua_be_entry.get_text()
                Thread(target = self.__set_be_name,
                    args = (orig_name, new_name, reboot)).start()
                self.w_ua_completed_dialog.hide()

        def __set_be_name(self, orig_name, new_name, reboot):
                ret_code = self.__verify_be_name(new_name)
                be_rename_code = 0
                if ret_code != ACTIVATED_BE_NAME:
                        self.__start_bouncing_progress()
                        be_rename_code = self.__rename_be(orig_name, new_name)
                if be_rename_code != 0:
                        # Workaround for bug: 6472202
                        # If the rename didn't work for the first time, we should:
                        # - Activate current BE - active_name
                        # - Rename the BE orig_name to BE new_name
                        # - Activate the BE new_name
                        active_name = self.__get_active_be_name()
                        workaround_code = self.__workaround_for_6472202(
                            active_name, orig_name, new_name)
                        if workaround_code != 0:
                                gobject.idle_add(self.__g_be_rename_problem_dialog,
                                    new_name, orig_name)
                if reboot:
                        ret = gui_misc.restart_system()
                        if ret != 0:
                                gobject.idle_add(self.__g_be_reboot_problem_dialog)
                        else:
                                gobject.idle_add(self.parent.shutdown_after_image_update)
                else:
                        gobject.idle_add(self.parent.update_package_list,
                            self.updated_packages_list)
                self.__stop_bouncing_progress()
                gobject.idle_add(self.parent.shutdown_after_image_update, False)

        @staticmethod
        def __g_be_rename_problem_dialog(new_name, orig_name):
                msg_type = gtk.MESSAGE_INFO
                error_msg = _("Could not change the BE name to:\n\t"
                    "%s\n\nThe following name will be used instead:"
                    "\n\t%s" % (new_name, orig_name))
                msg_title = _("BE Name")
                gui_misc.error_occurred(None, error_msg, msg_title, msg_type)

        @staticmethod
        def __g_be_reboot_problem_dialog():
                msg_type = gtk.MESSAGE_ERROR
                error_msg = _("Could not restart the system."
                    "\nPlease restart the system manually.")
                msg_title = _("Restart Error")
                gui_misc.error_occurred(None, error_msg, msg_title, msg_type)

        def __on_ua_be_entry_changed(self, widget):
                if len(widget.get_text()) == 0:
                        self.w_be_error_label.hide()
                        self.__set_buttons_state(False)
                        return
                ret_code = self.__verify_be_name(widget.get_text())
                if ret_code == ACTIVATED_BE_NAME:
                        self.__set_buttons_state(True)
                        self.w_be_error_label.hide()
                elif ret_code == DUPLICATE_BE_NAME:
                        self.__set_buttons_state(False)
                        error_string = _("This name already exists.")
                        error = ERROR_FORMAT % error_string
                        self.w_be_error_label.set_markup(error)
                        self.w_be_error_label.show()
                elif ret_code == INVALID_BE_NAME:
                        self.__set_buttons_state(False)
                        error_string = _("BE name contains invalid character.")
                        error = ERROR_FORMAT % error_string
                        self.w_be_error_label.set_markup(error)
                        self.w_be_error_label.show()
                else:
                        self.__set_buttons_state(True)
                        self.w_be_error_label.hide()

        def __set_buttons_state(self, sensitive=False):
                self.w_ua_restart_later_button.set_sensitive(sensitive)
                self.w_ua_restart_now_button.set_sensitive(sensitive)

        def __setup_be_list(self):
                be_list = self.__get_be_list()
                proposed_name = ""
                for bee in be_list:
                        if bee.get("active_boot"):
                                proposed_name = bee.get("orig_be_name")
                self.w_ua_be_entry.set_text(proposed_name)

        def __verify_be_name(self, new_name):
                be_list = self.__get_be_list()
                for bee in be_list:
                        name = bee.get("orig_be_name")
                        if name == new_name:
                                active_boot = bee.get("active_boot")
                                if name == new_name \
                                    and active_boot == False:
                                        return DUPLICATE_BE_NAME
                                elif active_boot:
                                        return ACTIVATED_BE_NAME
                if be.beVerifyBEName(new_name) != VALID_BE_NAME:
                        return INVALID_BE_NAME
                return VALID_BE_NAME

        def __get_activated_be_name(self):
                be_list = self.__get_be_list()
                for bee in be_list:
                        name = bee.get("orig_be_name")
                        active_boot = bee.get("active_boot")
                        if active_boot:
                                return name

        def __get_active_be_name(self):
                be_list = self.__get_be_list()
                for bee in be_list:
                        name = bee.get("orig_be_name")
                        active_boot = bee.get("active")
                        if active_boot:
                                return name

        def __start_bouncing_progress(self):
                self.stop_progress_bouncing = False
                self.stopped_bouncing_progress = False
                gobject.idle_add(self.w_progress_dialog.show)
                Thread(target =
                    self.__g_progressdialog_progress_pulse).start()

        def __stop_bouncing_progress(self):
                if self.__is_progress_bouncing():
                        self.stop_progress_bouncing = True

        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
                gobject.idle_add(self.w_progress_dialog.hide)

        def __is_progress_bouncing(self):
                return not self.stopped_bouncing_progress

        @staticmethod
        def __get_be_list():
                be_list_vals = be.beList()
                be_list = None
                if isinstance(be_list_vals[0], int):
                        be_list = be_list_vals[1]
                else:
                        be_list = be_list_vals
                return be_list

        @staticmethod
        def __rename_be(orig_name, new_name):
                # The rename operation is i/o intensive, so the gui
                # progress is not responsive. This will allow to show the
                # gui progress.
                time.sleep(0.2)
                return be.beRename(orig_name, new_name)

        @staticmethod
        def __workaround_for_6472202(active_name, orig_name, new_name):
                ret_code = 0
                ret_code = be.beActivate(active_name)
                if ret_code == 0:
                        ret_code = be.beRename(orig_name, new_name)
                if ret_code == 0:
                        ret_code = be.beActivate(new_name)
                else:
                        be.beActivate(orig_name)
                return ret_code

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