1 #!/usr/bin/python2.4
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23 # Use is subject to license terms.
  24 #
  25 
  26 MIN_IND_ELEMENTS_BOUNCE = 5      # During indexing the progress will be progressive if 
  27                                  # the number of indexing elements is greater then this, 
  28                                  # otherwise it will bounce
  29 
  30 import errno
  31 import os
  32 import sys
  33 import time
  34 import pango
  35 import datetime
  36 import traceback
  37 import string
  38 from threading import Thread
  39 try:
  40         import gobject
  41         import gtk
  42         import gtk.glade
  43         import pygtk
  44         pygtk.require("2.0")
  45 except ImportError:
  46         sys.exit(1)
  47 nobe = False
  48 try:
  49         import libbe as be
  50 except ImportError:
  51         nobe = True
  52 import pkg.client.progress as progress
  53 import pkg.misc
  54 import pkg.client.api_errors as api_errors
  55 import pkg.gui.beadmin as beadm
  56 import pkg.gui.misc as gui_misc
  57 import pkg.gui.enumerations as enumerations
  58 
  59 ERROR_FORMAT = "<span color = \"red\">%s</span>"
  60 
  61 
  62 class InstallUpdate(progress.ProgressTracker):
  63         def __init__(self, list_of_packages, parent, api_o,
  64             ips_update = False, action = -1, be_name = None, 
  65             parent_name = "", pkg_list = None, main_window = None,
  66             icon_confirm_dialog = None, title = None, web_install = False):
  67                 if action == -1:
  68                         return
  69                 progress.ProgressTracker.__init__(self)
  70                 self.web_install = web_install
  71                 self.web_updates_list = None
  72                 api_o.progresstracker = self
  73                 self.api_o = api_o
  74                 self.parent = parent
  75                 self.be_list = None
  76                 self.be_name = be_name
  77                 self.parent_name = parent_name
  78                 self.ipkg_ipkgui_list = pkg_list
  79                 self.icon_confirm_dialog = icon_confirm_dialog
  80                 self.title = title
  81                 self.w_main_window = main_window
  82                 self.ips_update = ips_update
  83                 self.list_of_packages = list_of_packages
  84                 self.act_phase_last = None
  85                 self.action = action
  86                 self.canceling = False
  87                 self.current_stage_name = None
  88                 self.ind_started = None
  89                 self.ip = None
  90                 self.operations_done = False
  91                 self.prev_ind_phase = None
  92                 self.prev_pkg = None
  93                 self.progress_stop_timer_running = False
  94                 self.proposed_be_name = None
  95                 self.stages = {
  96                           1:[_("Preparing..."), _("Preparation")],
  97                           2:[_("Downloading..."), _("Download")],
  98                           3:[_("Installing..."), _("Install")],
  99                          }
 100                 self.stop_bouncing_progress = False
 101                 self.stopped_bouncing_progress = True
 102                 self.update_list = {}
 103                 gladefile = os.path.join(self.parent.application_dir,
 104                     "usr/share/package-manager/packagemanager.glade")
 105                 w_tree_dialog = gtk.glade.XML(gladefile, "createplandialog")
 106                 w_tree_uaconfirm = gtk.glade.XML(gladefile, "ua_confirm_dialog")
 107                 w_tree_removeconfirm = \
 108                     gtk.glade.XML(gladefile, "removeconfirmation")
 109                 self.w_dialog = w_tree_dialog.get_widget("createplandialog")
 110                 self.w_expander = w_tree_dialog.get_widget("expander3")
 111                 self.w_cancel_button = w_tree_dialog.get_widget("cancelcreateplan")
 112                 self.w_progressbar = w_tree_dialog.get_widget("createplanprogress")
 113                 self.w_details_textview = w_tree_dialog.get_widget("createplantextview")
 114                 self.w_removeconfirm_dialog = \
 115                     w_tree_removeconfirm.get_widget("removeconfirmation")
 116                 w_removeproceed_button = w_tree_removeconfirm.get_widget("remove_proceed")
 117                 w_remove_treeview = w_tree_removeconfirm.get_widget("removetreeview")
 118                 w_stage2 = w_tree_dialog.get_widget("stage2")
 119                 self.w_stages_box = w_tree_dialog.get_widget("stages_box")
 120                 self.w_stage1_label = w_tree_dialog.get_widget("label_stage1")
 121                 self.w_stage1_icon = w_tree_dialog.get_widget("icon_stage1")
 122                 self.w_stage2_label = w_tree_dialog.get_widget("label_stage2")
 123                 self.w_stage2_icon = w_tree_dialog.get_widget("icon_stage2")
 124                 self.w_stage3_label = w_tree_dialog.get_widget("label_stage3")
 125                 self.w_stage3_icon = w_tree_dialog.get_widget("icon_stage3")
 126                 self.w_stages_label = w_tree_dialog.get_widget("label_stages")
 127                 self.w_stages_icon = w_tree_dialog.get_widget("icon_stages")
 128                 self.current_stage_label = self.w_stage1_label
 129                 self.current_stage_icon = self.w_stage1_icon
 130                 self.current_stage_label_done = None
 131 
 132                 self.done_icon = gui_misc.get_icon(
 133                     self.parent.icon_theme, "progress_checkmark")
 134                 blank_icon = gui_misc.get_icon(
 135                     self.parent.icon_theme, "progress_blank")
 136 
 137                 self.w_stage1_icon.set_from_pixbuf(blank_icon)
 138                 self.w_stage2_icon.set_from_pixbuf(blank_icon)
 139                 self.w_stage3_icon.set_from_pixbuf(blank_icon)
 140 
 141                 infobuffer = self.w_details_textview.get_buffer()
 142                 infobuffer.create_tag("bold", weight=pango.WEIGHT_BOLD)
 143                 infobuffer.create_tag("level1", left_margin=30, right_margin=10)
 144                 infobuffer.create_tag("level2", left_margin=50, right_margin=10)
 145                 self.w_ua_dialog = w_tree_uaconfirm.get_widget("ua_confirm_dialog")
 146                 self.w_ua_error_label = w_tree_uaconfirm.get_widget(
 147                     "ua_confirm_error_label")
 148                 self.w_ua_proceed_button = w_tree_uaconfirm.get_widget(
 149                     "ua_proceed_button")
 150                 self.w_ua_be_name_entry = w_tree_uaconfirm.get_widget(
 151                     "ua_be_name_entry")
 152                 self.w_ua_be_name_box = w_tree_uaconfirm.get_widget(
 153                     "ua_be_name_box")
 154 
 155                 w_ua_proceed_button = w_tree_uaconfirm.get_widget("ua_proceed_button")
 156                 self.w_progressbar.set_pulse_step(0.02)
 157                 try:
 158                         dic_createplan = \
 159                             {
 160                                 "on_cancelcreateplan_clicked": \
 161                                     self.__on_cancelcreateplan_clicked,
 162                                 "on_createplandialog_delete_event": \
 163                                     self.__on_createplandialog_delete,
 164                             }
 165                         dic_uaconfirm = \
 166                             {
 167                                 "on_ua_cancel_button_clicked": \
 168                                     self.__on_ua_cancel_button_clicked,
 169                                 "on_ua_proceed_button_clicked": \
 170                                 self.__on_ua_proceed_button_clicked,
 171                                 "on_ua_be_name_entry_changed": \
 172                                 self.__on_ua_be_name_entry_changed,
 173                                 "on_ua_help_button_clicked": \
 174                                 self.__on_ua_help_button_clicked,                                
 175                             }
 176                         dic_removeconfirm = \
 177                             {
 178                                 "on_proceed_button_clicked": \
 179                                     self.__on_remove_proceed_button_clicked,
 180                                 "on_cancel_button_clicked": \
 181                                 self.__on_remove_cancel_button_clicked,
 182                             }
 183                         w_tree_dialog.signal_autoconnect(dic_createplan)
 184                         w_tree_uaconfirm.signal_autoconnect(dic_uaconfirm)
 185                         w_tree_removeconfirm.signal_autoconnect(dic_removeconfirm)
 186                 except AttributeError, error:
 187                         print _("GUI will not respond to any event! %s. "
 188                             "Check installupdate.py signals") \
 189                             % error
 190 
 191 
 192                 self.w_dialog.set_transient_for(self.w_main_window)
 193                 self.w_ua_dialog.set_transient_for(self.w_main_window)
 194                 if self.icon_confirm_dialog != None:  
 195                         self.w_ua_dialog.set_icon(self.icon_confirm_dialog)
 196 
 197                 if self.action == enumerations.REMOVE:
 198                         #We are not showing the download stage in the main stage list
 199                         self.stages[3] = [_("Removing..."), _("Remove")]
 200                         self.w_stage3_label.set_text(self.stages[3][1])
 201                         w_stage2.hide()
 202                         self.w_dialog.set_title(_("Remove"))
 203                         w_removeproceed_button.grab_focus()
 204                         cell = gtk.CellRendererText()
 205                         remove_column = gtk.TreeViewColumn('Removed')
 206                         remove_column.pack_start(cell, True)
 207                         remove_column.add_attribute(cell, 'text', 0)
 208                         w_remove_treeview.append_column(remove_column)
 209 
 210                         liststore = gtk.ListStore(str)
 211                         for sel_pkg in list_of_packages:
 212                                 liststore.append([sel_pkg])
 213                         w_remove_treeview.set_model(liststore)
 214                         w_remove_treeview.expand_all()
 215                         self.w_removeconfirm_dialog.show()
 216 
 217                 elif self.action == enumerations.IMAGE_UPDATE:
 218                         self.w_dialog.set_title(_("Update All"))
 219                         w_ua_proceed_button.grab_focus()
 220                         if not self.be_name:
 221                                 if nobe or not "beVerifyBEName" in be.__dict__:
 222                                         self.w_ua_be_name_box.set_property(
 223                                             "visible", False)
 224                                 else:
 225                                         self.__setup_be_list()
 226                                 self.w_ua_dialog.show()
 227                         else:
 228                                 self.proposed_be_name = self.be_name
 229                                 self.__proceed_with_stages()
 230                 else:
 231                         if self.title != None:
 232                                 self.w_dialog.set_title(self.title)
 233                         else:
 234                                 self.w_dialog.set_title(_("Install/Update"))
 235                         self.__proceed_with_stages()
 236 
 237 
 238         def __on_createplandialog_delete(self, widget, event):
 239                 self.__on_cancelcreateplan_clicked(None)
 240                 return True
 241 
 242         def __on_cancelcreateplan_clicked(self, widget):
 243                 '''Handler for signal send by cancel button, which user might press during
 244                 evaluation stage - while the dialog is creating plan'''
 245                 if self.api_o.can_be_canceled():
 246                         self.canceling = True
 247                         Thread(target = self.api_o.cancel, args = ()).start()
 248                         cancel_txt = _("Canceling...")
 249                         txt = "<b>" + self.current_stage_label_done + " - " \
 250                             + cancel_txt + "</b>"
 251                         gobject.idle_add(self.current_stage_label.set_markup, txt)
 252                         gobject.idle_add(self.current_stage_icon.set_from_stock, 
 253                             gtk.STOCK_CANCEL, gtk.ICON_SIZE_MENU)
 254                         gobject.idle_add(self.w_stages_label.set_markup, cancel_txt)
 255                         self.w_cancel_button.set_sensitive(False)
 256                 if self.operations_done:
 257                         self.w_dialog.hide()
 258                         if self.web_install:
 259                                 gobject.idle_add(self.parent.update_package_list, 
 260                                     self.web_updates_list)
 261                                 return
 262                         gobject.idle_add(self.parent.update_package_list, None)
 263 
 264         def __on_ua_help_button_clicked(self, widget):
 265                 gui_misc.display_help(self.parent.application_dir, "update_all")
 266                 
 267         def __on_ua_cancel_button_clicked(self, widget):
 268                 self.w_ua_dialog.hide()
 269                 if self.web_install:
 270                         gobject.idle_add(self.parent.update_package_list, 
 271                             self.web_updates_list)
 272                         return
 273                 gobject.idle_add(self.parent.update_package_list, None)
 274 
 275         def __on_ua_proceed_button_clicked(self, widget):
 276                 proposed_be_name = self.w_ua_be_name_entry.get_text()
 277                 if proposed_be_name != "":
 278                         self.proposed_be_name = proposed_be_name
 279                 self.w_ua_dialog.hide()
 280                 self.__proceed_with_stages()
 281 
 282         def __setup_be_list(self):
 283                 be_list = be.beList()
 284                 error_code = None
 285                 if len(be_list) > 1 and type(be_list[0]) == type(-1):
 286                         error_code = be_list[0]
 287                 if error_code != None and error_code == 0:
 288                         self.be_list = be_list[1]
 289                 elif error_code == None:
 290                         self.be_list = be_list
 291 
 292                 # Now set proposed name in entry field.
 293                 active_name = None
 294                 for bee in self.be_list:
 295                         name = bee.get("orig_be_name")
 296                         if name:
 297                                 if bee.get("active"):
 298                                         active_name = name
 299                                         break
 300                 if active_name != None:
 301                         proposed_name = None
 302                         list = string.rsplit(active_name, '-', 1)
 303                         if len(list) == 1:
 304                                 proposed_name = self.__construct_be_name(
 305                                     active_name, 0)
 306                         else:
 307                                 try:
 308                                         i = int(list[1])
 309                                         proposed_name = self.__construct_be_name(
 310                                             list[0], i)
 311                                 except ValueError:
 312                                         proposed_name = self.__construct_be_name(
 313                                             active_name, 0)
 314 
 315                         if proposed_name != None:
 316                                 self.w_ua_be_name_entry.set_text(proposed_name)
 317 
 318                         
 319         def __construct_be_name(self, name, i):
 320                 in_use = True
 321                 proposed_name = None
 322                 while in_use:
 323                         i += 1
 324                         proposed_name = name + '-'  + str(i)
 325                         in_use = self.__is_be_name_in_use(proposed_name)
 326                 return proposed_name
 327             
 328         def __is_be_name_in_use(self, name):
 329                 in_use = False
 330                 if name == "":
 331                         return in_use
 332                 for bee in self.be_list:
 333                         be_name = bee.get("orig_be_name")
 334                         if be_name == name:
 335                                 in_use = True
 336                                 break
 337                 return in_use
 338  
 339         def __is_be_name_valid(self, name):
 340                 if name == "":
 341                         return True
 342                 return be.beVerifyBEName(name) == 0
 343    
 344         def __validate_be_name(self, widget):
 345                 name = widget.get_text()
 346                 is_name_valid = self.__is_be_name_valid(name)
 347                 self.w_ua_error_label.hide()
 348                 error_str = None
 349                 if is_name_valid:
 350                         is_name_in_use = self.__is_be_name_in_use(name)
 351                         if is_name_in_use:
 352                                 error_str = ERROR_FORMAT % _("BE name is in use")
 353                 else:
 354                         error_str = ERROR_FORMAT % _("BE name is invalid")
 355                 if error_str != None:
 356                         self.w_ua_error_label.set_markup(error_str)
 357                         self.w_ua_error_label.show()
 358                   
 359                 self.w_ua_proceed_button.set_sensitive(error_str == None)
 360 
 361         def __on_ua_be_name_entry_changed(self, widget):
 362                 self.__validate_be_name(widget)
 363 
 364         def __on_remove_cancel_button_clicked(self, widget):
 365                 self.w_removeconfirm_dialog.hide()
 366 
 367         def __on_remove_proceed_button_clicked(self, widget):
 368                 self.w_removeconfirm_dialog.hide()
 369                 self.__proceed_with_stages()
 370 
 371         def __ipkg_ipkgui_uptodate(self):
 372                 if self.ipkg_ipkgui_list == None:
 373                         return True
 374                 upgrade_needed, cre = self.api_o.plan_install(
 375                     self.ipkg_ipkgui_list, filters = [])
 376                 return not upgrade_needed
 377 
 378         def __proceed_with_stages(self):
 379                 self.__start_stage_one()
 380                 self.w_dialog.show()
 381                 Thread(target = self.__proceed_with_stages_thread_ex, 
 382                     args = ()).start()
 383 
 384         def __proceed_with_stages_thread_ex(self):
 385                 try:
 386                         if self.action == enumerations.IMAGE_UPDATE:
 387                                 self.__start_substage(
 388                                     _("Ensuring %s is up to date...") % self.parent_name, 
 389                                     bounce_progress=True)
 390                                 opensolaris_image = True
 391                                 ips_uptodate = True
 392                                 notfound = self.__installed_fmris_from_args(
 393                                     ["SUNWipkg", "SUNWcs"])
 394                                 if notfound:
 395                                         opensolaris_image = False
 396                                 if opensolaris_image:
 397                                         ips_uptodate = self.__ipkg_ipkgui_uptodate()
 398                                 if not ips_uptodate:
 399                                         #Do the stuff with installing ipkg ipkggui and
 400                                         #restart in the special mode
 401                                         self.ips_update = True
 402                                         self.__proceed_with_ipkg_thread()
 403                                         return
 404                                 else:
 405                                         self.api_o.reset()
 406                         self.__proceed_with_stages_thread()
 407                 except api_errors.CertificateError:
 408                         self.stop_bouncing_progress = True
 409                         msg = _("Accessing this restricted repository failed."
 410                             "\nYou either need to register to access this repository,"
 411                             "\nthe certificate expired, or you need to accept the"
 412                             " repository\ncertificate.")
 413                         self.__g_error_stage(msg)
 414                         return
 415                 except api_errors.PlanCreationException, e:
 416                         self.__g_error_stage(str(e))
 417                         return
 418                 except api_errors.InventoryException, e:
 419                         msg = _("Inventory exception:\n")
 420                         if e.illegal:
 421                                 for i in e.illegal:
 422                                         msg += "\tpkg:\t" + i +"\n"
 423                         self.__g_error_stage(msg)
 424                         return
 425                 except api_errors.CatalogRefreshException, e:
 426                         msg = _("Please check the network "
 427                             "connection.\nIs the repository accessible?")
 428                         if e.message and len(e.message) > 0:
 429                                 msg = e.message
 430                         self.__g_error_stage(msg)
 431                         return
 432                 except api_errors.TransportError, ex:
 433                         msg = _("Please check the network "
 434                             "connection.\nIs the repository accessible?\n\n"
 435                             "%s") % str(ex)
 436                         self.__g_error_stage(msg)
 437                         return
 438                 except api_errors.InvalidDepotResponseException, e:
 439                         msg = _("\nUnable to contact a valid package depot. "
 440                             "Please check your network\nsettings and "
 441                             "attempt to contact the server using a web "
 442                             "browser.\n\n%s") % str(e)
 443                         self.__g_error_stage(msg)
 444                         return
 445                 except api_errors.IpkgOutOfDateException:
 446                         msg = _("pkg(5) appears to be out of "
 447                             "date and should be\nupdated before running "
 448                             "Update All.\nPlease update SUNWipkg package")
 449                         self.__g_error_stage(msg)
 450                         return
 451                 except api_errors.NonLeafPackageException, nlpe:
 452                         msg = _("Cannot remove:\n\t%s\n"
 453                                 "Due to the following packages that "
 454                                 "depend on it:\n") % nlpe[0].get_name()
 455                         for pkg_a in nlpe[1]:
 456                                 msg += "\t" + pkg_a.get_name() + "\n"
 457                         self.__g_error_stage(msg)
 458                         return
 459                 except api_errors.ProblematicPermissionsIndexException, err:
 460                         msg = str(err)
 461                         msg += _("\nFailure of consistent use of pfexec or gksu when "
 462                             "running\n%s is often a source of this problem.") % \
 463                             self.parent_name
 464                         msg += _("\nTo rebuild index, please use the terminal command:")
 465                         msg += _("\n\tpfexec pkg rebuild-index")
 466                         self.__g_error_stage(msg)
 467                         return
 468                 except api_errors.CorruptedIndexException:
 469                         msg = _("There was an error during installation. The search\n"
 470                             "index is corrupted. You might want try to fix this\n"
 471                             "problem by running command:\n"
 472                             "\tpfexec pkg rebuild-index")
 473                         self.__g_error_stage(msg)
 474                         return
 475                 except api_errors.ImageUpdateOnLiveImageException:
 476                         msg = _("This is an Live Image. The install"
 477                             "\noperation can't be performed.")
 478                         self.__g_error_stage(msg)
 479                         return
 480                 except api_errors.PlanMissingException:
 481                         msg = _("There was an error during installation.\n"
 482                             "The Plan of the operation is missing and the operation\n"
 483                             "can't be finished. You might want try to fix this\n"
 484                             "problem by restarting %s\n") % self.parent_name
 485                         self.__g_error_stage(msg)
 486                         return
 487                 except api_errors.ImageplanStateException:
 488                         msg = _("There was an error during installation.\n"
 489                             "The State of the image is incorrect and the operation\n"
 490                             "can't be finished. You might want try to fix this\n"
 491                             "problem by restarting %s\n") % self.parent_name
 492                         self.__g_error_stage(msg)
 493                         return
 494                 except api_errors.CanceledException:
 495                         gobject.idle_add(self.w_dialog.hide)
 496                         self.stop_bouncing_progress = True
 497                         return
 498                 except api_errors.BENamingNotSupported:
 499                         msg = _("Specifying BE Name not supported.\n")
 500                         self.__g_error_stage(msg)
 501                         return
 502                 except api_errors.InvalidBENameException:
 503                         msg = _("Invalid BE Name: %s.\n") % self.proposed_be_name
 504                         self.__g_error_stage(msg)
 505                         return
 506                 except api_errors.PermissionsException, pex:
 507                         msg = str(pex)
 508                         self.__g_error_stage(msg)
 509                         return
 510                 except (api_errors.UnableToCopyBE, 
 511                     api_errors.UnableToMountBE,
 512                     api_errors.BENameGivenOnDeadBE,
 513                     api_errors.UnableToRenameBE), ex:
 514                         msg = str(ex)
 515                         self.__g_error_stage(msg)
 516                         return
 517                 except Exception, uex:
 518                         # We do want to prompt user to load BE admin if there is
 519                         # not enough disk space. This error can either come as an
 520                         # error within API exception, see bug #7642 or as a standalone
 521                         # error, that is why we need to check for both situations.
 522                         if ("error" in uex.__dict__ and isinstance(uex.error, OSError)
 523                             and ("args" in uex.error.__dict__ and uex.error.args and \
 524                             (uex.error.args[0] == errno.EDQUOT or 
 525                             uex.error.args[0] == errno.ENOSPC))) \
 526                             or ("args" in uex.__dict__ and uex.args and (uex.args[0] ==
 527                             errno.EDQUOT or uex.args[0] == errno.ENOSPC)):
 528                                 gobject.idle_add(self.__prompt_to_load_beadm)
 529                                 gobject.idle_add(self.w_dialog.hide)
 530                                 self.stop_bouncing_progress = True
 531                         else:
 532                                 traceback_lines = traceback.format_exc().splitlines()
 533                                 traceback_str = ""
 534                                 for line in traceback_lines: 
 535                                         traceback_str += line + "\n"
 536                                 self.__g_exception_stage(traceback_str)
 537                                 sys.exc_clear()
 538 
 539         def __proceed_with_ipkg_thread(self):
 540                 self.__start_substage(_("Updating %s") % self.parent_name, 
 541                     bounce_progress=True)
 542                 self.__afterplan_information()
 543                 self.prev_pkg = None
 544                 self.__start_substage(_("Downloading..."), bounce_progress=False)
 545                 self.api_o.prepare()
 546                 self.__start_substage(_("Executing..."), bounce_progress=False)
 547                 self.api_o.execute_plan()
 548                 gobject.idle_add(self.__operations_done)
 549 
 550 
 551         def __proceed_with_stages_thread(self):
 552                 self.__start_substage(
 553                     _("Gathering package information, please wait..."))
 554                 stuff_todo = self.__plan_stage()
 555                 if stuff_todo:
 556                         self.__afterplan_information()
 557                         self.prev_pkg = None
 558                         # The api.prepare() mostly is downloading the files so we are
 559                         # Not showing this stage in the main stage dialog. If download 
 560                         # is necessary, then we are showing it in the details view
 561                         if not self.action == enumerations.REMOVE:
 562                                 self.__start_stage_two()
 563                                 self.__start_substage(None, 
 564                                     bounce_progress=False)
 565                         self.api_o.prepare()
 566                         self.__start_stage_three()
 567                         self.__start_substage(None, 
 568                             bounce_progress=False)
 569                         self.api_o.execute_plan()
 570                         gobject.idle_add(self.__operations_done)
 571                 else:
 572                         if self.web_install:
 573                                 gobject.idle_add(self.w_expander.hide)
 574                                 gobject.idle_add(self.__operations_done, 
 575                                     _("All packages already installed."))
 576                                 return
 577                                 
 578                         msg = None
 579                         if self.action == enumerations.INSTALL_UPDATE:
 580                                 msg = _("Selected package(s) cannot be updated on "
 581                                 "their own.\nClick Update All to update all packages.")
 582                         elif self.action == enumerations.IMAGE_UPDATE:
 583                                 msg = _("Your system has already been updated.")
 584                         self.__g_error_stage(msg)
 585 
 586         def __start_stage_one(self):
 587                 self.current_stage_label = self.w_stage1_label
 588                 self.current_stage_icon = self.w_stage1_icon
 589                 self.__start_stage(self.stages.get(1))
 590                 self.__g_update_details_text(self.stages.get(1)[0]+"\n", "bold")
 591                 
 592         def __start_stage_two(self):
 593                 # End previous stage
 594                 self.__end_stage()
 595                 self.current_stage_label = self.w_stage2_label
 596                 self.current_stage_icon = self.w_stage2_icon
 597                 self.__start_stage(self.stages.get(2))
 598                 self.__g_update_details_text(self.stages.get(2)[0]+"\n", "bold")
 599 
 600         def __start_stage_three(self):
 601                 self.__end_stage()
 602                 self.current_stage_label = self.w_stage3_label
 603                 self.current_stage_icon = self.w_stage3_icon
 604                 self.__start_stage(self.stages.get(3))
 605                 self.__g_update_details_text(self.stages.get(3)[0]+"\n", "bold")
 606 
 607         def __start_stage(self, stage_text):
 608                 self.current_stage_label_done = stage_text[1]
 609                 gobject.idle_add(self.current_stage_label.set_markup, 
 610                     "<b>"+stage_text[0]+"</b>")
 611                 gobject.idle_add(self.current_stage_icon.set_from_stock, 
 612                     gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_MENU)
 613 
 614         def __end_stage(self):
 615                 gobject.idle_add(self.current_stage_label.set_text, 
 616                     self.current_stage_label_done)
 617                 gobject.idle_add(self.current_stage_icon.set_from_pixbuf, self.done_icon)
 618 
 619         def __g_error_stage(self, msg):
 620                 if msg == None or len(msg) == 0:
 621                         msg = _("No futher information available") 
 622                 self.operations_done = True
 623                 self.stop_bouncing_progress = True
 624                 self.__g_update_details_text(_("\nError:\n"), "bold")
 625                 self.__g_update_details_text("%s" % msg, "level1")
 626                 self.__g_update_details_text("\n")
 627                 txt = "<b>" + self.current_stage_label_done + _(" - Failed </b>")
 628                 gobject.idle_add(self.current_stage_label.set_markup, txt)
 629                 gobject.idle_add(self.current_stage_icon.set_from_stock, 
 630                     gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU)
 631                 gobject.idle_add(self.w_expander.set_expanded, True)
 632                 gobject.idle_add(self.w_cancel_button.set_sensitive, True)
 633 
 634         def __g_exception_stage(self, tracebk):
 635                 self.operations_done = True
 636                 self.stop_bouncing_progress = True
 637                 txt = "<b>" + self.current_stage_label_done + _(" - Failed </b>")
 638                 gobject.idle_add(self.current_stage_label.set_markup, txt)
 639                 gobject.idle_add(self.current_stage_icon.set_from_stock, 
 640                     gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU)
 641                 msg_1 = _("An unknown error occurred in the %s stage.\n"
 642                     "Please let the developers know about this problem\n"
 643                     "by filing a bug together with exception value at:\n"
 644                     ) % self.current_stage_name
 645                 msg_2 = _("http://defect.opensolaris.org\n\n")
 646                 msg_3 = _("Exception value:\n")
 647                 self.__g_update_details_text(_("\nError:\n"), "bold")
 648                 self.__g_update_details_text("%s" % msg_1, "level1")
 649                 self.__g_update_details_text("%s" % msg_2, "bold", "level2")
 650                 if tracebk:
 651                         msg = _("Exception traceback:\n")
 652                         self.__g_update_details_text("%s" % msg, 
 653                             "bold","level1")
 654                         self.__g_update_details_text("%s\n" % tracebk, "level2")
 655                 else:
 656                         msg = _("No futher information available")
 657                         self.__g_update_details_text("%s\n" % msg, "level2")
 658                 gobject.idle_add(self.w_expander.set_expanded, True)
 659                 gobject.idle_add(self.w_cancel_button.set_sensitive, True)
 660 
 661         def __start_substage(self, text, bounce_progress=True):
 662                 if text:
 663                         gobject.idle_add(self.__stages_label_set_markup, text)
 664                         self.__g_update_details_text(text + "\n")
 665                 if bounce_progress:
 666                         if self.stopped_bouncing_progress:
 667                                 self.__start_bouncing_progress()
 668                 else:
 669                         self.stop_bouncing_progress = True
 670 
 671         def __stages_label_set_markup(self, markup_text):
 672                 if not self.canceling == True:
 673                         self.w_stages_label.set_markup(markup_text)
 674 
 675         def __start_bouncing_progress(self):
 676                 self.stop_bouncing_progress = False
 677                 self.stopped_bouncing_progress = False
 678                 Thread(target = 
 679                     self.__g_progressdialog_progress_pulse).start()
 680 
 681         def __g_progressdialog_progress_pulse(self):
 682                 while not self.stop_bouncing_progress:
 683                         gobject.idle_add(self.w_progressbar.pulse)
 684                         time.sleep(0.1)
 685                 self.stopped_bouncing_progress = True
 686 
 687         def __g_update_details_text(self, text, *tags):
 688                 gobject.idle_add(self.__update_details_text, text, *tags)
 689 
 690         def __update_details_text(self, text, *tags):
 691                 buf = self.w_details_textview.get_buffer()
 692                 textiter = buf.get_end_iter()
 693                 if tags:
 694                         buf.insert_with_tags_by_name(textiter, text, *tags)
 695                 else:
 696                         buf.insert(textiter, text)
 697                 self.w_details_textview.scroll_to_iter(textiter, 0.0)
 698                 
 699         def __update_download_progress(self, cur_bytes, total_bytes):
 700                 prog = float(cur_bytes)/total_bytes
 701                 self.w_progressbar.set_fraction(prog)
 702                 size_a_str = ""
 703                 size_b_str = ""
 704                 if cur_bytes >= 0:
 705                         size_a_str = pkg.misc.bytes_to_str(cur_bytes)
 706                 if total_bytes >= 0:
 707                         size_b_str = pkg.misc.bytes_to_str(total_bytes)
 708                 c = _("Downloaded %(current)s of %(total)s") % \
 709                     {"current" : size_a_str,
 710                     "total" : size_b_str}
 711                 self.__stages_label_set_markup(c)
 712 
 713         def __update_install_progress(self, current, total):
 714                 prog = float(current)/total
 715                 self.w_progressbar.set_fraction(prog)
 716 
 717         def __plan_stage(self):
 718                 '''Function which plans the image'''
 719                 stuff_to_do = False
 720                 if self.action == enumerations.INSTALL_UPDATE:
 721                         stuff_to_do, cre = self.api_o.plan_install(
 722                             self.list_of_packages, refresh_catalogs = False, 
 723                             filters = [])
 724                         if cre and not cre.succeeded:
 725                                 # cre is either None or a catalog refresh exception 
 726                                 # which was caught while planning.
 727                                 raise api_errors.CatalogRefreshException(None, None, None
 728                                     ,_("Catalog refresh failed during install."))
 729                 elif self.action == enumerations.REMOVE:
 730                         plan_uninstall = self.api_o.plan_uninstall
 731                         stuff_to_do = \
 732                             plan_uninstall(self.list_of_packages, False, False)
 733                 elif self.action == enumerations.IMAGE_UPDATE:  
 734                         # we are passing force, since we already checked if the
 735                         # SUNWipkg and SUNWipkg-gui are up to date.
 736                         stuff_to_do, opensolaris_image, cre = \
 737                             self.api_o.plan_update_all(sys.argv[0],
 738                             refresh_catalogs = False,
 739                             noexecute = False, force = True,
 740                             be_name = self.proposed_be_name)
 741                         if cre and not cre.succeeded:
 742                                 raise api_errors.CatalogRefreshException(None, None, None
 743                                     ,_("Catalog refresh failed during Update All."))
 744                 return stuff_to_do
 745 
 746         def __operations_done(self, alternate_done_txt = None):
 747                 done_txt = _("Installation completed successfully.")
 748                 if self.action == enumerations.REMOVE:
 749                         done_txt = _("Packages removed successfully.")
 750                 elif self.action == enumerations.IMAGE_UPDATE:
 751                         done_txt = _("Packages updated successfully.")
 752                 if alternate_done_txt != None:
 753                         done_txt = alternate_done_txt
 754                 self.w_stages_box.hide()
 755                 self.w_stages_icon.set_from_stock(
 756                     gtk.STOCK_OK, gtk.ICON_SIZE_DND)
 757                 self.w_stages_icon.show()
 758                 self.__stages_label_set_markup(done_txt)
 759                 self.__update_details_text("\n"+ done_txt, "bold")
 760                 self.w_cancel_button.set_label("gtk-close")
 761                 self.w_progressbar.hide()
 762                 self.stop_bouncing_progress = True
 763                 self.operations_done = True
 764                 if self.parent != None:
 765                         if not self.web_install and not self.ips_update \
 766                             and not self.action == enumerations.IMAGE_UPDATE:
 767                                 self.parent.update_package_list(self.update_list)
 768                         if self.web_install:
 769                                 self.web_updates_list = self.update_list
 770                 if self.ips_update:
 771                         self.w_dialog.hide()
 772                         self.parent.restart_after_ips_update(self.proposed_be_name)
 773                 elif self.action == enumerations.IMAGE_UPDATE:
 774                         self.w_dialog.hide()
 775                         self.parent.shutdown_after_image_update()
 776 
 777         def __prompt_to_load_beadm(self):
 778                 msgbox = gtk.MessageDialog(parent = self.w_main_window,
 779                     buttons = gtk.BUTTONS_OK_CANCEL, flags = gtk.DIALOG_MODAL,
 780                     type = gtk.MESSAGE_ERROR, 
 781                     message_format = _(
 782                         "Not enough disk space, the selected action cannot "
 783                         "be performed.\n\n"
 784                         "Click OK to manage your existing BEs and free up disk space or "
 785                         "Cancel to cancel the action."))
 786                 msgbox.set_title(_("Not Enough Disk Space"))
 787                 result = msgbox.run()
 788                 msgbox.destroy()
 789                 if result == gtk.RESPONSE_OK:
 790                         beadm.Beadmin(self.parent)
 791 
 792         def __afterplan_information(self):
 793                 install_iter = None 
 794                 update_iter = None
 795                 remove_iter = None
 796                 plan = self.api_o.describe().get_changes()
 797                 self.__g_update_details_text("\n")
 798                 for pkg_plan in plan:
 799                         origin_fmri = pkg_plan[0]
 800                         destination_fmri = pkg_plan[1]
 801                         if origin_fmri and destination_fmri:
 802                                 if not update_iter:
 803                                         update_iter = True
 804                                         txt = _("Packages To Be Updated:\n")
 805                                         self.__g_update_details_text(txt, "bold")
 806                                 pkg_a = self.__get_pkgstr_from_pkginfo(destination_fmri)
 807                                 self.__g_update_details_text(pkg_a+"\n", "level1")
 808                         elif not origin_fmri and destination_fmri:
 809                                 if not install_iter:
 810                                         install_iter = True
 811                                         txt = _("Packages To Be Installed:\n")
 812                                         self.__g_update_details_text(txt, "bold")
 813                                 pkg_a = self.__get_pkgstr_from_pkginfo(destination_fmri)
 814                                 self.__g_update_details_text(pkg_a+"\n", "level1")
 815                         elif origin_fmri and not destination_fmri:
 816                                 if not remove_iter:
 817                                         remove_iter = True
 818                                         txt = _("Packages To Be Removed:\n")
 819                                         self.__g_update_details_text(txt, "bold")
 820                                 pkg_a = self.__get_pkgstr_from_pkginfo(origin_fmri)
 821                                 self.__g_update_details_text(pkg_a+"\n", "level1")
 822                 self.__g_update_details_text("\n")
 823 
 824         def __get_pkgstr_from_pkginfo(self, pkginfo):
 825                 dt_str = self.get_datetime(pkginfo.packaging_date)
 826                 if not dt_str:
 827                         dt_str = ""
 828                 s_ver = pkginfo.version
 829                 s_bran = pkginfo.branch
 830                 pkg_name = pkginfo.pkg_stem
 831                 pkg_publisher = pkginfo.publisher
 832                 if not pkg_publisher in self.update_list:
 833                         self.update_list[pkg_publisher] = []
 834                 pub_list = self.update_list.get(pkg_publisher)
 835                 if not pkg_name in pub_list:
 836                         pub_list.append(pkg_name)
 837                 l_ver = 0
 838                 version_pref = ""
 839                 while l_ver < len(s_ver) -1:
 840                         version_pref += "%d%s" % (s_ver[l_ver],".")
 841                         l_ver += 1
 842                 version_pref += "%d%s" % (s_ver[l_ver],"-")
 843                 l_ver = 0
 844                 version_suf = ""
 845                 if s_bran != None:
 846                         while l_ver < len(s_bran) -1:
 847                                 version_suf += "%d%s" % (s_bran[l_ver],".")
 848                                 l_ver += 1
 849                         version_suf += "%d" % s_bran[l_ver]
 850                 pkg_version = version_pref + version_suf + dt_str
 851                 return pkg_name + "@" + pkg_version  
 852 
 853         def act_output(self):
 854                 if self.act_phase != self.act_phase_last:
 855                         self.act_phase_last = self.act_phase
 856                         gobject.idle_add(self.__stages_label_set_markup, self.act_phase)
 857                         self.__g_update_details_text(_("%s\n") % self.act_phase, "level1")
 858                 gobject.idle_add(self.__update_install_progress,
 859                     self.act_cur_nactions, self.act_goal_nactions)
 860                 return
 861 
 862         def act_output_done(self):
 863                 return
 864 
 865         def cat_output_start(self): 
 866                 return
 867 
 868         def cat_output_done(self): 
 869                 return
 870 
 871         def cache_cats_output_start(self):
 872                 return
 873 
 874         def cache_cats_output_done(self):
 875                 return
 876 
 877         def load_cat_cache_output_start(self):
 878                 return
 879 
 880         def load_cat_cache_output_done(self):
 881                 return
 882 
 883         def dl_output(self):
 884                 gobject.idle_add(self.__update_download_progress, \
 885                     self.dl_cur_nbytes, self.dl_goal_nbytes)
 886                 if self.prev_pkg != self.dl_cur_pkg:
 887                         self.prev_pkg = self.dl_cur_pkg
 888                         self.__g_update_details_text(
 889                             _("Package %d of %d: %s\n") % (self.dl_cur_npkgs+1, 
 890                             self.dl_goal_npkgs, self.dl_cur_pkg), "level1")
 891 
 892         def dl_output_done(self):
 893                 self.__g_update_details_text("\n")
 894 
 895         def eval_output_start(self):
 896                 '''Called by progress tracker when the evaluation of the packages just 
 897                 started.'''
 898                 return
 899 
 900         def eval_output_progress(self):
 901                 '''Called by progress tracker each time some package was evaluated. The
 902                 call is being done by calling progress tracker evaluate_progress() 
 903                 function'''
 904                 if self.prev_pkg != self.eval_cur_fmri:
 905                         self.prev_pkg = self.eval_cur_fmri
 906                         self.__g_update_details_text("%s\n" % self.eval_cur_fmri,
 907                             "level1")
 908                         text = _("Evaluating: %s") % self.eval_cur_fmri.get_name()
 909                         gobject.idle_add(self.__stages_label_set_markup, text)
 910 
 911         def eval_output_done(self):
 912                 return
 913 
 914         def ind_output(self):
 915                 if self.ind_started != self.ind_phase:
 916                         self.ind_started = self.ind_phase
 917                         gobject.idle_add(self.__stages_label_set_markup, self.ind_phase)
 918                         self.__g_update_details_text(
 919                             _("%s\n") % (self.ind_phase), "level1")
 920                 gobject.idle_add(self.__indexing_progress)
 921 
 922         def __indexing_progress(self):
 923                 #It doesn't look nice if the progressive is just for few elements
 924                 if self.ind_goal_nitems > MIN_IND_ELEMENTS_BOUNCE:
 925                         gobject.idle_add(self.__update_install_progress, 
 926                             self.ind_cur_nitems-1, self.ind_goal_nitems)
 927                 else:
 928                         if self.stopped_bouncing_progress:
 929                                 self.__start_bouncing_progress()
 930                         
 931         def ind_output_done(self):
 932                 gobject.idle_add(self.__update_install_progress, self.ind_cur_nitems, 
 933                     self.ind_goal_nitems)
 934 
 935         def ver_output(self): 
 936                 return
 937 
 938         def ver_output_error(self, actname, errors): 
 939                 return
 940 
 941         @staticmethod
 942         def get_datetime(date_time):
 943                 '''Support function for getting date from the API.'''
 944                 date_tmp = None
 945                 try:
 946                         date_tmp = time.strptime(date_time, "%a %b %d %H:%M:%S %Y")
 947                 except ValueError:
 948                         return None
 949                 if date_tmp:
 950                         date_tmp2 = datetime.datetime(*date_tmp[0:5])        
 951                         return date_tmp2.strftime(":%m%d")
 952                 return None
 953 
 954         def __installed_fmris_from_args(self, args_f):
 955                 found = []
 956                 notfound = []
 957                 try:
 958                         for m in self.api_o.img.inventory(args_f):
 959                                 found.append(m[0])
 960                 except api_errors.InventoryException, e:
 961                         notfound = e.notfound
 962                 return notfound
 963