~ubuntu-branches/ubuntu/karmic/firmware-tools/karmic

« back to all changes in this revision

Viewing changes to bin/inventory_firmware_gui

  • Committer: Bazaar Package Importer
  • Author(s): Matt Domsch
  • Date: 2007-12-15 23:00:29 UTC
  • Revision ID: james.westby@ubuntu.com-20071215230029-9aly4zd935kqtm60
Tags: upstream-1.5.10
ImportĀ upstreamĀ versionĀ 1.5.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0
 
3
"""
 
4
    this is the documentation...
 
5
"""
 
6
 
 
7
# import arranged alphabetically
 
8
import ConfigParser
 
9
import commands
 
10
import cStringIO
 
11
import getopt
 
12
from gettext import gettext as _
 
13
import os
 
14
import pygtk
 
15
import sys
 
16
import threading
 
17
import traceback
 
18
 
 
19
pygtk.require('2.0')
 
20
import gtk, gtk.glade, pango
 
21
import gobject
 
22
import gnome.ui
 
23
 
 
24
import firmwaretools.trace_decorator as trace_decorator
 
25
import firmwaretools.guihelpers as guihelpers
 
26
import firmwaretools.repository as repository
 
27
import firmwaretools.clifuncs
 
28
import firmwaretools.package
 
29
 
 
30
PROGRAM_NAME="Firmware Inventory and Update GUI"
 
31
version="1.5.10"
 
32
 
 
33
class CommandAlreadyExecuted(Exception): pass
 
34
class CommandAlreadyUndone(Exception): pass
 
35
class Command(object):
 
36
    def __init__(self, object, method, args, kargs):
 
37
        self.object = object
 
38
        self.method = method
 
39
        self.args = args
 
40
        self.kargs = kargs
 
41
        self.memento = None
 
42
        self.executed = False
 
43
        self.undone = False
 
44
 
 
45
    def execute(self):
 
46
        if self.executed:
 
47
            raise CommandAlreadyExecuted()
 
48
        self.executed=True
 
49
        self.undone = False
 
50
        self.memento = self.object.getMemento()
 
51
        self.method(*self.args, **self.kargs)
 
52
 
 
53
    def undo(self):
 
54
        if self.undone:
 
55
            raise CommandAlreadyUndone()
 
56
        self.undone = True
 
57
        self.executed = False
 
58
        self.object.setMemento(self.memento)
 
59
 
 
60
# use only for pin/unpin, it is more efficent as it doesnt save full memento
 
61
class UpdateSetPinCommand(Command):
 
62
    def execute(self):
 
63
        if self.executed:
 
64
            raise CommandAlreadyExecuted()
 
65
        self.executed=True
 
66
        self.undone = False
 
67
        self.memento = self.object.getMemento(deviceHint = self.args[0])
 
68
        self.method(*self.args, **self.kargs)
 
69
 
 
70
class InventoryFirmware:
 
71
    GLADE_FILE = '/glade/inventory_firmware_gui.glade'
 
72
    def __init__(self, ini):
 
73
        self.wTree = gtk.glade.XML(TOPDIR + self.GLADE_FILE)
 
74
        self.wTree.signal_autoconnect(self)
 
75
        self.main_window = self.wTree.get_widget("MainWindow")
 
76
        self.wTree.get_widget("about_dialog").destroy()
 
77
 
 
78
        # set up toggles
 
79
        self.toolbarAllowDowngrade = self.wTree.get_widget("toolbar_allow_downgrade")
 
80
        self.toolbarAllowReflash = self.wTree.get_widget("toolbar_allow_reflash")
 
81
        self.menuAllowDowngrade = self.wTree.get_widget("menu_allow_downgrade")
 
82
        self.menuAllowReflash = self.wTree.get_widget("menu_allow_reflash")
 
83
        self.recursiveCallback=0
 
84
 
 
85
        # internal accounting
 
86
        self.numDowngradeSelected = 0
 
87
        self.numReflashSelected = 0
 
88
 
 
89
        # set up command stack, used for undo/redo
 
90
        self.undoStack = []
 
91
        self.redoStack = []
 
92
 
 
93
        # setup tree views
 
94
        self._setupInventoryTreeView()
 
95
        self._setupBootstrapTreeView()
 
96
        self._setupUpdateStatusTreeView()
 
97
 
 
98
        # get handle to status bar
 
99
        self.statusBar = self.wTree.get_widget("main_window_status_bar")
 
100
        ctx = self.statusBar.get_context_id("main")
 
101
        self.statusBar.push(ctx, "Ready")
 
102
 
 
103
        # disable input in main window until we are finished initializing...
 
104
        self.main_window.set_sensitive(0)
 
105
 
 
106
        # show main window
 
107
        self.main_window.show()
 
108
 
 
109
        # set status == collecting inventory
 
110
        ctx = self.statusBar.get_context_id("inventory")
 
111
        self.statusBar.push(ctx, _("Performing system inventory..."))
 
112
        guihelpers.gtkYield() # make sure current GUI is fully displayed
 
113
 
 
114
        # special function to make sure GUI updates smoothly while we
 
115
        # generate the update set
 
116
        def myYield(*args, **kargs):
 
117
            # eats all its args...
 
118
            guihelpers.gtkYield()
 
119
 
 
120
        # collect inventory
 
121
        r = firmwaretools.repository.Repository( ini.get("main", "storage_topdir") )
 
122
        firmwaretools.package.RepositoryPackage.mainIni = ini
 
123
 
 
124
        # the following two lines are equivalent, but runLongProcess() does the
 
