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