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 = os.path.join(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