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