125
        # action in a background thread so the GUI will update while it works.
 
126
        #self.updateSet = repository.generateUpdateSet(r, firmwaretools.clifuncs.runInventory(ini), cb=(myYield, None))
 
127
        self.updateSet = guihelpers.runLongProcessGtk(repository.generateUpdateSet, args=(r, firmwaretools.clifuncs.runInventory(ini)), kargs={'cb':(myYield, None)})
 
128
 
 
129
        self._populateInventoryTree()
 
130
        self._populateBootstrapTree(ini)
 
131
        self.inventoryTreeView.expand_all()
 
132
        self._refresh()
 
133
 
 
134
        # set status == ready
 
135
        self.statusBar.pop(ctx)
 
136
        self.main_window.set_sensitive(1)
 
137
 
 
138
    def _setupBootstrapTreeView(self):
 
139
        # create model for bootstrap treeview
 
140
        self.bootstrapTreeView = self.wTree.get_widget("bootstrap_treeview")
 
141
        self.bootstrapTreeModel= gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
 
142
        self.bootstrapTreeView.set_model(self.bootstrapTreeModel)
 
143
        self.BOOTSTRAP_COLUMN_BOOTSTRAP_NAME = 0
 
144
        self.BOOTSTRAP_COLUMN_DEVICE_NAME = 1
 
145
        self.BOOTSTRAP_COLUMN_FW_VER = 2
 
146
 
 
147
        # add column headers to the inventory treeview
 
148
        self.bootstrapTreeView.set_headers_visible(True)
 
149
 
 
150
        # add column: Display name for devices, version select for updates
 
151
        renderer=gtk.CellRendererText()
 
152
        column=gtk.TreeViewColumn(_("Bootstrap Name"),renderer, text=self.BOOTSTRAP_COLUMN_BOOTSTRAP_NAME)
 
153
        column.set_resizable(True)
 
154
        self.bootstrapTreeView.append_column(column)
 
155
 
 
156
        # add column: Display name for devices, version select for updates
 
157
        renderer=gtk.CellRendererText()
 
158
        column=gtk.TreeViewColumn(_("Device Name"),renderer, text=self.BOOTSTRAP_COLUMN_DEVICE_NAME)
 
159
        column.set_resizable(True)
 
160
        self.bootstrapTreeView.append_column(column)
 
161
 
 
162
        # add column: Display name for devices, version select for updates
 
163
        renderer=gtk.CellRendererText()
 
164
        column=gtk.TreeViewColumn(_("Firmware Version"),renderer, text=self.BOOTSTRAP_COLUMN_FW_VER)
 
165
        column.set_resizable(True)
 
166
        self.bootstrapTreeView.append_column(column)
 
167
 
 
168
        # let us select multiple releases
 
169
        self.bootstrapTreeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
 
170
 
 
171
    def _setupUpdateStatusTreeView(self):
 
172
        # create model for update status treeview
 
173
        self.updateStatusTreeView = self.wTree.get_widget("status_treeview")
 
174
        self.updateStatusTreeModel= gtk.TreeStore( object, object, int )
 
175
        self.updateStatusTreeView.set_model(self.updateStatusTreeModel)
 
176
        self.STATUS_COLUMN_DEVICE = 0
 
177
        self.STATUS_COLUMN_PACKAGE = 1
 
178
        self.STATUS_COLUMN_SERIAL = 2
 
179
 
 
180
        # add column headers to the inventory treeview
 
181
        self.updateStatusTreeView.set_headers_visible(True)
 
182
 
 
183
        # status, component, status description, log?
 
184
 
 
185
        # add column: Display name for devices, version select for updates
 
186
        renderer=gtk.CellRendererText()
 
187
        column=gtk.TreeViewColumn(_("Status"),renderer)
 
188
        column.set_resizable(True)
 
189
        column.set_cell_data_func(renderer, self.cell_data_func_us_status)
 
190
        self.updateStatusTreeView.append_column(column)
 
191
 
 
192
        # add column: Display name for devices, version select for updates
 
193
        renderer=gtk.CellRendererText()
 
194
        column=gtk.TreeViewColumn(_("Component"),renderer)
 
195
        column.set_resizable(True)
 
196
        column.set_cell_data_func(renderer, self.cell_data_func_us_component)
 
197
        self.updateStatusTreeView.append_column(column)
 
198
 
 
199
        # add column: Display name for devices, version select for updates
 
200
        renderer=gtk.CellRendererText()
 
201
        column=gtk.TreeViewColumn(_("Current Version"),renderer)
 
202
        column.set_resizable(True)
 
203
        column.set_cell_data_func(renderer, self.cell_data_func_us_cur_version)
 
204
        self.updateStatusTreeView.append_column(column)
 
205
 
 
206
        # add column: Display name for devices, version select for updates
 
207
        renderer=gtk.CellRendererText()
 
208
        column=gtk.TreeViewColumn(_("Update Version"),renderer)
 
209
        column.set_resizable(True)
 
210
        column.set_cell_data_func(renderer, self.cell_data_func_us_update_version)
 
211
        self.updateStatusTreeView.append_column(column)
 
212
 
 
213
        # add column: Display name for devices, version select for updates
 
214
        renderer=gtk.CellRendererText()
 
215
        column=gtk.TreeViewColumn(_("Status Description"),renderer)
 
216
        column.set_resizable(True)
 
217
        column.set_cell_data_func(renderer, self.cell_data_func_us_status_description)
 
218
        self.updateStatusTreeView.append_column(column)
 
