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