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 
  27 import gettext
  28 import sys
  29 import time
  30 from threading import Thread
  31 try:
  32         import gobject
  33         import gtk
  34         import gtk.glade
  35         import pygtk
  36         pygtk.require("2.0")
  37 except ImportError:
  38         sys.exit(1)
  39 import pkg.client.bootenv as bootenv
  40 import pkg.client.history as history
  41 import pkg.client.imageplan as imageplan
  42 import pkg.search_errors as search_errors
  43 import pkg.client.progress as progress
  44 import pkg.gui.enumerations as enumerations
  45 import pkg.gui.thread as guithread
  46 
  47 class Remove(progress.ProgressTracker):
  48         def __init__(self, remove_list, parent):
  49                 # XXX Workaround as BE is using msg(_("message")) 
  50                 # which bypasses the self._ mechanism the GUI is using
  51                 gettext.install("pkg","/usr/lib/locale")
  52                 progress.ProgressTracker.__init__(self)
  53                 self.gui_thread = guithread.ThreadRun()
  54                 self.remove_list = remove_list
  55                 self.parent = parent
  56                 #This is hack since we should show proper dialog.
  57                 self.error = None
  58                 self.ip = None
  59                 self.progress_stop_timer_thread = False
  60                 self.progress_stop_timer_running = False
  61                 w_tree_createplan = gtk.glade.XML(parent.gladefile, "createplandialog2")
  62                 w_tree_removedialog = gtk.glade.XML(parent.gladefile, "removedialog")
  63                 w_tree_removingdialog = gtk.glade.XML(parent.gladefile, "removingdialog") 
  64                 self.w_createplan_dialog = \
  65                     w_tree_createplan.get_widget("createplandialog2")
  66                 self.w_createplan_textview = \
  67                     w_tree_createplan.get_widget("createplantextview2")
  68                 self.w_createplan_progressbar = \
  69                     w_tree_createplan.get_widget("createplanprogress2")
  70                 self.w_createplan_expander = \
  71                     w_tree_createplan.get_widget("expander7")      
  72                 self.w_createplan_label = \
  73                     w_tree_createplan.get_widget("packagedependencies5")  
  74                 self.w_createplancancel_button = \
  75                     w_tree_createplan.get_widget("cancelcreateplan2")                      
  76                 self.w_remove_dialog = w_tree_removedialog.get_widget("removedialog")
  77                 self.w_summary_label = w_tree_removedialog.get_widget("removelabel")
  78                 self.w_review_treeview = w_tree_removedialog.get_widget("treeview3")
  79                 self.w_next_button = w_tree_removedialog.get_widget("next_remove")
  80                 self.w_removing_dialog = \
  81                     w_tree_removingdialog.get_widget("removingdialog")
  82                 self.w_removing_progressbar = \
  83                     w_tree_removingdialog.get_widget("removingprogress")
  84                 self.w_removingdialog_label = \
  85                     w_tree_removingdialog.get_widget("packagedependencies4")
  86                 self.w_removingdialog_expander = \
  87                     w_tree_removingdialog.get_widget("expander6") 
  88                 self.w_createplan_progressbar.set_pulse_step(0.1)
  89                 remove_column = gtk.TreeViewColumn('Removed')
  90                 self.w_review_treeview.append_column(remove_column)
  91                 cell = gtk.CellRendererText()
  92                 remove_column.pack_start(cell, True)
  93                 remove_column.add_attribute(cell, 'text', 0)
  94                 self.w_review_treeview.expand_all()
  95                 try:
  96                         dic_createplan = \
  97                             {
  98                                 "on_cancelcreateplan2_clicked": \
  99                                     self.__on_cancelcreateplan_clicked,
 100                             }
 101                         dic_removedialog = \
 102                             {
 103                                 "on_cancel_remove_clicked": \
 104                                     self.__on_cancel_button_clicked,
 105                                 "on_next_remove_clicked":self.__on_next_button_clicked,
 106                             }
 107                         w_tree_createplan.signal_autoconnect(dic_createplan)
 108                         w_tree_removedialog.signal_autoconnect(dic_removedialog)
 109                 except AttributeError, error:
 110                         print self.parent._('GUI will not respond to any event! %s. \
 111                             Check remove.py signals') \
 112                             % error
 113                 list_of_packages = self.__prepare_list_of_packages()
 114                 # XXX Hidden until progress will give information about fmri
 115                 self.w_createplan_expander.hide()
 116                 self.w_removingdialog_expander.hide()
 117                 pulse_t = Thread(target = self.__progressdialog_progress_pulse)
 118                 thread = Thread(target = self.__plan_the_removeimage, \
 119                     args = (list_of_packages, ))
 120                 pulse_t.start()
 121                 thread.start()
 122                 self.w_createplan_label.set_text(\
 123                     self.parent._("Checking package dependencies..."))
 124                 self.w_createplancancel_button.set_sensitive(True)
 125                 self.w_createplan_dialog.run()
 126                 return
 127 
 128         def __on_cancelcreateplan_clicked(self, widget):         
 129                 self.ip.image.history.operation_result = \
 130                     history.RESULT_CANCELED
 131                 self.w_createplan_label.set_text(\
 132                     self.parent._("Canceling..."))
 133                 self.w_createplancancel_button.set_sensitive(False)                    
 134                 self.gui_thread.cancel()
 135 
 136         def __on_next_button_clicked(self, widget):
 137                 self.w_remove_dialog.hide()
 138                 self.w_removing_dialog.show()
 139                 remove_thread = Thread(target = self.__remove_stage, args = ())
 140                 remove_thread.start()
 141 
 142         def __on_cancel_button_clicked(self, widget):
 143                 self.ip.image.history.operation_result = \
 144                     history.RESULT_CANCELED
 145                 self.gui_thread.cancel()
 146                 self.w_remove_dialog.hide()
 147 
 148         # XXX Not used until progress will give information about fmri
 149         def __update_createplan_progress(self, action):
 150                 buf = self.w_createplan_textview.get_buffer()
 151                 textiter = buf.get_end_iter()
 152                 buf.insert(textiter, action)
 153                 self.w_createplan_progressbar.pulse()
 154 
 155         def __update_remove_progress(self, current, total):
 156                 prog = float(current)/total
 157                 self.w_removing_progressbar.set_fraction(prog)
 158 
 159         def __prepare_list_of_packages(self):
 160                 ''' This method return the dictionary of images for removal'''
 161                 fmri_to_remove = {}
 162                 for row in self.remove_list:
 163                         if row[enumerations.MARK_COLUMN]:
 164                                 image = row[enumerations.IMAGE_OBJECT_COLUMN]
 165                                 package = row[enumerations.INSTALLED_OBJECT_COLUMN]
 166                                 im = fmri_to_remove.get(image)
 167                                 if im:
 168                                         if package:
 169                                                 im.append(package)
 170                                 else:
 171                                         if package:
 172                                                 fmri_to_remove[image] = [package, ]
 173                 return fmri_to_remove
 174 
 175         def __plan_the_removeimage(self, list_of_packages):
 176                 '''Function which plans the image'''
 177                 self.gui_thread.run()
 178                 filters = []
 179                 for image in list_of_packages:
 180                         self.ip = imageplan.ImagePlan(image, self, filters = filters)
 181                         self.ip.image.history.operation_name = "uninstall"
 182                         fmris = list_of_packages.get(image)
 183                         for fmri in fmris:
 184                                 if self.gui_thread.is_cancelled():
 185                                         self.progress_stop_timer_thread = True
 186                                         gobject.idle_add(self.w_createplan_dialog.hide)
 187                                         return
 188                                 self.ip.propose_fmri_removal(fmri)
 189                         try:
 190                                 self.ip.image.history.operation_start_state = \
 191                                     self.ip.get_plan()
 192                                 self.ip.evaluate()
 193                                 self.ip.image.history.operation_end_state = \
 194                                     self.ip.get_plan(full=False)
 195                                 if self.gui_thread.is_cancelled():
 196                                         self.progress_stop_timer_thread = True
 197                                         gobject.idle_add(self.w_createplan_dialog.hide)
 198                                         return
 199                                 image.imageplan = self.ip
 200                         except imageplan.NonLeafPackageException, e:
 201                                         self.error = e[1]
 202                                         self.ip.progtrack.evaluate_done()
 203                                         return
 204                 return
 205 
 206         def __remove_stage(self):
 207                 self.ip.preexecute()
 208                 try:
 209                         be = bootenv.BootEnv(self.ip.image.get_root())
 210                 except RuntimeError:
 211                         be = bootenv.BootEnvNull(self.ip.image.get_root())
 212                 try:
 213                         ret_code = 0
 214                         self.ip.execute()
 215                 except RuntimeError:
 216                         be.restore_install_uninstall()
 217                         self.ip.image.history.operation_result = \
 218                             history.RESULT_FAILED_UNKNOWN
 219                 except search_errors.InconsistentIndexException, e:
 220                         ret_code = 2
 221                         self.ip.image.history.operation_result = \
 222                             history.RESULT_FAILED_SEARCH
 223                 except search_errors.PartialIndexingException, e:
 224                         ret_code = 2
 225                         self.ip.image.history.operation_result = \
 226                             history.RESULT_FAILED_SEARCH
 227                 except search_errors.ProblematicPermissionsIndexException, e:
 228                         ret_code = 2
 229                         self.ip.image.history.operation_result = \
 230                             history.RESULT_FAILED_STORAGE
 231                 except KeyError, e:
 232                         # XXX KeyError was seen while problem with
 233                         # creating index
 234                         ret_code = 2
 235                         self.ip.image.history.operation_result = \
 236                             history.RESULT_FAILED_UNKNOWN
 237                 except Exception:
 238                         be.restore_install_uninstall()
 239                         gobject.idle_add(self.w_removing_dialog.hide)
 240                         self.ip.image.history.operation_result = \
 241                             history.RESULT_FAILED_UNKNOWN
 242                         raise
 243 
 244                 if ret_code == 2:
 245                         return_code = 0
 246                         return_code = self.__rebuild_index()
 247                         if return_code == 1:
 248                                 gobject.idle_add(self.w_removing_dialog.hide)
 249                                 return
 250 
 251                 if self.ip.state == imageplan.EXECUTED_OK:
 252                         be.activate_install_uninstall()
 253                         self.ip.image.history.operation_result = \
 254                             history.RESULT_SUCCEEDED
 255                 else:
 256                         be.restore_install_uninstall()
 257 
 258                 if self.parent != None:
 259                         gobject.idle_add(self.parent.update_package_list)
 260 
 261                 gobject.idle_add(self.w_removing_dialog.hide)
 262 
 263         def __rebuild_index(self):
 264                 '''Code duplication from pkg(1):
 265                        Forcibly rebuild the search indexes. Will remove existing indexes
 266                        and build new ones from scratch.'''
 267                 quiet = False
 268                 
 269                 try:
 270                         self.ip.image.rebuild_search_index(self.ip.progtrack)
 271                 except search_errors.InconsistentIndexException, iie:
 272                         return 1
 273                 except search_errors.ProblematicPermissionsIndexException, ppie:
 274                         return 1
 275                         
 276         def __progressdialog_progress_pulse(self):
 277                 while not self.progress_stop_timer_thread:
 278                         gobject.idle_add(self.w_createplan_progressbar.pulse)
 279                         time.sleep(0.1)
 280 
 281         def __removedialog_progress_pulse(self):
 282                 while not self.progress_stop_timer_thread:
 283                         self.progress_stop_timer_running = True
 284                         gobject.idle_add(self.w_removing_progressbar.pulse)
 285                         time.sleep(0.1)
 286                 self.progress_stop_timer_running = False
 287                 
 288         def cat_output_start(self): 
 289                 return
 290 
 291         def cat_output_done(self): 
 292                 return
 293 
 294         def eval_output_start(self):
 295                 return
 296 
 297         def eval_output_progress(self): 
 298                 return
 299 
 300         def eval_output_done(self):
 301             gobject.idle_add(self.__eval_output_done)
 302             
 303         def __eval_output_done(self):
 304                 if self.gui_thread.is_cancelled():
 305                         self.progress_stop_timer_thread = True
 306                         self.w_createplan_dialog.hide()
 307                         return
 308                 packaged_removed = \
 309                     [
 310                         ["Packages To Be Removed:"],
 311                     ]
 312                 if self.ip.state != imageplan.EVALUATED_OK:
 313                         packaged_removed = \
 314                             [
 315                                 ["Cannot remove, due to the following dependencies:"],
 316                             ]
 317                 treestore = gtk.TreeStore(str)
 318                 remove_iter = None 
 319                 remove_count = 0
 320                 if self.ip.state == imageplan.EVALUATED_OK:
 321                         self.w_next_button.set_sensitive(True)
 322                         for package_plan in self.ip.pkg_plans:
 323                                 if package_plan.origin_fmri and not \
 324                                     package_plan.destination_fmri:
 325                                         if not remove_iter:
 326                                                 remove_iter = \
 327                                                     treestore.append(None, \
 328                                                     packaged_removed[0])
 329                                         pkg_fmri = package_plan.origin_fmri
 330                                         pkg_version = \
 331                                             pkg_fmri.version.get_short_version()
 332                                         pkg = package_plan.origin_fmri.get_name() + \
 333                                             "@" + pkg_version
 334                                         remove_count = remove_count + 1
 335                                         treestore.append(remove_iter, [pkg])
 336                 else:
 337                         self.w_next_button.set_sensitive(False)
 338                         if self.error:
 339                                 for package in self.error:
 340                                         if not remove_iter:
 341                                                 remove_iter = \
 342                                                     treestore.append(None, \
 343                                                     packaged_removed[0])
 344                                         treestore.append(remove_iter, [package])
 345 
 346                 self.w_review_treeview.set_model(treestore)
 347                 self.w_review_treeview.expand_all()
 348                 remove_str = self.parent._("%d packages will be removed\n\n")
 349                 if remove_count == 1:
 350                         remove_str = self.parent._("%d package will be removed\n\n")
 351                 text = remove_str % remove_count
 352                 self.w_summary_label.set_text(text)
 353                 self.progress_stop_timer_thread = True
 354                 self.w_createplan_dialog.hide()
 355                 self.w_remove_dialog.show()
 356 
 357         def ver_output(self): 
 358                 return
 359 
 360         def ver_output_error(self, actname, errors): 
 361                 return
 362 
 363         def dl_output(self): 
 364                 return
 365 
 366         def dl_output_done(self): 
 367                 return
 368 
 369         def act_output(self):
 370                 text = self.parent._("Removing Packages...")
 371                 gobject.idle_add(self.w_removingdialog_label.set_text, text)
 372                 gobject.idle_add(self.__update_remove_progress, \
 373                     self.ip.progtrack.act_cur_nactions, \
 374                     self.ip.progtrack.act_goal_nactions)
 375                 return
 376 
 377         def act_output_done(self):
 378                 return
 379 
 380         def ind_output(self):
 381                 self.progress_stop_timer_thread = False
 382                 gobject.idle_add(self.__indexing_progress)
 383                 return
 384 
 385         def __indexing_progress(self):
 386                 if not self.progress_stop_timer_running:
 387                         self.w_removingdialog_label.set_text(\
 388                             self.parent._("Creating packages index..."))
 389                         Thread(target = self.__removedialog_progress_pulse).start()
 390 
 391         def ind_output_done(self):
 392                 self.progress_stop_timer_thread = True
 393                 return