219
 
 
220
        # let us select multiple releases
 
221
        self.updateStatusTreeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
 
222
 
 
223
    def _setupInventoryTreeView(self):
 
224
        # create model for inventory treeview
 
225
        self.inventoryTreeView = self.wTree.get_widget("inventory_treeview")
 
226
        self.inventoryTreeModel= gtk.TreeStore(
 
227
                object,                # device or package
 
228
                gobject.TYPE_BOOLEAN,  # for device == enable update for device (checkbox),
 
229
                                       # for package == update to this package
 
230
                gobject.TYPE_INT,      # flags
 
231
                gobject.TYPE_INT,      # update serial
 
232
                )
 
233
        self.inventoryTreeView.set_model(self.inventoryTreeModel)
 
234
        self.INVENTORY_COLUMN_DEVICE = 0
 
235
        self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE = 1
 
236
        self.INVENTORY_COLUMN_FLAGS = 2
 
237
        self.INVENTORY_COLUMN_SERIAL = 3
 
238
        self.FLAG_REFLASH = 1
 
239
        self.FLAG_DOWNGRADE = 2
 
240
 
 
241
        # add column headers to the inventory treeview
 
242
        self.inventoryTreeView.set_headers_visible(True)
 
243
 
 
244
        # select, status, criticality, package name, component, type (bios/firmware/driver), current ver, repo ver
 
245
 
 
246
        # add column: Flash yes/no checkbox column
 
247
        renderer=gtk.CellRendererToggle()
 
248
        renderer.set_property("radio", False)
 
249
        renderer.set_property('activatable', True)
 
250
        renderer.connect('toggled', self.toggle_device_cb, self.inventoryTreeModel)
 
251
        column=gtk.TreeViewColumn(_("Flash"),renderer)
 
252
        column.add_attribute(renderer, "active", self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE)
 
253
        column.set_cell_data_func(renderer, self.cell_data_func_iv_toggle)
 
254
        self.inventoryTreeView.append_column(column)
 
255
 
 
256
        # add column: Display name for devices, version select for updates
 
257
        renderer=gtk.CellRendererToggle()
 
258
        renderer.set_property("radio", True)
 
259
        renderer.set_property('activatable', True)
 
260
        renderer.connect('toggled', self.toggle_update_cb, self.inventoryTreeModel)
 
261
        column=gtk.TreeViewColumn(_("Device Name"),renderer)
 
262
        column.add_attribute(renderer, "active", self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE)
 
263
        column.set_resizable(True)
 
264
        renderer=gtk.CellRendererText()
 
265
        column.pack_start(renderer)
 
266
        column.set_cell_data_func(renderer, self.cell_data_func_iv_display_name)
 
267
        self.inventoryTreeView.append_column(column)
 
268
        self.inventoryTreeView.set_expander_column(column)
 
269
 
 
270
        # add column: Firmware version
 
271
        renderer = gtk.CellRendererText()
 
272
        column=gtk.TreeViewColumn(_("Current Version"),renderer)
 
273
        column.set_cell_data_func(renderer, self.cell_data_func_iv_version)
 
274
        column.set_resizable(True)
 
275
        self.inventoryTreeView.append_column(column)
 
276
 
 
277
        # let us select multiple releases
 
278
        self.inventoryTreeView.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
 
279
 
 
280
 
 
281
    # this is a helper function to initially populate the tree model.
 
282
    # should only ever be called once.
 
283
    def _populateInventoryTree(self):
 
284
        self.inventoryTreeModel.clear()
 
285
        for device in self.updateSet.iterDevices():
 
286
            guihelpers.gtkYield()
 
287
            flags = 0
 
288
            toggle=False
 
289
            if self.updateSet.getUpdatePackageForDevice(device) is not None:
 
290
                toggle=True
 
291
            iter = self.inventoryTreeModel.append(None, [device, toggle, flags, 0])
 
292
            for availPkg in self.updateSet.iterAvailableUpdates(device):
 
293
                guihelpers.gtkYield()
 
294
                flags = 0
 
295
                if device.compareVersion(availPkg) == 0:
 
296
                    flags = flags | self.FLAG_REFLASH
 
297
                if device.compareVersion(availPkg) > 0:
 
298
                    flags = flags | self.FLAG_DOWNGRADE
 
299
                toggle=False
 
300
                if self.updateSet.getUpdatePackageForDevice(device) == availPkg:
 
301
                    toggle=True
 
302
                self.inventoryTreeModel.append(iter, [availPkg, toggle, flags, 0])
 
303
 
 
304
    # this is a helper function to initially populate the tree model.
 
305
    # should only ever be called once.
 
306
    def _populateBootstrapTree(self, ini):
 
307
        self.bootstrapTreeModel.clear()
 
308
        for dev in firmwaretools.clifuncs.runBootstrapInventory(ini):
 
309
            guihelpers.gtkYield()
 
310
            self.bootstrapTreeModel.append(None, [dev.name, dev.displayname, dev.version])
 
311
 
 
312
    def _populateUpdateStatusTree(self):
 
313
        self.updateStatusTreeModel.clear()
 
314
        for device, package in self.updateSet.generateInstallationOrder(returnDeviceToo=1):
 
315
            self.updateStatusTreeModel.append(None, [device, package, 0])
 
316
 
 
317
    # refresh the display when something happens behind the scenes. Should be rarely used.
 
318
    def _refresh(self):
 
319
        self._refreshUpdateEnableToggles()
 
320
        self._refreshAllowToggles()
 
