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