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