321
        self._refreshEnableUndoRedo()
 
322
 
 
323
    # only refreshes the toggles and radio buttons to reflect current package set.
 
324
    def _refreshUpdateEnableToggles(self):
 
325
        for i in range(self.inventoryTreeModel.iter_n_children(None)):
 
326
            device_path = self.inventoryTreeModel.get_path(self.inventoryTreeModel.iter_nth_child(None, i))
 
327
            device = self.inventoryTreeModel[device_path][self.INVENTORY_COLUMN_DEVICE]
 
328
            if self.updateSet.getUpdatePackageForDevice(device) is not None:
 
329
                self.inventoryTreeModel[device_path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE]=True
 
330
            else:
 
331
                self.inventoryTreeModel[device_path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE]=False
 
332
            self._fixupChildren(self.inventoryTreeModel, self.inventoryTreeModel.get_iter(device_path))
 
333
 
 
334
    # refreshes allow reflash/downgrade toggles
 
335
    def _refreshAllowToggles(self):
 
336
        self.recursiveCallback=1
 
337
        self.menuAllowDowngrade.set_property("active", self.updateSet.allowDowngrade)
 
338
        self.menuAllowReflash.set_property("active", self.updateSet.allowReflash)
 
339
        self.toolbarAllowDowngrade.set_active(self.updateSet.allowDowngrade)
 
340
        self.toolbarAllowReflash.set_active(self.updateSet.allowReflash)
 
341
 
 
342
        if self.numDowngradeSelected:
 
343
            self.menuAllowDowngrade.set_sensitive(0)
 
344
            self.toolbarAllowDowngrade.set_sensitive(0)
 
345
        else:
 
346
            self.menuAllowDowngrade.set_sensitive(1)
 
347
            self.toolbarAllowDowngrade.set_sensitive(1)
 
348
 
 
349
        if self.numReflashSelected:
 
350
            self.menuAllowReflash.set_sensitive(0)
 
351
            self.toolbarAllowReflash.set_sensitive(0)
 
352
        else:
 
353
            self.menuAllowReflash.set_sensitive(1)
 
354
            self.toolbarAllowReflash.set_sensitive(1)
 
355
 
 
356
        self.recursiveCallback=0
 
357
 
 
358
    # enables/disables the undo/redo/reset buttons so only valid actions are enabled
 
359
    def _refreshEnableUndoRedo(self):
 
360
        if self.undoStack:
 
361
            self.wTree.get_widget("reset_button").set_sensitive(1)
 
362
            self.wTree.get_widget("reset_menu").set_sensitive(1)
 
363
            self.wTree.get_widget("undo_button").set_sensitive(1)
 
364
            self.wTree.get_widget("undo_menu").set_sensitive(1)
 
365
        else:
 
366
            self.wTree.get_widget("reset_button").set_sensitive(0)
 
367
            self.wTree.get_widget("reset_menu").set_sensitive(0)
 
368
            self.wTree.get_widget("undo_button").set_sensitive(0)
 
369
            self.wTree.get_widget("undo_menu").set_sensitive(0)
 
370
 
 
371
        if self.redoStack:
 
372
            self.wTree.get_widget("redo_button").set_sensitive(1)
 
373
            self.wTree.get_widget("redo_menu").set_sensitive(1)
 
374
        else:
 
375
            self.wTree.get_widget("redo_button").set_sensitive(0)
 
376
            self.wTree.get_widget("redo_menu").set_sensitive(0)
 
377
 
 
378
    def cell_data_func_iv_display_name(self, column, cell, model, iter):
 
379
        pyobj = model.get_value(iter,self.INVENTORY_COLUMN_DEVICE)
 
380
        renderers = column.get_cell_renderers()
 
381
        text = str(pyobj)
 
382
        if isinstance(pyobj, firmwaretools.package.Device):
 
383
            renderers[0].set_property("visible", False)
 
384
            renderers[1].set_property("text", text)
 
385
        else:
 
386
            flags = model.get_value(iter,self.INVENTORY_COLUMN_FLAGS)
 
387
            renderers[0].set_property("visible", True)
 
388
            parentIter = model.iter_parent(iter)
 
389
            device = model.get_value(parentIter,self.INVENTORY_COLUMN_DEVICE)
 
390
            text = str(pyobj.version)
 
391
 
 
392
            renderers[1].set_property("text", _("Available Version: %s") % text)
 
393
            renderers[0].set_property('activatable', True)
 
394
 
 
395
            if not model.get_value(parentIter, self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE):
 
396
                renderers[0].set_property('activatable', False)
 
397
 
 
398
            if self.updateSet.getSuggestedUpdatePackageForDevice(device) == pyobj:
 
399
                # TODO: picture?
 
400
                renderers[1].set_property("text", _("Available Version: %s  (suggested)") % text)
 
401
 
 
402
            if flags & self.FLAG_REFLASH:
 
403
                # TODO: picture?
 
404
                renderers[1].set_property("text", _("Available Version: %s  (reflash)") % text)
 
405
                if not self.updateSet.allowReflash:
 
406
                    renderers[0].set_property('activatable', False)
 
407
                    renderers[1].set_property("text", _("Available Version: %s  (reflash disabled per policy)") % text)
 
408
                if not pyobj.getCapability("can_reflash"):
 
409
                    renderers[0].set_property('activatable', False)
 
410
                    renderers[1].set_property("text", _("Available Version: %s  (reflash disabled due to package limitations)") % text)
 
411
 
 
412
            if flags & self.FLAG_DOWNGRADE:
 
