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