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 2008 Sun Microsystems, Inc. All rights reserved.
23 # Use is subject to license terms.
24 #
25
26 import errno
27 import gettext # XXX Temporary workaround
28 import itertools
29 import os
30 import sys
31 from threading import Thread
32 from urllib2 import URLError
33 try:
34 import gobject
35 import gtk
36 import gtk.glade
37 import pygtk
38 pygtk.require("2.0")
39 except ImportError:
40 sys.exit(1)
41 import pkg.client.bootenv as bootenv
42 import pkg.client.imageplan as imageplan
43 import pkg.client.progress as progress
44 import pkg.fmri as fmri
45 import pkg.indexer as indexer
46 import pkg.search_errors as search_errors
47 from pkg.misc import TransferTimedOutException
48 from pkg.misc import CLIENT_DEFAULT_MEM_USE_KB
49 import pkg.gui.enumerations as enumerations
50 import pkg.gui.filelist as filelist
51 import pkg.gui.thread as guithread
52 from pkg.gui.filelist import CancelException
53
54 class InstallUpdate(progress.ProgressTracker):
55 def __init__(self, install_list, parent, \
56 image_update = False, ips_update = False):
57 # XXX Workaround as BE is using msg(_("message"))
58 # which bypasses the self._ mechanism the GUI is using
59 gettext.install("pkg","/usr/lib/locale")
60 progress.ProgressTracker.__init__(self)
61 self.gui_thread = guithread.ThreadRun()
62 self.install_list = install_list
63 self.update_list = None
64 self.parent = parent
65 self.image_update = image_update
66 self.ips_update = ips_update
67 self.ip = None
68 w_tree_createplan = gtk.glade.XML(parent.gladefile, "createplandialog")
69 w_tree_installupdate = gtk.glade.XML(parent.gladefile, "installupdate")
70 w_tree_downloadingfiles = \
71 gtk.glade.XML(parent.gladefile, "downloadingfiles")
72 w_tree_installingdialog = \
73 gtk.glade.XML(parent.gladefile, "installingdialog")
74 w_tree_networkdown = gtk.glade.XML(parent.gladefile, "networkdown")
75 self.w_createplan_dialog = \
76 w_tree_createplan.get_widget("createplandialog")
77 self.w_createplan_progressbar = \
78 w_tree_createplan.get_widget("createplanprogress")
79 self.w_createplan_textview = \
80 w_tree_createplan.get_widget("createplantextview")
81 self.w_installupdate_dialog = w_tree_installupdate.get_widget("installupdate")
82 self.w_summary_label = w_tree_installupdate.get_widget("packagenamelabel3")
83 self.w_review_treeview = w_tree_installupdate.get_widget("treeview1")
84 self.w_downloadingfiles_dialog = \
85 w_tree_downloadingfiles.get_widget("downloadingfiles")
86 self.w_download_textview = \
87 w_tree_downloadingfiles.get_widget("downloadtextview")
88 self.w_download_progressbar = \
89 w_tree_downloadingfiles.get_widget("downloadprogress")
90 self.w_installing_dialog = \
91 w_tree_installingdialog.get_widget("installingdialog")
92 self.w_installing_textview = \
93 w_tree_installingdialog.get_widget("installingtextview")
94 self.w_installing_progressbar = \
95 w_tree_installingdialog.get_widget("installingprogress")
96 self.w_networkdown_dialog = w_tree_networkdown.get_widget("networkdown")
97 self.w_createplan_progressbar.set_pulse_step(0.1)
98 installed_updated_column = gtk.TreeViewColumn('Installed/Updated')
99 self.w_review_treeview.append_column(installed_updated_column)
100 cell = gtk.CellRendererText()
101 installed_updated_column.pack_start(cell, True)
102 installed_updated_column.add_attribute(cell, 'text', 0)
103 self.w_review_treeview.expand_all()
104 try:
105 dic_createplan = \
106 {
107 "on_cancelcreateplan_clicked": \
108 self.__on_cancelcreateplan_clicked,
109 }
110 dic_installupdate = \
111 {
112 "on_cancel_button_clicked": \
113 self.__on_cancel_button_clicked,
114 "on_next_button_clicked":self.__on_next_button_clicked,
115 }
116 dic_downloadingfiles = \
117 {
118 "on_canceldownload_clicked": \
119 self.__on_cancel_download_clicked,
120 }
121 dic_networkdown = \
122 {
123 "on_networkdown_close_clicked": \
124 self.__on_networkdown_close_clicked,
125 }
126 w_tree_createplan.signal_autoconnect(dic_createplan)
127 w_tree_installupdate.signal_autoconnect(dic_installupdate)
128 w_tree_downloadingfiles.signal_autoconnect(dic_downloadingfiles)
129 w_tree_networkdown.signal_autoconnect(dic_networkdown)
130 except AttributeError, error:
131 print self.parent._('GUI will not respond to any event! %s. \
132 Check installupdate.py signals') \
133 % error
134 if image_update or ips_update:
135 list_of_packages = install_list
136 else:
137 list_of_packages = self.__prepare_list_of_packages()
138 thread = Thread(target = self.__plan_the_install_updateimage, \
139 args = (list_of_packages, ))
140 thread.start()
141 self.w_createplan_dialog.run()
142
143 def __on_cancelcreateplan_clicked(self, widget):
144 '''Handler for signal send by cancel button, which user might press during
145 evaluation stage - while the dialog is creating plan'''
146 self.gui_thread.cancel()
147 self.w_createplan_dialog.destroy()
148
149 def __on_cancel_button_clicked(self, widget):
150 '''Handler for signal send by cancel button, which is available for the
151 user after evaluation stage on the dialog showing what will be installed
152 or updated'''
153 self.gui_thread.cancel()
154 self.w_installupdate_dialog.destroy()
155
156 def __on_next_button_clicked(self, widget):
157 '''Handler for signal send by next button, which is available for the
158 user after evaluation stage on the dialog showing what will be installed
159 or updated'''
160 download_thread = Thread(target = self.__download_stage, args = ())
161 download_thread.start()
162
163 def __on_cancel_download_clicked(self, widget):
164 '''Handler for signal send by cancel button, which user might press during
165 download stage.'''
166 self.gui_thread.cancel()
167 self.w_downloadingfiles_dialog.destroy()
168
169 def __on_networkdown_close_clicked(self, widget):
170 '''Handler for signal send by close button on the dialog showing that
171 there was some problem with the network connection.'''
172 self.gui_thread.cancel()
173 self.w_networkdown_dialog.destroy()
174
175 def __update_createplan_progress(self, action):
176 buf = self.w_createplan_textview.get_buffer()
177 textiter = buf.get_end_iter()
178 buf.insert(textiter, action)
179 self.w_createplan_progressbar.pulse()
180
181 def __update_download_progress(self, cur_bytes, total_bytes):
182 prog = float(cur_bytes)/total_bytes
183 self.w_download_progressbar.set_fraction(prog)
184 a = str(cur_bytes/1024)
185 b = str(total_bytes/1024)
186 c = "Downloaded: " + a + " / " + b + " KB"
187 self.w_download_progressbar.set_text(c)
188
189 def __update_install_progress(self, current, total):
190 prog = float(current)/total
191 self.w_installing_progressbar.set_fraction(prog)
192
193 def __prepare_list_of_packages(self):
194 ''' This method return the dictionary of
195 images and newest marked packages'''
196 fmri_to_install_update = {}
197 for row in self.install_list:
198 if row[enumerations.MARK_COLUMN]:
199 image = row[enumerations.IMAGE_OBJECT_COLUMN]
200 packages = row[enumerations.PACKAGE_OBJECT_COLUMN]
201 im = fmri_to_install_update.get(image)
202 # XXX Hack to be bug to bug compatible - incorporations
203 pkg_name = packages[0].get_name()
204 if im:
205 im.append(pkg_name)
206 else:
207 fmri_to_install_update[image] = [pkg_name, ]
208 return fmri_to_install_update
209
210 def __plan_the_install_updateimage(self, list_of_packages):
211 '''Function which plans the image'''
212 self.gui_thread.run()
213 filters = []
214 verbose = False
215 noexecute = False
216 for image in list_of_packages:
217 # Take a list of packages, specified in pkg_list, and attempt
218 # to assemble an appropriate image plan. This is a helper
219 # routine for some common operations in the client.
220 #
221 # This method checks all authorities for a package match;
222 # however, it defaults to choosing the preferred authority
223 # when an ambiguous package name is specified. If the user
224 # wishes to install a package from a non-preferred authority,
225 # the full FMRI that contains an authority should be used
226 # to name the package.
227
228 pkg_list = list_of_packages.get(image)
229
230 error = 0
231 self.ip = imageplan.ImagePlan(image, self, filters = filters)
232
233 self.__load_optional_dependencies(image)
234
235 for p in pkg_list:
236 try:
237 conp = image.apply_optional_dependencies(p)
238 matches = list(image.inventory([ conp ],
239 all_known = True))
240 except RuntimeError:
241 # XXX Module directly printing.
242 error = 1
243 continue
244
245 pnames = {}
246 pmatch = []
247 npnames = {}
248 npmatch = []
249 for m, state in matches:
250 if m.preferred_authority():
251 pnames[m.get_pkg_stem()] = 1
252 pmatch.append(m)
253 else:
254 npnames[m.get_pkg_stem()] = 1
255 npmatch.append(m)
256
257 if len(pnames.keys()) > 1:
258 # XXX Module directly printing.
259 error = 1
260 continue
261 elif len(pnames.keys()) < 1 and len(npnames.keys()) > 1:
262 # XXX Module directly printing.
263 error = 1
264 continue
265
266 # matches is a list reverse sorted by version, so take
267 # the first; i.e., the latest.
268 if len(pmatch) > 0:
269 self.ip.propose_fmri(pmatch[0])
270 else:
271 self.ip.propose_fmri(npmatch[0])
272
273 if error != 0:
274 raise RuntimeError, "Unable to assemble image plan"
275
276
277 self.__evaluate(image)
278
279 image.imageplan = self.ip
280
281 gobject.idle_add(self.ip.progtrack.evaluate_done)
282
283 return
284
285 def __load_optional_dependencies(self, image):
286 for b_fmri in image.gen_installed_pkgs():
287 if self.gui_thread.is_cancelled():
288 return
289 mfst = image.get_manifest(b_fmri, filtered = True)
290
291 for dep in mfst.gen_actions_by_type("depend"):
292 required, min_fmri, max_fmri = dep.parse(image)
293 if required == False:
294 image.update_optional_dependency(min_fmri)
295
296 def __evaluate(self, image):
297 assert self.ip.state == imageplan.UNEVALUATED
298
299 self.ip.progtrack.evaluate_start()
300
301 outstring = ""
302
303 # Operate on a copy, as it will be modified in flight.
304 for f in self.ip.target_fmris[:]:
305 self.ip.progtrack.evaluate_progress()
306 try:
307 self.__evaluate_fmri(f, image)
308 except KeyError, e:
309 outstring += "Attemping to install %s causes:\n\t%s\n" % \
310 (f.get_name(), e)
311 except NameError:
312 gobject.idle_add(self.__creating_plan_net_error)
313 return
314 if outstring:
315 raise RuntimeError("No packages were installed because "
316 "package dependencies could not be satisfied\n" +
317 outstring)
318
319 for f in self.ip.target_fmris:
320 self.ip.add_pkg_plan(f)
321 self.ip.progtrack.evaluate_progress()
322
323 for f in self.ip.target_rem_fmris[:]:
324 self.ip.evaluate_fmri_removal(f)
325 self.ip.progtrack.evaluate_progress()
326
327 self.ip.progtrack.evaluate_done()
328
329 self.ip.state = imageplan.EVALUATED_OK
330
331 def __creating_plan_net_error(self):
332 '''Helper method which shows the dialog informing user that there was
333 problem with network connection'''
334 self.w_createplan_dialog.hide()
335 self.w_networkdown_dialog.show()
336
337 def __evaluate_fmri(self, pfmri, image):
338
339 if self.gui_thread.is_cancelled():
340 return
341 gobject.idle_add(self.__update_createplan_progress, \
342 self.parent._("Evaluating: %s\n") % pfmri.get_fmri())
343
344 self.ip.progtrack.evaluate_progress()
345 m = image.get_manifest(pfmri)
346
347 # [manifest] examine manifest for dependencies
348 for a in m.actions:
349 if a.name != "depend":
350 continue
351
352 type = a.attrs["type"]
353
354 f = fmri.PkgFmri(a.attrs["fmri"],
355 self.ip.image.attrs["Build-Release"])
356
357 if self.ip.image.has_version_installed(f) and \
358 type != "exclude":
359 continue
360
361 # XXX This alone only prevents infinite recursion when a
362 # cycle member is on the commandline, as we never update
363 # target_fmris. Is target_fmris supposed to be just
364 # what was specified on the commandline, or include what
365 # we've found while processing dependencies?
366 # XXX probably should just use propose_fmri() here
367 # instead of this and the has_version_installed() call
368 # above.
369 if self.ip.is_proposed_fmri(f):
370 continue
371
372 # XXX LOG "%s not in pending transaction;
373 # checking catalog" % f
374
375 required = True
376 excluded = False
377 if type == "optional" and \
378 not self.ip.image.attrs["Policy-Require-Optional"]:
379 required = False
380 elif type == "transfer" and \
381 not self.ip.image.older_version_installed(f):
382 required = False
383 elif type == "exclude":
384 excluded = True
385 elif type == "incorporate":
386 self.ip.image.update_optional_dependency(f)
387 if self.ip.image.older_version_installed(f) or \
388 self.ip.older_version_proposed(f):
389 required = True
390 else:
391 required = False
392
393 if not required:
394 continue
395
396 if excluded:
397 raise RuntimeError, "excluded by '%s'" % f
398
399 # treat-as-required, treat-as-required-unless-pinned,
400 # ignore
401 # skip if ignoring
402 # if pinned
403 # ignore if treat-as-required-unless-pinned
404 # else
405 # **evaluation of incorporations**
406 # [imageplan] pursue installation of this package
407 # -->
408 # backtrack or reset??
409
410 # This will be the newest version of the specified
411 # dependency package, coming from the preferred
412 # authority, if it's available there.
413 cf = self.ip.image.inventory([ a.attrs["fmri"] ],
414 all_known = True, preferred = True,
415 first_only = True).next()[0]
416
417 # XXX LOG "adding dependency %s" % pfmri
418
419 #msg("adding dependency %s" % cf)
420
421 self.ip.propose_fmri(cf)
422 self.__evaluate_fmri(cf, image)
423
424 def __download_stage(self, rebuild=False):
425 '''Parts of the code duplicated from install and image-update from pkg(1)
426 and pkg.client.ImagePlan.preexecute()'''
427 self.gui_thread.run()
428 if not rebuild:
429 self.w_installupdate_dialog.hide()
430 if rebuild:
431 self.w_installing_dialog.hide()
432 self.w_downloadingfiles_dialog.show()
433
434 # Checks the index to make sure it exists and is
435 # consistent. If it's inconsistent an exception is thrown.
436 # If it's totally absent, it will index the existing packages
437 # so that the incremental update that follows at the end of
438 # the function will work correctly.
439 self.ip.image.update_index_dir()
440 ind = indexer.Indexer(self.ip.image.index_dir,
441 CLIENT_DEFAULT_MEM_USE_KB, progtrack=self.ip.progtrack)
442 ind.check_index(self.ip.image.get_fmri_manifest_pairs(),
443 force_rebuild=False)
444
445 for package_plan in self.ip.pkg_plans:
446 if self.gui_thread.is_cancelled():
447 return
448 try:
449 self.__preexecute(package_plan)
450 except TransferTimedOutException:
451 self.w_downloadingfiles_dialog.hide()
452 self.w_networkdown_dialog.show()
453 return
454 except URLError, e:
455 #if e.reason[0] == 8:
456 self.w_downloadingfiles_dialog.hide()
457 self.w_networkdown_dialog.show()
458 return
459 except CancelException:
460 self.w_downloadingfiles_dialog.hide()
461 return
462
463 self.ip.progtrack.download_done()
464 self.w_downloadingfiles_dialog.hide()
465
466 try:
467 be = bootenv.BootEnv(self.ip.image.get_root())
468 except RuntimeError:
469 be = bootenv.BootEnvNull(self.ip.image.get_root())
470
471 if self.image_update:
472 be.init_image_recovery(self.ip.image)
473 if self.ip.image.is_liveroot():
474 return 1
475 try:
476 self.__installation_stage()
477 if self.image_update:
478 be.activate_image()
479 else:
480 be.activate_install_uninstall()
481 ret_code = 0
482 except RuntimeError, e:
483 if self.image_update:
484 be.restore_image()
485 else:
486 be.restore_install_uninstall()
487 ret_code = 1
488 except search_errors.InconsistentIndexException, e:
489 ret_code = 2
490 except search_errors.PartialIndexingException, e:
491 ret_code = 2
492 except search_errors.ProblematicPermissionsIndexException, e:
493 ret_code = 2
494 except Exception, e:
495 if self.image_update:
496 be.restore_image()
497 else:
498 be.restore_install_uninstall()
499 self.ip.image.cleanup_downloads()
500 raise
501
502 self.ip.image.cleanup_downloads()
503 if ret_code == 0:
504 self.ip.image.cleanup_cached_content()
505 elif ret_code == 2:
506 return_code = 0
507 return_code = self.__rebuild_index()
508 if return_code == 1:
509 return
510 self.__download_stage(True)
511
512 def __preexecute(self, package_plan):
513 '''Code duplication from pkg.client.PkgPlan.preexecute() except that
514 pkg.gui.filelist is called instead of pkg.client.fileobject with shared
515 cancel object - self.gui_thread that allows to cancel download
516 operation'''
517 flist = filelist.FileList(self,
518 package_plan.image,
519 package_plan.destination_fmri,
520 self.gui_thread,
521 maxbytes = None
522 )
523 _PkgPlan__prog = package_plan._PkgPlan__progtrack
524 _PkgPlan__prog.download_start_pkg(package_plan.get_xfername())
525
526 # retrieval step
527 if package_plan.destination_fmri == None:
528 package_plan.image.remove_install_file(package_plan.origin_fmri)
529
530 try:
531 os.unlink("%s/pkg/%s/filters" % (
532 package_plan.image.imgdir,
533 package_plan.origin_fmri.get_dir_path()))
534 except EnvironmentError, e:
535 if e.errno != errno.ENOENT:
536 raise
537
538 for src, dest in itertools.chain(*package_plan.actions):
539 if dest:
540 dest.preinstall(package_plan, src)
541 if dest.needsdata(src):
542 flist.add_action(dest)
543 else:
544 src.preremove(package_plan)
545
546 # Tell flist to get any remaining files
547 flist.flush()
548 package_plan._PkgPlan__progtrack.download_end_pkg()
549
550 def __rebuild_index(self, pargs):
551 '''Code duplication from pkg(1):
552 Forcibly rebuild the search indexes. Will remove existing indexes
553 and build new ones from scratch.'''
554 quiet = False
555
556 try:
557 self.ip.image.rebuild_search_index(self.ip.image.progtrack)
558 except search_errors.InconsistentIndexException, iie:
559 return 1
560 except search_errors.ProblematicPermissionsIndexException, ppie:
561 return 1
562
563 def __installation_stage(self):
564 self.gui_thread.run()
565 self.w_installing_dialog.show()
566 self.ip.state = imageplan.PREEXECUTED_OK
567
568 if self.ip.nothingtodo():
569 self.ip.state = imageplan.EXECUTED_OK
570 self.ip.progtrack.actions_done()
571 return
572
573 actions = [ (p, src, dest)
574 for p in self.ip.pkg_plans
575 for src, dest in p.gen_removal_actions()
576 ]
577
578 actions.sort(key = lambda obj:obj[1], reverse=True)
579
580 self.ip.progtrack.actions_set_goal("Removal Phase", len(actions))
581 for p, src, dest in actions:
582 p.execute_removal(src, dest)
583 self.ip.progtrack.actions_add_progress()
584
585 # generate list of update actions, sort and execute
586
587 update_actions = [ (p, src, dest)
588 for p in self.ip.pkg_plans
589 for src, dest in p.gen_update_actions()
590 ]
591
592 install_actions = [ (p, src, dest)
593 for p in self.ip.pkg_plans
594 for src, dest in p.gen_install_actions()
595 ]
596
597 # move any user/group actions into modify list to
598 # permit package to add user/group and change existing
599 # files to that user/group in a single update
600 # iterate over copy since we're modify install_actions
601
602 for a in install_actions[:]:
603 if a[2].name == "user" or a[2].name == "group":
604 update_actions.append(a)
605 install_actions.remove(a)
606
607 update_actions.sort(key = lambda obj:obj[2])
608
609 self.ip.progtrack.actions_set_goal("Update Phase", len(update_actions))
610
611 for p, src, dest in update_actions:
612 p.execute_update(src, dest)
613 self.ip.progtrack.actions_add_progress()
614
615 # generate list of install actions, sort and execute
616
617 install_actions.sort(key = lambda obj:obj[2])
618
619 self.ip.progtrack.actions_set_goal("Install Phase", len(install_actions))
620
621 for p, src, dest in install_actions:
622 p.execute_install(src, dest)
623 self.ip.progtrack.actions_add_progress()
624
625 # handle any postexecute operations
626
627 for p in self.ip.pkg_plans:
628 p.postexecute()
629
630 self.ip.state = imageplan.EXECUTED_OK
631
632 del actions
633 del update_actions
634 del install_actions
635 del self.ip.target_rem_fmris
636 del self.ip.target_fmris
637 del self.ip.directories
638
639 # Perform the incremental update to the search indexes
640 # for all changed packages
641 plan_info = []
642 for p in self.ip.pkg_plans:
643 d_fmri = p.destination_fmri
644 d_manifest_path = None
645 if d_fmri:
646 d_manifest_path = \
647 self.ip.image.get_manifest_path(d_fmri)
648 o_fmri = p.origin_fmri
649 o_manifest_path = None
650 o_filter_file = None
651 if o_fmri:
652 o_manifest_path = \
653 self.ip.image.get_manifest_path(o_fmri)
654 plan_info.append((d_fmri, d_manifest_path, o_fmri,
655 o_manifest_path))
656 self.update_list = self.ip.pkg_plans[:]
657 del self.ip.pkg_plans
658
659 self.ip.progtrack.actions_set_goal("Index Phase", len(plan_info))
660
661 self.ip.image.update_index_dir()
662 ind = indexer.Indexer(self.ip.image.index_dir,
663 CLIENT_DEFAULT_MEM_USE_KB, progtrack=self.ip.progtrack)
664 ind.client_update_index((self.ip.filters, plan_info))
665
666 self.ip.progtrack.actions_done()
667
668 def actions_done(self):
669 if self.parent != None:
670 if not self.ips_update and not self.image_update:
671 gobject.idle_add(self.__update_package_list)
672 gobject.idle_add(self.w_installing_dialog.hide)
673
674 if self.ips_update:
675 gobject.idle_add(self.parent.shutdown_after_ips_update)
676 elif self.image_update:
677 gobject.idle_add(self.parent.shutdown_after_image_update)
678
679 def __update_package_list(self):
680 for pkg in self.update_list:
681 pkg_name = pkg.get_xfername()
682 self.__update_install_list(pkg_name)
683 del self.update_list
684 self.parent.update_package_list()
685
686 def __update_install_list(self, pkg_name):
687 for row in self.install_list:
688 if row[enumerations.NAME_COLUMN] == pkg_name:
689 row[enumerations.MARK_COLUMN] = True
690 return
691
692 def download_file_path(self, file_path):
693 '''Called by GUI's filelist.py through the progress, which is passed
694 to the filelist.'''
695 # XXX this function should be removed and also pkg.gui.filelist should
696 # not call it, since we don't want to show single file progress
697 gobject.idle_add(self.__add_file_to_downloadtext, file_path)
698
699 def __add_file_to_downloadtext(self, file_path):
700 '''Function which adds another line text in the "more details" download
701 dialog'''
702 buf = self.w_download_textview.get_buffer()
703 textiter = buf.get_end_iter()
704 buf.insert(textiter, self.parent._("Downloading: ") + file_path + "\n")
705
706 def __add_info_to_installtext(self, text):
707 '''Function which adds another line text in the "more details" install
708 dialog'''
709 buf = self.w_installing_textview.get_buffer()
710 textiter = buf.get_end_iter()
711 buf.insert(textiter, text)
712
713 def cat_output_start(self):
714 return
715
716 def cat_output_done(self):
717 return
718
719 def eval_output_start(self):
720 '''Called by progress tracker when the evaluation of the packages just
721 started.'''
722 return
723
724 def eval_output_progress(self):
725 '''Called by progress tracker each time some package was evaluated. The
726 call is being done by calling progress tracker evaluate_progress()
727 function'''
728 return
729
730 def eval_output_done(self):
731 '''Called by progress tracker after the evaluation of the packages is
732 finished. Gets information like how many packages will be
733 updated/installed and maximum amount of data which will be downloaded.
734 Later this information is being adjusted, while downloading'''
735 self.w_createplan_dialog.hide()
736 if self.gui_thread.is_cancelled():
737 return
738 updated_installed = \
739 [
740 ["Packages To Be Installed:"],
741 ["Packages To Be Updated:"]
742 ]
743 treestore = gtk.TreeStore(str)
744 install_iter = None
745 updated_iter = None
746 install_count = 0
747 updated_count = 0
748 total_download_count = 0
749 total_files_count = 0
750 npkgs = 0
751 for package_plan in self.ip.pkg_plans:
752 npkgs += 1
753 if package_plan.origin_fmri and package_plan.destination_fmri:
754 if not updated_iter:
755 updated_iter = treestore.append(None, \
756 updated_installed[1])
757 d_fmri = package_plan.destination_fmri
758 dt = self.get_datetime(d_fmri.version)
759 dt_str = (":%02d%02d") % (dt.month, dt.day)
760 pkg_version = d_fmri.version.get_short_version() + dt_str
761 pkg = d_fmri.get_name() + "@" + pkg_version
762 updated_count = updated_count + 1
763 treestore.append(updated_iter, [pkg])
764 elif package_plan.destination_fmri:
765 if not install_iter:
766 install_iter = treestore.append(None, \
767 updated_installed[0])
768 d_fmri = package_plan.destination_fmri
769 dt = self.get_datetime(d_fmri.version)
770 dt_str = (":%02d%02d") % (dt.month, dt.day)
771 pkg_version = d_fmri.version.get_short_version() + dt_str
772 pkg = d_fmri.get_name() + "@" + pkg_version
773 install_count = install_count + 1
774 treestore.append(install_iter, [pkg])
775 xferfiles, xfersize = package_plan.get_xferstats()
776 total_download_count = total_download_count + xfersize
777 total_files_count = total_files_count + xferfiles
778 self.ip.progtrack.download_set_goal(npkgs, total_files_count, \
779 total_download_count)
780 self.w_review_treeview.set_model(treestore)
781 self.w_review_treeview.expand_all()
782 updated_str = self.parent._("%d packages will be updated\n")
783 if updated_count == 1:
784 updated_str = self.parent._("%d package will be updated\n")
785 install_str = self.parent._("%d packages will be installed\n\n")
786 if install_count == 1:
787 install_str = self.parent._("%d package will be installed\n\n")
788 self.w_summary_label.set_text((updated_str + install_str + \
789 self.parent._("%d MB will be downloaded"))% \
790 (updated_count, install_count, (total_download_count/1024/1024)))
791 self.w_createplan_dialog.hide()
792 self.w_installupdate_dialog.show()
793 return True
794
795 def ver_output(self):
796 return
797
798 def ver_output_error(self, actname, errors):
799 return
800
801 def dl_output(self):
802 gobject.idle_add(self.__update_download_progress, self.dl_cur_nbytes, \
803 self.dl_goal_nbytes)
804 return
805
806 def dl_output_done(self):
807 return
808
809 def act_output(self):
810 gobject.idle_add(self.__update_install_progress, \
811 self.act_cur_nactions, self.act_goal_nactions)
812 return
813
814 def act_output_done(self):
815 return
816
817 def ind_output(self):
818 return
819
820 def ind_output_done(self):
821 return
822
823 @staticmethod
824 def get_datetime(version):
825 '''Support function for change in the IPS API: get_timestamp() was
826 replaced by get_datetime()'''
827 dt = None
828 try:
829 dt = version.get_datetime()
830 except AttributeError:
831 dt = version.get_timestamp()
832 return dt
833