413
                # TODO: picture?
 
414
                renderers[1].set_property("text", _("Available Version: %s  (downgrade)") % text)
 
415
                if not self.updateSet.allowDowngrade:
 
416
                    renderers[0].set_property('activatable', False)
 
417
                    renderers[1].set_property("text", _("Available Version: %s  (downgrade disabled per policy)") % text)
 
418
                if not pyobj.getCapability("can_downgrade"):
 
419
                    renderers[0].set_property('activatable', False)
 
420
                    renderers[1].set_property("text", _("Available Version: %s  (downgrade disabled due to package limitations)") % text)
 
421
 
 
422
 
 
423
    def cell_data_func_iv_version(self, column, cell, model, iter):
 
424
        pyobj = model.get_value(iter,self.INVENTORY_COLUMN_DEVICE)
 
425
        if isinstance(pyobj, firmwaretools.package.Device):
 
426
            cell.set_property("visible", True)
 
427
            cell.set_property("text", pyobj.version)
 
428
        else:
 
429
            cell.set_property("visible", False)
 
430
 
 
431
    def cell_data_func_iv_toggle(self, column, cell, model, iter):
 
432
        pyobj = model.get_value(iter,self.INVENTORY_COLUMN_DEVICE)
 
433
        if isinstance(pyobj, firmwaretools.package.Device):
 
434
            cell.set_property("visible", True)
 
435
        else:
 
436
            cell.set_property("visible", False)
 
437
 
 
438
    def toggle_device_cb(self, renderer, path, model, *args, **kargs):
 
439
        model[path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE] = not model[path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE]
 
440
        device = model[path][self.INVENTORY_COLUMN_DEVICE]
 
441
        if model[path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE]:
 
442
            # unpin it and calculated pkg takes over
 
443
            self._executeCommand(UpdateSetPinCommand(self.updateSet, self.updateSet.unPinDevice, (device,), {}))
 
444
        else:
 
445
            # pin it to None to disable update for this device
 
446
            self._executeCommand(UpdateSetPinCommand(self.updateSet, self.updateSet.pinUpdatePackage, (device, None), {}))
 
447
 
 
448
        self._fixupChildren(model, model.get_iter(path))
 
449
        self._refreshAllowToggles()
 
450
 
 
451
    def toggle_update_cb(self, renderer, path, model, *args, **kargs):
 
452
        # dont re-activate if it is already the active update
 
453
        if not model[path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE]:
 
454
            iter = model.get_iter(path)
 
455
            parentIter = model.iter_parent(iter)
 
456
            device = model.get_value(parentIter, self.INVENTORY_COLUMN_DEVICE)
 
457
            update = model[path][self.INVENTORY_COLUMN_DEVICE]
 
458
 
 
459
            self._executeCommand(UpdateSetPinCommand(self.updateSet, self.updateSet.pinUpdatePackage, (device, update), {}))
 
460
            self._fixupChildren(model, parentIter)
 
461
            self._refreshAllowToggles()
 
462
 
 
463
    # this method sets the enable toggle on packages appropriately
 
464
    # it also interlocks the allow reflash/downgrade toggles
 
465
    def _fixupChildren(self, model, iter):
 
466
        for i in range(model.iter_n_children(iter)):
 
467
            child_path = model.get_path(model.iter_nth_child(iter, i))
 
468
            curValue = model[child_path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE]
 
469
            if model[child_path][self.INVENTORY_COLUMN_DEVICE] == self.updateSet.getUpdatePackageForDevice(model.get_value(iter,self.INVENTORY_COLUMN_DEVICE)):
 
470
                if curValue == False and model[child_path][self.INVENTORY_COLUMN_FLAGS] & self.FLAG_DOWNGRADE:
 
471
                    self.numDowngradeSelected = self.numDowngradeSelected + 1
 
472
                if curValue == False and model[child_path][self.INVENTORY_COLUMN_FLAGS] & self.FLAG_REFLASH:
 
473
                    self.numReflashSelected = self.numReflashSelected + 1
 
474
                model[child_path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE] = True
 
475
            else:
 
476
                if curValue == True and model[child_path][self.INVENTORY_COLUMN_FLAGS] & self.FLAG_DOWNGRADE:
 
477
                    self.numDowngradeSelected = self.numDowngradeSelected - 1
 
478
                if curValue == True and model[child_path][self.INVENTORY_COLUMN_FLAGS] & self.FLAG_REFLASH:
 
479
                    self.numReflashSelected = self.numReflashSelected - 1
 
480
                model[child_path][self.INVENTORY_COLUMN_DEVICE_ENABLE_UPDATE] = False
 
481
 
 
482
            # force update serial so rows get redisplayed
 
483
            self.inventoryTreeModel[child_path][self.INVENTORY_COLUMN_SERIAL]= (
 
484
                self.inventoryTreeModel[child_path][self.INVENTORY_COLUMN_SERIAL] + 1)
 
485
 
 
486
    def on_allow_downgrade_toggled(self, widget, *args, **kargs):
 
487
        # guard against executeCommand being called while we are in a refresh
 
488
        if not self.recursiveCallback:
 
489
            if self.numDowngradeSelected > 0:
 
490
                active = 1
 
491
            else:
 
492
                active = widget.get_active()
 
493
            self._executeCommand(Command(self.updateSet, self.updateSet.setAllowDowngrade, (active,), {}))
 
494
            self._refreshAllowToggles()
 
495
            # force update serial so rows get redisplayed
 
496
            for i in range(self.inventoryTreeModel.iter_n_children(None)):
 
497
                device_path = self.inventoryTreeModel.get_path(self.inventoryTreeModel.iter_nth_child(None, i))
 
498
                self._fixupChildren(self.inventoryTreeModel, self.inventoryTreeModel.get_iter(device_path))
 
499
 
 
500
    def on_allow_reflash_toggled(self, widget, *args, **kargs):
 
501
        # guard against executeCommand being called while we are in a refresh
 
502
        if not self.recursiveCallback:
 
503
            if self.numReflashSelected > 0:
 
504
                active = 1
 
505
            else:
 
506
                active = widget.get_active()
 
507
            self._executeCommand(Command(self.updateSet, self.updateSet.setAllowReflash, (active,), {}))
 
508
            self._refreshAllowToggles()
 
509
            # force update serial so rows get redisplayed
 
510
            for i in range(self.inventoryTreeModel.iter_n_children(None)):
 
511
                device_path = self.inventoryTreeModel.get_path(self.inventoryTreeModel.iter_nth_child(None, i))
 
512
                self._fixupChildren(self.inventoryTreeModel, self.inventoryTreeModel.get_iter(device_path))
 
513
 
 
514
    def on_help_about(self, *args):
 
515
        wTree = gtk.glade.XML(TOPDIR + self.GLADE_FILE, "about_dialog")
 
516
        wTree.get_widget("about_dialog").set_property('name',PROGRAM_NAME)
 
517
        wTree.get_widget("about_dialog").set_property('version',version)
 
518
        wTree.get_widget("about_dialog").run() # modal until 'close'
 
519
        wTree.get_widget("about_dialog").destroy()
 
520
 
 
521
    def _executeCommand(self, command):
 
522
        command.execute()
 
523
        self.undoStack.append(command)
 
524
        self.redoStack = []
 
525
        self._refreshEnableUndoRedo()
 
526
        if len(self.undoStack) > 20:
 
527
            self.undoStack = self.undoStack[-20:]
 
528
 
 
529
    def on_undo_activate(self, *args, **kargs):
 
530
        if self.undoStack:
 
531
            command = self.undoStack.pop()
 
532
            command.undo()
 
533
            self.redoStack.append(command)
 
534
 
 
535
        self._refresh()
 
536
 
 
537
    def on_redo_activate(self, *args, **kargs):
 
538
        if self.redoStack:
 
539
            command = self.redoStack.pop()
 
540
            command.execute()
 
541
            self.undoStack.append(command)
 
542
 
 
543
        self._refresh()
 
544
 
 
545
    def on_reset_activate(self, *args, **kargs):
 
546
        self.updateSet.reset()
 
547
        self.undoStack = []
 
548
        self.redoStack = []
 
549
        self.updateSet.setAllowReflash(0)
 
550
        self.updateSet.setAllowDowngrade(0)
 
551
        self._refresh()
 
552
 
 
553
    def on_system_inventory_menu_activate(self, *args, **kargs):
 
554
        notebook = self.wTree.get_widget("notebook")
 
555
        widget = self.wTree.get_widget("inventory_vbox")
 
556
        page = notebook.page_num(widget)
 
557
        notebook.set_current_page(page)
 
558
 
 
559
    def on_bootstrap_inventory_menu_activate(self, *args, **kargs):
 
560
        notebook = self.wTree.get_widget("notebook")
 
561
        widget = self.wTree.get_widget("bootstrap_scrolledwindow")
 
562
        page = notebook.page_num(widget)
 
563
        notebook.set_current_page(page)
 
564
 
 
565
    def on_file_quit(self, *args):
 
566
        self.on_quit_app( allowCancel = 1 )
 
567
 
 
568
    def on_quit_app(self, *args, **kargs):
 
569
        # check kargs['allowCancel']
 
570
        gtk.main_quit()
 
571
 
 
572
    def on_update_now_activate(self, *args, **kargs):
 
573
        detailsStr = cStringIO.StringIO()
 
574
        detailsStr.write( _("Going to update the following devices:\n\n") )
 
575
        for device in self.updateSet.iterDevices():
 
576
            pkg = self.updateSet.getUpdatePackageForDevice(device)
 
577
            if pkg is not None:
 
578
                detailsStr.write("\t%s\n" % str(device))
 
579
                detailsStr.write(_("\t\tFrom Version: %s\n") % device.version)
 
580
                detailsStr.write(_("\t\tTo Version  : %s\n") % pkg.version)
 
581
                detailsStr.write("\n")
 
582
 
 
583
        dialog = gtk.MessageDialog(parent=None,
 
584
                   flags=0,
 
585
                   type=gtk.MESSAGE_WARNING,
 
586
                   buttons=gtk.BUTTONS_NONE)
 
587
        dialog.set_title(_("Update Firmware"))
 
588
        dialog.set_markup(_( "<big>Your system will now be updated.</big>\n\nYou will not be able to come back to this page after you continue.\nPress the 'Show Details' button to see which devices are set to be updated."))
 
589
 
 
590
        showButton = dialog.add_button(_("Show Details..."), 1)
 
591
        dialog.add_button(_("Continue to update page..."), 2)
 
592
        dialog.add_button(_("Cancel Update"), 3)
 
593
 
 
594
        # Details
 
595
        textview = gtk.TextView()
 
596
        textview.set_editable(False)
 
597
        textview.modify_font(pango.FontDescription("Monospace"))
 
598
        sw = gtk.ScrolledWindow()
 
599
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
600
        sw.add(textview)
 
601
        frame = gtk.Frame()
 
602
        frame.set_shadow_type(gtk.SHADOW_IN)
 
603
        frame.add(sw)
 
604
        frame.set_border_width(6)
 
605
        dialog.vbox.add(frame)
 
606
        textbuffer = textview.get_buffer()
 
607
        textbuffer.set_text(detailsStr.getvalue())
 
608
        textview.set_size_request(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/3)
 
609
 
 
610
        dialog.details = frame
 
611
        dialog.set_position(gtk.WIN_POS_CENTER)
 
612
        dialog.set_gravity(gtk.gdk.GRAVITY_CENTER)
 
613
 
 
614
        show = False
 
615
        while 1:
 
616
            resp = dialog.run()
 
617
            if resp == 1:
 
618
                show = not show
 
619
                if show:
 
620
                    dialog.details.show_all()
 
621
                    showButton.set_label(_("Hide Details"))
 
622
                else:
 
623
                    dialog.details.hide_all()
 
624
                    showButton.set_label(_("Show Details..."))
 
625
            elif resp == 2:
 
626
                dialog.destroy()
 
627
                self._gotoUpdatePage()
 
628
                break
 
629
            else:
 
630
                dialog.destroy()
 
631
                break
 
632
 
 
633
    def _gotoUpdatePage(self):
 
634
        self._populateUpdateStatusTree()
 
635
        notebook = self.wTree.get_widget("notebook")
 
636
        widget = self.wTree.get_widget("update_status_scrolledwindow")
 
637
        page = notebook.page_num(widget)
 
638
        notebook.set_current_page(page)
 
639
        # disable view menu
 
640
        view_menu = self.wTree.get_widget("view_menu")
 
641
        view_menu.set_sensitive(0)
 
642
        self.menuAllowDowngrade.set_sensitive(0)
 
643
        self.menuAllowReflash.set_sensitive(0)
 
644
        self.wTree.get_widget("update_now_menu").set_sensitive(0)
 
645
        self.redoStack = []
 
646
        self.undoStack = []
 
647
        self._refreshEnableUndoRedo()
 
648
        ctx = self.statusBar.get_context_id("update page")
 
649
        self.statusBar.push(ctx, "Click 'Update Now' to begin update...")
 
650
 
 
651
    def cell_data_func_us_status(self, column, cell, model, iter):
 
652
        pkg = model.get_value(iter,self.STATUS_COLUMN_PACKAGE)
 
653
        if pkg.getCapability('accurate_update_percentage'):
 
654
            cell.set_property("text", "%s%%" % (pkg.getProgress()*100))
 
655
        else:
 
656
            if pkg.status == "installing":
 
657
                cell.set_property("text", firmwaretools.pycompat.spinner())
 
658
            else:
 
659
                cell.set_property("text", "")
 
660
 
 
661
    def cell_data_func_us_status_description(self, column, cell, model, iter):
 
662
        pkg = model.get_value(iter,self.STATUS_COLUMN_PACKAGE)
 
663
        cell.set_property("text", pkg.getStatusStr())
 
664
 
 
665
    def cell_data_func_us_component(self, column, cell, model, iter):
 
666
        device = model.get_value(iter,self.STATUS_COLUMN_DEVICE)
 
667
        cell.set_property("text", str(device))
 
668
 
 
669
    def cell_data_func_us_cur_version(self, column, cell, model, iter):
 
670
        device = model.get_value(iter,self.STATUS_COLUMN_DEVICE)
 
671
        cell.set_property("text", device.version)
 
672
 
 
673
    def cell_data_func_us_update_version(self, column, cell, model, iter):
 
674
        pkg = model.get_value(iter,self.STATUS_COLUMN_PACKAGE)
 
675
        cell.set_property("text", pkg.version)
 
676
 
 
677
    def on_really_update_now_button_clicked(self, *args, **kargs):
 
678
        # disable update button...
 
679
        ctx = self.statusBar.get_context_id("update page")
 
680
        self.statusBar.pop(ctx)
 
681
        self.statusBar.push(ctx, "Performing updates now...")
 
682
        self.wTree.get_widget("really_update_now_button").set_sensitive(0)
 
683
 
 
684
        success = 1
 
685
        for pkg in self.updateSet.generateInstallationOrder():
 
686
            stop = self.wTree.get_widget("stop_on_errors").get_active()
 
687
            try:
 
688
                ret = guihelpers.runLongProcessGtk(pkg.install, waitLoopFunction=self._refreshUpdateStatus)
 
689
 
 
690
            except (firmwaretools.package.NoInstaller,), e:
 
691
                print "package %s - %s does not have an installer available." % (pkg.name, pkg.version)
 
692
                print "skipping this package for now."
 
693
            except (Exception,), e:
 
694
                success = 0
 
695
                print "Installation failed for package: %s - %s" % (pkg.name, pkg.version)
 
696
                print "The error message from the low-level command was:"
 
697
                print e
 
698
                if stop:
 
699
                    print
 
700
                    print "stop on errors selected. error detected, so I'm stopping."
 
701
                    break
 
702
 
 
703
        self._refreshUpdateStatus()
 
704
        self.statusBar.pop(ctx)
 
705
        if success:
 
706
            self.statusBar.push(ctx, "All updates successfully completed.")
 
707
        else:
 
708
            self.statusBar.push(ctx, "Some updates failed.")
 
709
 
 
710
    def _refreshUpdateStatus(self):
 
711
        # update serial # to force GUI to refresh
 
712
        model = self.updateStatusTreeModel
 
713
        for i in range(model.iter_n_children(None)):
 
714
            path = model.get_path(model.iter_nth_child(None, i))
 
715
            model[path][self.STATUS_COLUMN_SERIAL] = model[path][self.STATUS_COLUMN_SERIAL] + 1
 
716
 
 
717
def main():
 
718
    pretty = 0
 
719
 
 
720
    global firmwaretools
 
721
    global TOPDIR
 
722
    TOPDIR = "/usr/share/firmware-tools/"
 
723
    ini = ConfigParser.ConfigParser()
 
724
    verbose = 0
 
725
    warnings = 0
 
726
    altConfig = 0
 
727
    overrides = []
 
728
    try:
 
729
        opts, args = getopt.getopt(sys.argv[1:], "c:fho:t:vw", ["help", "verbose", "warnings", "topdir=", "config=", "overrides=", "fake-mode"])
 
730
        for option, argument in opts:
 
731
            if option in ("-h", "--help"):
 
732
                print __doc__
 
733
                sys.exit(1)
 
734
            if option in ("-c", "--config"):
 
735
                firmwaretools.clifuncs.getConfig(ini, [argument,])
 
736
                altConfig = 1
 
737
            if option in ("-o", "--override"):
 
738
                overrides.append(argument)
 
739
            if option in ("-v", "--verbose"):
 
740
                verbose = verbose + 1
 
741
                trace_decorator.debug["__main__"] = verbose
 
742
            if option in ("-w", "--warnings"):
 
743
                warnings = warnings + 1
 
744
            if option in ("-t", "--topdir"):
 
745
                TOPDIR = argument
 
746
            if option in ("-f", "--fake-mode"):
 
747
                os.environ['DEBUG_REPOSITORY'] = "1"
 
748
                os.environ['DEBUG_INVENTORY'] = "1"
 
749
                os.environ['DEBUG_BOOTSTRAP'] = "1"
 
750
                import firmwaretools.mockrepository
 
751
                import firmwaretools.mockpackage
 
752
 
 
753
        # load standard configuration
 
754
        if not altConfig:
 
755
            firmwaretools.clifuncs.getConfig(ini, firmwaretools.clifuncs.configLocations)
 
756
 
 
757
        for over in overrides:
 
758
            section, key, value = over.split(",", 2)
 
759
            if not ini.has_section(section):
 
760
                ini.add_section(section)
 
761
            ini.set(section, key, value)
 
762
 
 
763
        #gnome.init(PROGRAM_NAME, version)
 
764
        test = InventoryFirmware(ini)
 
765
        gtk.main()
 
766
 
 
767
    except (getopt.GetoptError):
 
768
        print __doc__
 
769
        sys.exit(1)
 
770
 
 
771
    except:
 
772
        traceback.print_exc()
 
773
        sys.exit(2)
 
774
 
 
775
    sys.exit(0)
 
776
 
 
777
def _info(type, value, tb):
 
778
    # exception dialog code from: Gustavo J A M Carneiro <gjc at inescporto.pt>
 
779
    # http://www.daa.com.au/pipermail/pygtk/attachments/20030828/2d304204/gtkexcepthook.py
 
780
    # license: "The license is whatever you want."
 
781
    # http://www.daa.com.au/pipermail/pygtk/2003-August/005777.html
 
782
    # Bugfixes by Michael Brown <michael_e_brown at dell.com>
 
783
    dialog = gtk.MessageDialog(parent=None,
 
784
                   flags=0,
 
785
                   type=gtk.MESSAGE_WARNING,
 
786
                   buttons=gtk.BUTTONS_NONE,
 
787
                   message_format=_(
 
788
    "<big><b>A programming error has been detected during the execution of this program.</b></big>"
 
789
    "\n\nIt probably isn't fatal, but should be reported to the developers nonetheless."))
 
790
    dialog.set_title(_("Bug Detected"))
 
791
    dialog.set_property("has-separator", False)
 
792
    dialog.vbox.get_children()[0].get_children()[1].set_property("use-markup", True)
 
793
 
 
794
    dialog.add_button(_("Show Details"), 1)
 
795
    dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
 
796
 
 
797
    # Details
 
798
    textview = gtk.TextView()
 
799
    textview.set_editable(False)
 
800
    textview.modify_font(pango.FontDescription("Monospace"))
 
801
    sw = gtk.ScrolledWindow()
 
802
    sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
803
    sw.add(textview)
 
804
    frame = gtk.Frame()
 
805
    frame.set_shadow_type(gtk.SHADOW_IN)
 
806
    frame.add(sw)
 
807
    frame.set_border_width(6)
 
808
    dialog.vbox.add(frame)
 
809
    textbuffer = textview.get_buffer()
 
810
    trace = cStringIO.StringIO()
 
811
    traceback.print_exception(type, value, tb, None, trace)
 
812
    textbuffer.set_text(trace.getvalue())
 
813
    textview.set_size_request(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/3)
 
814
 
 
815
    dialog.details = frame
 
816
    dialog.set_position(gtk.WIN_POS_CENTER)
 
817
    dialog.set_gravity(gtk.gdk.GRAVITY_CENTER)
 
818
 
 
819
    while 1:
 
820
        resp = dialog.run()
 
821
        if resp == 1:
 
822
            dialog.details.show_all()
 
823
            dialog.action_area.get_children()[1].set_sensitive(0)
 
824
            continue
 
825
        else:
 
826
            dialog.destroy()
 
827
            break
 
828
 
 
829
if __name__ == "__main__":
 
830
    sys.excepthook = _info
 
831
    main()