FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Gentoo > Gentoo Embedded

 
 
LinkBack Thread Tools
 
Old 12-02-2009, 09:23 AM
Hans de Goede
 
Default Add an early user interface for filtering storage devices.

Ah,

The big one, way too big to review, esp by me as I know little about
pygtk stuff. I say lets just commit it and deal with issues as they
arrive. But maybe you can find someone else to review it ? Either way
has my blessing

Regards,

Hans


On 12/01/2009 09:15 PM, Chris Lumens wrote:

This UI allows the user to select which devices they would like to include
in the rest of the installation process, sorted out by their types. All
devices not checked will never again be referenced by anaconda, though we
may see their udev information in the logs from time to time. This UI
supports two different ways of looking at things: the simple UI for
regular Fedora users with basic devices, and the complex UI with many tabs
and filtering options.
---
dispatch.py | 3 +-
gui.py | 1 +
installclass.py | 1 +
instdata.py | 2 +
iw/DeviceSelector.py | 191 +++++++
iw/filter_gui.py | 528 +++++++++++++++++
kickstart.py | 2 +
pixmaps/filter-menu.png | Bin 0 -> 456 bytes
text.py | 1 +
ui/filter.glade | 1431 +++++++++++++++++++++++++++++++++++++++++++++++
upgrade.py | 1 +
11 files changed, 2160 insertions(+), 1 deletions(-)
create mode 100644 iw/DeviceSelector.py
create mode 100644 iw/filter_gui.py
create mode 100644 pixmaps/filter-menu.png
create mode 100644 ui/filter.glade

diff --git a/dispatch.py b/dispatch.py
index dbf4269..5beb5bc 100644
--- a/dispatch.py
+++ b/dispatch.py
@@ -69,6 +69,7 @@ installSteps = [
("language", ),
("keyboard", ),
("betanag", betaNagScreen, ),
+ ("filter", ),
("storageinit", storageInitialize, ),
("findrootparts", findRootParts, ),
("findinstall", ),
@@ -76,7 +77,7 @@ installSteps = [
("timezone", ),
("accounts", ),
("setuptime", setupTimezone, ),
- ("parttype", ),
+ ("parttype", ),
("autopartitionexecute", doAutoPartition, ),
("partition", ),
("upgrademount", upgradeMountFilesystems, ),
diff --git a/gui.py b/gui.py
index c54e935..4d9542c 100755
--- a/gui.py
+++ b/gui.py
@@ -63,6 +63,7 @@ stepToClass = {
"language" : ("language_gui", "LanguageWindow"),
"keyboard" : ("kbd_gui", "KeyboardWindow"),
"welcome" : ("welcome_gui", "WelcomeWindow"),
+ "filter" : ("filter_gui", "FilterWindow"),
"zfcpconfig" : ("zfcp_gui", "ZFCPWindow"),
"partition" : ("partition_gui", "PartitionWindow"),
"parttype" : ("autopart_type", "PartitionTypeWindow"),
diff --git a/installclass.py b/installclass.py
index a490ce9..92c4acd 100644
--- a/installclass.py
+++ b/installclass.py
@@ -89,6 +89,7 @@ class BaseInstallClass(object):
"language",
"keyboard",
"welcome",
+ "filter",
"storageinit",
"findrootparts",
"betanag",
diff --git a/instdata.py b/instdata.py
index 2e1503d..04af9c0 100644
--- a/instdata.py
+++ b/instdata.py
@@ -299,4 +299,6 @@ class InstallData:
self.isHeadless = 0
self.extraModules = extraModules

+ self.simpleFilter = True
+
self.reset()
diff --git a/iw/DeviceSelector.py b/iw/DeviceSelector.py
new file mode 100644
index 0000000..a542b21
--- /dev/null
+++ b/iw/DeviceSelector.py
@@ -0,0 +1,191 @@
+#
+# Filtering UI for the simple path through the storage code.
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see<http://www.gnu.org/licenses/>.
+#
+
+import gtk, gobject
+import gtk.glade
+import gui
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+# The column that holds a python object containing information about the
+# device in each row. This value really shouldn't be overridden.
+OBJECT_COL = 0
+
+# These columns can be overridden with the active= and visible= parameters to
+# __init__. active indicates which column tracks whether the row is checked
+# by default, and visible indicates which column tracks whether the row is
+# seen or not.
+VISIBLE_COL = 1
+ACTIVE_COL = 2
+
+class DeviceDisplayer(object):
+ def _column_toggled(self, menuItem, col):
+ # This is called when a selection is made in the column visibility drop
+ # down menu, and obviously makes a column visible (or not).
+ col.set_visible(not col.get_visible())
+
+ def __init__(self, store, model, view, active=ACTIVE_COL, visible=VISIBLE_COL):
+ self.store = store
+ self.model = model
+ self.view = view
+
+ self.menu = None
+
+ self.active = active
+ self.visible = visible
+
+ def addColumn(self, title, num, displayed=True):
+ cell = gtk.CellRendererText()
+ cell.set_property("yalign", 0)
+
+ col = gtk.TreeViewColumn(title, cell, text=num, active=self.active)
+ col.set_visible(displayed)
+ col.set_expand(True)
+ col.set_resizable(True)
+ self.view.append_column(col)
+
+ # This needs to be set on all columns or it will be impossible to sort
+ # by that column.
+ col.set_sort_column_id(num)
+
+ if self.menu:
+ # Add a new entry to the drop-down menu.
+ item = gtk.CheckMenuItem(title)
+ item.set_active(displayed)
+ item.connect("toggled", self._column_toggled, col)
+ item.show()
+ self.menu.append(item)
+
+ def createMenu(self):
+ self.menu = gtk.Menu()
+
+ # Add a blank column at the (current) end of the view. This column
+ # exists only so we can have a header to click on and display the
+ # drop down allowing column configuration.
+ menuCol = gtk.TreeViewColumn("")
+ menuCol.set_clickable(True)
+ menuCol.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ menuCol.set_fixed_width(30)
+ menuCol.connect("clicked", lambda col, menu: menu.popup(None, None, None, 0, 0),
+ self.menu)
+
+ image = gui.readImageFromFile("filter-menu.png")
+ image.show_all()
+ menuCol.set_widget(image)
+
+ # Make sure the menu column gets added after all other columns so it
+ # will be on the far right edge.
+ self.view.connect("show", lambda x: self.view.append_column(menuCol))
+
+ def getSelected(self):
+ """Return a list of all the items currently checked in the UI, or
+ an empty list if nothing is selected.
+ """
+ retval = []
+ iter = self.store.get_iter_first()
+
+ while iter:
+ if self.store.get_value(iter, self.active):
+ retval.append(self.store[iter])
+
+ iter = self.store.iter_next(iter)
+
+ return retval
+
+class DeviceSelector(DeviceDisplayer):
+ def createSelectionCol(self, title="", radioButton=False, toggledCB=None):
+ # Add a column full of checkboxes/radiobuttons in the first column of the view.
+ crt = gtk.CellRendererToggle()
+ crt.set_property("activatable", True)
+ crt.set_property("yalign", 0)
+ crt.set_radio(radioButton)
+
+ crt.connect("toggled", self._device_toggled, toggledCB, radioButton)
+
+ col = gtk.TreeViewColumn(title, crt, active=self.active)
+ col.set_alignment(0.75)
+
+ if not radioButton:
+ self.allButton = gtk.ToggleButton()
+ col.connect("clicked", lambda *args: self.allButton.set_active(self.allButton.get_activ e() != True))
+
+ col.set_widget(self.allButton)
+ self.allButton.show_all()
+
+ self.allButton.connect("toggled", self._all_clicked, toggledCB)
+
+ self.view.append_column(col)
+ self.view.set_headers_clickable(True)
+
+ def _all_clicked(self, button, cb=None):
+ # This is called when the Add/Remove all button is checked and does
+ # the obvious.
+ def _toggle_all(model, path, iter, set):
+ # Don't check the boxes of rows that aren't visible.
+ visible = model.get_value(iter, self.visible)
+ if not visible:
+ return
+
+ # Don't try to set a row to active if it's already been checked.
+ # This prevents devices that have been checked before the all
+ # button was checked from getting double counted.
+ if model.get_value(iter, self.active) == set:
+ return
+
+ model.set_value(iter, self.active, set)
+
+ if cb:
+ cb(set, model.get_value(iter, OBJECT_COL))
+
+ set = button.get_active()
+ self.store.foreach(_toggle_all, set)
+
+ def _device_toggled(self, button, row, cb, isRadio):
+ # This is called when the checkbox for a device is clicked or unclicked.
+ model = self.model
+ iter = model.get_iter(row)
+
+ if not iter:
+ return
+
+ while not self.store.iter_is_valid(iter):
+ iter = model.convert_iter_to_child_iter(iter)
+ model = model.get_model()
+
+ if isRadio:
+ # This is lame, but there's no other way to do it. First we have
+ # to uncheck everything in the store, then we check the one that
+ # was clicked on.
+ for r in self.store:
+ r[self.active] = False
+
+ self.store[iter][self.active] = True
+ else:
+ is_checked = self.store.get_value(iter, self.active)
+ self.store.set_value(iter, self.active, not is_checked)
+
+ if cb:
+ cb(not is_checked, self.store.get_value(iter, OBJECT_COL))
+
+# if self.callback.noneAreSelected():
+# self.allButton.set_active(False)
+# elif self.callback.allAreSelected():
+# self.allButton.set_active(True)
diff --git a/iw/filter_gui.py b/iw/filter_gui.py
new file mode 100644
index 0000000..972d059
--- /dev/null
+++ b/iw/filter_gui.py
@@ -0,0 +1,528 @@
+#
+# Storage filtering UI
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see<http://www.gnu.org/licenses/>.
+#
+
+import block
+import gtk, gobject
+import gtk.glade
+import gui
+import parted
+from DeviceSelector import *
+from baseudev import *
+from constants import *
+from iw_gui import *
+from storage.udev import *
+from storage.devicelibs.mpath import *
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+DEVICE_COL = 3
+MODEL_COL = 4
+CAPACITY_COL = 5
+VENDOR_COL = 6
+INTERCONNECT_COL = 7
+SERIAL_COL = 8
+WWID_COL = 9
+PATHS_COL = 10
+PORT_COL = 11
+TARGET_COL = 12
+LUN_COL = 13
+
+# These are global because they need to be accessible across all Callback
+# objects as the same values, and from the AdvancedFilterWindow object to add
+# and remove devices when populating scrolled windows.
+totalDevices = 0
+selectedDevices = 0
+totalSize = 0
+selectedSize = 0
+
+# These are global so they can be accessed from all Callback objects. The
+# basic callback defines its membership as anything that doesn't pass the
+# is* methods.
+def isCCISS(info):
+ return udev_device_is_cciss(info)
+
+def isRAID(info):
+ return udev_device_is_md(info) or udev_device_is_biosraid(info)
+
+def isMultipath(info):
+ return udev_device_is_multipath_member(info)
+
+def isOther(info)
+ return udev_device_is_iscsi(info) or udev_device_is_fcoe(info)
+
+class Callbacks(object):
+ def __init__(self, xml):
+ self.model = None
+ self.xml = xml
+
+ self.sizeLabel = self.xml.get_widget("sizeLabel")
+ self.sizeLabel.connect("realize", self._update_size_label)
+
+ def addToUI(self, tuple):
+ pass
+
+ def deviceToggled(self, set, device):
+ global selectedDevices, totalDevices
+ global selectedSize, totalSize
+
+ if set:
+ selectedDevices += 1
+ selectedSize += device["XXX_SIZE"]
+ else:
+ selectedDevices -= 1
+ selectedSize -= device["XXX_SIZE"]
+
+ self._update_size_label()
+
+ def isMember(self, info):
+ return info and not isRAID(info) and not isCCISS(info) and
+ not isMultipath(info) and not isOther(info)
+
+ def visible(self, model, iter, view):
+ # Most basic visibility function - does the model say this row
+ # should be visible? Subclasses can define their own more specific
+ # visibility function, though they should also take a look at this
+ # one to see what the model says.
+ return self.isMember(model.get_value(iter, OBJECT_COL)) and
+ model.get_value(iter, 1)
+
+ def _update_size_label(self, *args, **kwargs):
+ global selectedDevices, totalDevices
+ global selectedSize, totalSize
+
+ self.sizeLabel.set_markup(_("<b>%s device(s) (%s MB) selected</b> out of %s device(s) (%s MB) total.") % (selectedDevices, selectedSize, totalDevices, totalSize))
+
+class RAIDCallbacks(Callbacks):
+ def isMember(self, info):
+ return info and (isRAID(info) or isCCISS(info))
+
+class FilteredCallbacks(Callbacks):
+ def __init__(self, *args, **kwargs):
+ Callbacks.__init__(self, *args, **kwargs)
+
+ # Are we even applying the filtering UI? This is False when
+ # whateverFilterBy is empty, True the rest of the time.
+ self.filtering = False
+
+ def reset(self):
+ self.notebook.set_current_page(0)
+ self.filtering = False
+
+ def set(self, num):
+ self.notebook.set_current_page(num)
+ self.filtering = True
+
+class MPathCallbacks(FilteredCallbacks):
+ def __init__(self, *args, **kwargs):
+ FilteredCallbacks.__init__(self, *args, **kwargs)
+
+ self._vendors = []
+ self._interconnects = []
+
+ self.filterBy = self.xml.get_widget("mpathFilterBy")
+ self.notebook = self.xml.get_widget("mpathNotebook")
+
+ self.vendorEntry = self.xml.get_widget("mpathVendorEntry")
+ self.interconnectEntry = self.xml.get_widget("mpathInterconnectEntry")
+ self.WWIDEntry = self.xml.get_widget("mpathWWIDEntry")
+
+ self.vendorEntry.connect("changed", lambda entry: self.model.refilter())
+ self.vendorEntry.connect("realize", self._populateUI)
+ self.interconnectEntry.connect("changed", lambda entry: self.model.refilter())
+ self.WWIDEntry.connect("changed", lambda entry: self.model.refilter())
+
+ def addToUI(self, tuple):
+ if not tuple[VENDOR_COL] in self._vendors:
+ self._vendors.append(tuple[VENDOR_COL])
+
+ if not tuple[INTERCONNECT_COL] in self._interconnects:
+ self._interconnects.append(tuple[INTERCONNECT_COL])
+
+ def isMember(self, info):
+ return info and isMultipath(info)
+
+ def visible(self, model, iter, view):
+ if not FilteredCallbacks.visible(self, model, iter, view):
+ return False
+
+ if self.filtering:
+ if self.notebook.get_current_page() == 0:
+ return self._visible_by_interconnect(model, iter, view)
+ elif self.notebook.get_current_page() == 1:
+ return self._visible_by_vendor(model, iter, view)
+ elif self.notebook.get_current_page() == 2:
+ return self._visible_by_wwid(model, iter, view)
+
+ return True
+
+ def _populateUI(self, widget):
+ self._vendors.sort()
+ self.vendorEntry.set_model(gtk.ListStore(gobject.T YPE_STRING))
+ for v in self._vendors:
+ self.vendorEntry.append_text(v)
+
+ self._interconnects.sort()
+ self.interconnectEntry.set_model(gtk.ListStore(gob ject.TYPE_STRING))
+ for i in self._interconnects:
+ self.interconnectEntry.append_text(i)
+
+ def _visible_by_vendor(self, model, iter, view):
+ return model.get_value(iter, VENDOR_COL) == self.vendorEntry.get_text()
+
+ def _visible_by_interconnect(self, model, iter, view):
+ return model.get_value(iter, INTERCONNECT_COL) == self.interconnectEntry.get_text()
+
+ def _visible_by_wwid(self, model, iter, view):
+ # FIXME: make this support globs, etc.
+ entered = self.WWIDEntry.get_text()
+
+ return entered != "" and model.get_value(iter, WWID_COL).find(entered) != -1
+
+class OtherCallbacks(MPathCallbacks):
+ def __init__(self, *args, **kwargs):
+ FilteredCallbacks.__init__(self, *args, **kwargs)
+
+ self._vendors = []
+ self._interconnects = []
+
+ self.filterBy = self.xml.get_widget("otherFilterBy")
+ self.notebook = self.xml.get_widget("otherNotebook")
+
+ self.vendorEntry = self.xml.get_widget("otherVendorEntry")
+ self.interconnectEntry = self.xml.get_widget("otherInterconnectEntry")
+ self.WWIDEntry = self.xml.get_widget("otherWWIDEntry")
+
+ self.vendorEntry.connect("changed", lambda entry: self.model.refilter())
+ self.vendorEntry.connect("realize", self._populateUI)
+ self.interconnectEntry.connect("changed", lambda entry: self.model.refilter())
+ self.WWIDEntry.connect("changed", lambda entry: self.model.refilter())
+
+ def isMember(self, info):
+ return info and isOther(info)
+
+class SearchCallbacks(FilteredCallbacks):
+ def __init__(self, *args, **kwargs):
+ FilteredCallbacks.__init__(self, *args, **kwargs)
+
+ self._ports = []
+ self._targets = []
+ self._luns = []
+
+ self.filterBy = self.xml.get_widget("searchFilterBy")
+ self.notebook = self.xml.get_widget("searchNotebook")
+
+ self.portEntry = self.xml.get_widget("searchPortEntry")
+ self.targetEntry = self.xml.get_widget("searchTargetEntry")
+ self.LUNEntry = self.xml.get_widget("searchLUNEntry")
+ self.WWIDEntry = self.xml.get_widget("searchWWIDEntry")
+
+ # When these entries are changed, we need to redo the filtering.
+ # If we don't do filter-as-you-type, we'd need a Search/Clear button.
+ self.portEntry.connect("changed", lambda entry: self.model.refilter())
+ self.targetEntry.connect("changed", lambda entry: self.model.refilter())
+ self.LUNEntry.connect("changed", lambda entry: self.model.refilter())
+ self.WWIDEntry.connect("changed", lambda entry: self.model.refilter())
+
+ def isMember(self, info):
+ return True
+
+ def visible(self, model, iter, view):
+ if not model.get_value(iter, 1):
+ return False
+
+ if self.filtering:
+ if self.notebook.get_current_page() == 0:
+ return self._visible_by_ptl(model, iter, view)
+ else:
+ return self._visible_by_wwid(model, iter, view)
+
+ return True
+
+ def _visible_by_ptl(self, model, iter, view):
+ rowPort = model.get_value(iter, PORT_COL)
+ rowTarget = model.get_value(iter, TARGET_COL)
+ rowLUN = model.get_value(iter, LUN_COL)
+
+ enteredPort = self.portEntry.get_text()
+ enteredTarget = self.targetEntry.get_text()
+ enteredLUN = self.LUNEntry.get_text()
+
+ return (not enteredPort or enteredPort and enteredPort == rowPort) and
+ (not enteredTarget or enteredTarget and enteredTarget == rowTarget) and
+ (not enteredLUN or enteredLUN and enteredLUN == rowLUN)
+
+ def _visible_by_wwid(self, model, iter, view):
+ # FIXME: make this support globs, etc.
+ entered = self.WWIDEntry.get_text()
+
+ return entered != "" and model.get_value(iter, WWID_COL).find(entered) != -1
+
+class NotebookPage(object):
+ def __init__(self, store, name, xml, cb):
+ # Every page needs a ScrolledWindow to display the results in.
+ self.scroll = xml.get_widget("%sScroll" % name)
+
+ self.sortedModel = gtk.TreeModelSort(store)
+ self.filteredModel = store.filter_new()
+ self.treeView = gtk.TreeView(self.filteredModel)
+
+ self.scroll.add(self.treeView)
+
+ self.cb = cb
+ self.cb.model = self.filteredModel
+
+ self.ds = DeviceSelector(store, self.filteredModel, self.treeView,
+ visible=VISIBLE_COL, active=ACTIVE_COL)
+ self.ds.createMenu()
+ self.ds.createSelectionCol(toggledCB=self.cb.devic eToggled)
+
+ self.filteredModel.set_visible_func(self.cb.visibl e, self.treeView)
+
+ # Not every NotebookPage will have a filter box - just those that do
+ # some sort of filtering (obviously).
+ self.filterBox = xml.get_widget("%sFilterHBox" % name)
+
+ if self.filterBox:
+ self.filterBy = xml.get_widget("%sFilterBy" % name)
+ self.filterBy.connect("changed", self._filter_by_changed)
+
+ # However if the page has a filter box, then it must also have a
+ # notebook with an easily discoverable name.
+ self.notebook = xml.get_widget("%sNotebook" % name)
+
+ def _filter_by_changed(self, combo):
+ active = combo.get_active()
+
+ if active == -1:
+ self.cb.reset()
+ else:
+ self.cb.set(active)
+
+ self.filteredModel.refilter()
+
+class FilterWindow(InstallWindow):
+ windowTitle = N_("Device Filter")
+
+ def getNext(self):
+ # All pages use the same store, so we only need to use the first one.
+ # However, we do need to make sure all paths from multipath devices
+ # are in the list.
+ selected = set()
+ for dev in self.pages[0].ds.getSelected():
+ for path in dev[PATHS_COL].split():
+ selected.add(path)
+
+ selected.add(udev_device_get_name(dev[OBJECT_COL]))
+
+ if len(selected) == 0:
+ self.anaconda.intf.messageWindow(_("Error"),
+ _("You must select at least one "
+ "drive to be used for installation."),
+ custom_icon="error")
+ raise gui.StayOnScreen
+
+ self.anaconda.id.storage.exclusiveDisks.extend(lis t(selected))
+
+ def _addTuple(self, tuple):
+ global totalDevices, totalSize
+
+ self.store.append(None, tuple)
+ totalDevices += 1
+ totalSize += tuple[0]["XXX_SIZE"]
+
+ for pg in self.pages:
+ if pg.cb.isMember(tuple[0]):
+ pg.cb.addToUI(tuple)
+
+ def _makeBasic(self):
+ np = NotebookPage(self.store, "basic", self.xml, Callbacks(self.xml))
+
+ np.ds.addColumn(_("Model"), MODEL_COL)
+ np.ds.addColumn(_("Capacity"), CAPACITY_COL)
+ np.ds.addColumn(_("Vendor"), VENDOR_COL)
+ np.ds.addColumn(_("Interconnect"), INTERCONNECT_COL)
+ np.ds.addColumn(_("Serial Number"), SERIAL_COL)
+ return np
+
+ def _makeRAID(self):
+ np = NotebookPage(self.store, "raid", self.xml, RAIDCallbacks(self.xml))
+
+ np.ds.addColumn(_("Device"), DEVICE_COL)
+ np.ds.addColumn(_("Capacity"), CAPACITY_COL)
+ return np
+
+ def _makeMPath(self):
+ np = NotebookPage(self.store, "mpath", self.xml, MPathCallbacks(self.xml))
+
+ np.ds.addColumn(_("WWID"), WWID_COL)
+ np.ds.addColumn(_("Capacity"), CAPACITY_COL)
+ np.ds.addColumn(_("Vendor"), VENDOR_COL)
+ np.ds.addColumn(_("Interconnect"), INTERCONNECT_COL)
+ np.ds.addColumn(_("Paths"), PATHS_COL)
+ return np
+
+ def _makeOther(self)
+ np = NotebookPage(self.store, "other", self.xml, OtherCallbacks(self.xml))
+
+ np.ds.addColumn(_("WWID"), WWID_COL)
+ np.ds.addColumn(_("Capacity"), CAPACITY_COL)
+ np.ds.addColumn(_("Vendor"), VENDOR_COL)
+ np.ds.addColumn(_("Interconnect"), INTERCONNECT_COL)
+ return np
+
+ def _makeSearch(self):
+ np = NotebookPage(self.store, "search", self.xml, SearchCallbacks(self.xml))
+
+ np.ds.addColumn(_("Model"), MODEL_COL)
+ np.ds.addColumn(_("Capacity"), CAPACITY_COL, displayed=False)
+ np.ds.addColumn(_("Vendor"), VENDOR_COL)
+ np.ds.addColumn(_("Interconnect"), INTERCONNECT_COL, displayed=False)
+ np.ds.addColumn(_("Serial Number"), SERIAL_COL, displayed=False)
+ np.ds.addColumn(_("WWID"), WWID_COL)
+ np.ds.addColumn(_("Port"), PORT_COL)
+ np.ds.addColumn(_("Target"), TARGET_COL)
+ np.ds.addColumn(_("LUN"), LUN_COL)
+ return np
+
+ def _page_switched(self, notebook, useless, page_num):
+ # When the page is switched, we need to change what is visible so the
+ # Select All button only selects/deselected things on the current page.
+ # Unfortunately, the only way to do this is iterate over all rows and
+ # check for membership.
+ for line in self.store:
+ line[VISIBLE_COL] = self.pages[page_num].cb.isMember(line[OBJECT_COL])
+
+ def getScreen(self, anaconda):
+ (self.xml, self.vbox) = gui.getGladeWidget("filter.glade", "vbox")
+ self.buttonBox = self.xml.get_widget("buttonBox")
+ self.notebook = self.xml.get_widget("notebook")
+
+ self.notebook.connect("switch-page", self._page_switched)
+
+ self.pages = []
+
+ self.anaconda = anaconda
+
+ # One common store that all the views on all the notebook tabs share.
+ # Yes, this means a whole lot of columns that are going to be empty or
+ # unused much of the time. Oh well.
+
+ # Object,
+ # visible, active (checked),
+ # device, model, capacity, vendor, interconnect, serial number, wwid
+ # paths, port, target, lun
+ self.store = gtk.TreeStore(gobject.TYPE_PYOBJECT,
+ gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+ self.store.set_sort_column_id(MODEL_COL, gtk.SORT_ASCENDING)
+
+ if anaconda.id.simpleFilter:
+ self.pages = [self._makeBasic()]
+ self.notebook.set_show_border(False)
+ self.notebook.set_show_tabs(False)
+ else:
+ self.pages = [self._makeBasic(), self._makeRAID(),
+ self._makeMPath(), self._makeOther(),
+ self._makeSearch()]
+
+ udev_trigger(subsystem="block")
+ all_devices = filter(udev_device_is_disk, udev_get_block_devices())
+ (all_devices, mpaths, partitions) = identifyMultipaths(all_devices)
+
+ # The device list could be really long, so we really only want to
+ # iterate over it the bare minimum of times. Dividing this list up
+ # now means fewer elements to iterate over later.
+ (raid_devices, devices) = self.partition_list(lambda d: isRAID(d) and not isCCISS(d),
+ all_devices)
+
+ for d in devices:
+ partedDevice = parted.Device(path="/dev/" + udev_device_get_name(d))
+ d["XXX_SIZE"] = int(partedDevice.getSize())
+
+ tuple = (d, True, False, udev_device_get_name(d),
+ partedDevice.model, str(d["XXX_SIZE"]) + " MB",
+ udev_device_get_vendor(d), udev_device_get_bus(d),
+ udev_device_get_serial(d), udev_device_get_wwid(d),
+ "", "", "", "")
+ self._addTuple(tuple)
+
+ for md in block.getRaidSets():
+ md.activate(mknod=True, mkparts=False)
+ udev_settle()
+
+ partedDevice = md.PedDevice
+ size = int(partedDevice.getSize())
+ fstype = ""
+
+ members = map(lambda m: m.get_devpath(), list(md.get_members()))
+ for d in raid_devices:
+ if udev_device_get_name(d) in members:
+ fstype = udev_device_get_format(d)
+ break
+
+ # biosraid devices don't really get udev data, at least not in a
+ # way that's useful to the filtering UI. So we need to fake that
+ # data now so we have something to put into the store.
+ data = {"XXX_SIZE": size, "ID_FS_TYPE": fstype, "DM_NAME": md.name,
+ "name": md.name}
+
+ tuple = (data, True, False, md.name, partedDevice.model,
+ str(size) + " MB", "", "", "", "", "", "", "", "")
+ self._addTuple(tuple)
+
+ md.deactivate()
+
+ for mpath in mpaths:
+ # We only need to grab information from the first device in the set.
+ partedDevice = parted.Device(path="/dev/" + udev_device_get_name(mpath[0]))
+ mpath[0]["XXX_SIZE"] = int(partedDevice.getSize())
+ model = partedDevice.model
+
+ # However, we do need all the paths making up this multipath set.
+ paths = "
".join(map(udev_device_get_name, mpath))
+
+ tuple = (mpath[0], True, False, "", model,
+ str(mpath[0]["XXX_SIZE"]) + " MB",
+ udev_device_get_vendor(mpath[0]),
+ udev_device_get_bus(mpath[0]),
+ udev_device_get_serial(mpath[0]),
+ udev_device_get_wwid(mpath[0]),
+ paths, "", "", "")
+ self._addTuple(tuple)
+
+ return self.vbox
+
+ def partition_list(self, pred, lst):
+ pos = []
+ neg = []
+
+ for ele in lst:
+ if pred(ele):
+ pos.append(ele)
+ else:
+ neg.append(ele)
+
+ return (pos, neg)
diff --git a/kickstart.py b/kickstart.py
index a28eb03..147e48d 100644
--- a/kickstart.py
+++ b/kickstart.py
@@ -333,6 +333,7 @@ class IgnoreDisk(commands.ignoredisk.F8_IgnoreDisk):
def execute(self, anaconda):
anaconda.id.storage.ignoredDisks = self.ignoredisk
anaconda.id.storage.exclusiveDisks = self.onlyuse
+ self.handler.skipSteps.extend(["filter"])

class IscsiData(commands.iscsi.F10_IscsiData):
def execute(self, anaconda):
@@ -1408,6 +1409,7 @@ def setSteps(anaconda):
# out if we don't.
if anaconda.id.displayMode == "t":
missingSteps = [("bootloader", "Bootloader configuration"),
+ ("filter", "Disks to use in installation"),
("group-selection", "Package selection")]
errors = []

diff --git a/pixmaps/filter-menu.png b/pixmaps/filter-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..037519f9 dcd2e216430a8a6a64974cfef9f68aa0
GIT binary patch
literal 456
zcmV;(0XP1MP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDU v00004b3#c}2nYxW
zd<bNS00009a7bBm000fw000fw0YWI7cmMzZ8FWQhbW?9;ba!E LWdL_~cP?peYja~^
zaAhuUa%Y?FJQ@H10YynfK~zYI?NmKW!%!4G_tD`MD!4hj8GKM kLWyMTl7hdY{s9-q
z(3Fgw-Q4^G?!_?>2^~bFV}F8zgBc9hsUb~YqK$1)@W6$0&%M0!xbK6Q8 M}no>FWD1
zXqAQWczjsQN2Aee6TQ*^z{`@vag4!WkQW@sDQ=`{(^O2fBkCF kx`qJ&ShV|CwEJs%
z6)$7|X8-_?^LfsX-|w}06<-50uS5_8yoBd@Je*8<IGM8NdAVNf(eRb^{{B1oH~13T
zYnrC1ws}(>0I2+3hhg|eL@fY_qDU5NuIr`%kY!n}3n4&6DF86 @hwu9bwFcWp<1<i7
zb?WjT87QSXQp#t`vd#g(w(S!s<x{WMJ1zG%9+7F9XF`ZOBD!J b3nIEolH_)41^{5&
ywh7>YnJ;Iv*|oN*{5fqJN~xo|`tQ9(-^C~Aq4s7|cda@A0000<MNUMnLSTZTzQq;*

literal 0
HcmV?d00001

diff --git a/text.py b/text.py
index 89f017d..5b8900d 100644
--- a/text.py
+++ b/text.py
@@ -549,6 +549,7 @@ class InstallInterface:
self.screen.finish()

def setSteps(self, anaconda):
+ anaconda.dispatch.skipStep("filter")
anaconda.dispatch.skipStep("basepkgsel")
anaconda.dispatch.skipStep("group-selection")

diff --git a/ui/filter.glade b/ui/filter.glade
new file mode 100644
index 0000000..a889418
--- /dev/null
+++ b/ui/filter.glade
@@ -0,0 +1,1431 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="advancedFilterWindow">
+<property name="border_width">6</property>
+<property name="title" translatable="yes" context="yes"></property>
+<property name="type">GTK_WINDOW_TOPLEVEL</property>
+<property name="window_position">GTK_WIN_POS_NONE</property>
+<property name="modal">False</property>
+<property name="resizable">True</property>
+<property name="destroy_with_parent">False</property>
+<property name="decorated">True</property>
+<property name="skip_taskbar_hint">False</property>
+<property name="skip_pager_hint">False</property>
+<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+<property name="focus_on_map">True</property>
+<property name="urgency_hint">False</property>
+
+<child>
+<widget class="GtkVBox" id="vbox">
+<property name="visible">True</property>
+<property name="homogeneous">False</property>
+<property name="spacing">6</property>
+
+<child>
+ <widget class="GtkLabel" id="label1">
+ <property name="width_request">600</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Please select both the drives you'd like to install the operating system on and any devices you'd like to automatically mount to your system below:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+</child>
+
+<child>
+ <widget class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="basicScroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Basic Devices</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="raidScroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Firmware RAID</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="mpathVBox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="mpathFilterHBox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Filter By:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="mpathFilterBy">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Interconnect
+Vendor
+WWID</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="mpathNotebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show Only Devices Using:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="mpathInterconnectEntry">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label22</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show Only Devices From:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="mpathVendorEntry">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label23</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show WWIDs that Include:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="mpathWWIDEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">â—?</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label24</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="mpathScroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Multipath Devices</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="otherVBox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="otherFilterHBox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Filter By:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="otherFilterBy">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Interconnect
+Vendor
+WWID</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="otherNotebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show Only Devices Using:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="otherInterconnectEntry">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label22</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show Only Devices From:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="otherVendorEntry">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label23</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label40">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show WWIDs that Include:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="otherWWIDEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">â—?</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label41">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label24</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="otherScroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Other SAN Devices</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="searchVBox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="searchFilterHBox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search By:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="searchFilterBy">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Port / Target / LUN
+Target WWID</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <wid
 
Old 12-02-2009, 10:48 AM
Hans de Goede
 
Default Add an early user interface for filtering storage devices.

Hi,

Correction when trying to review patch 12/14 I decided it would be good to
look deeper into the non UI logic in this one, see my comments inline.



On 12/01/2009 09:15 PM, Chris Lumens wrote:

<snip>


+# These are global because they need to be accessible across all Callback
+# objects as the same values, and from the AdvancedFilterWindow object to add
+# and remove devices when populating scrolled windows.
+totalDevices = 0
+selectedDevices = 0
+totalSize = 0
+selectedSize = 0
+
+# These are global so they can be accessed from all Callback objects. The
+# basic callback defines its membership as anything that doesn't pass the
+# is* methods.
+def isCCISS(info):
+ return udev_device_is_cciss(info)
+


See my previous comments on this.


+def isRAID(info):
+ return udev_device_is_md(info) or udev_device_is_biosraid(info)
+


mdraid devices use partitions, not disks, and are software raid devices,
which should not be shown in the filtering UI, just like we don't show
lv's or anything else living above the "disk" level. Note that intel
BIOS RAID is special in that it does use mdraid now a days, but
udev_device_is_biosraid(), will identify Intel BIOS RAID anyways
independent of us using mdraid or dmraid (which we can still do using
the noiswmd cmdline option) for Intel BIOS RAID.

So I believe the udev_device_is_md(info) call here should be removed.

<snip>


+ def getScreen(self, anaconda):
+ (self.xml, self.vbox) = gui.getGladeWidget("filter.glade", "vbox")
+ self.buttonBox = self.xml.get_widget("buttonBox")
+ self.notebook = self.xml.get_widget("notebook")
+
+ self.notebook.connect("switch-page", self._page_switched)
+
+ self.pages = []
+
+ self.anaconda = anaconda
+
+ # One common store that all the views on all the notebook tabs share.
+ # Yes, this means a whole lot of columns that are going to be empty or
+ # unused much of the time. Oh well.
+
+ # Object,
+ # visible, active (checked),
+ # device, model, capacity, vendor, interconnect, serial number, wwid
+ # paths, port, target, lun
+ self.store = gtk.TreeStore(gobject.TYPE_PYOBJECT,
+ gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+ self.store.set_sort_column_id(MODEL_COL, gtk.SORT_ASCENDING)
+
+ if anaconda.id.simpleFilter:
+ self.pages = [self._makeBasic()]
+ self.notebook.set_show_border(False)
+ self.notebook.set_show_tabs(False)
+ else:
+ self.pages = [self._makeBasic(), self._makeRAID(),
+ self._makeMPath(), self._makeOther(),
+ self._makeSearch()]
+
+ udev_trigger(subsystem="block")
+ all_devices = filter(udev_device_is_disk, udev_get_block_devices())
+ (all_devices, mpaths, partitions) = identifyMultipaths(all_devices)
+
+ # The device list could be really long, so we really only want to
+ # iterate over it the bare minimum of times. Dividing this list up
+ # now means fewer elements to iterate over later.
+ (raid_devices, devices) = self.partition_list(lambda d: isRAID(d) and not isCCISS(d),
+ all_devices)
+


This seems wrong, we should not look at partitions to see if they are BIOS RAID, only at whole
disks, nor should we look at individual mpath disks in anyway, we should only every interact
with them at the multipath (devicemapper) device level which represent the single disk which
is hidden behind the mpath members for that disk. And here at the storage filtering level
we should not even do that, as we only care about the disk and not what is on it at this
point, simply displaying the disk in the mpath tab should be enough.


+ for d in devices:
+ partedDevice = parted.Device(path="/dev/" + udev_device_get_name(d))
+ d["XXX_SIZE"] = int(partedDevice.getSize())
+
+ tuple = (d, True, False, udev_device_get_name(d),
+ partedDevice.model, str(d["XXX_SIZE"]) + " MB",
+ udev_device_get_vendor(d), udev_device_get_bus(d),
+ udev_device_get_serial(d), udev_device_get_wwid(d),
+ "", "", "", "")
+ self._addTuple(tuple)
+
+ for md in block.getRaidSets():


Ok, so this returns a list of biosraid devices, *including* Intel BIOS RAID
devices independent of whether we will be using mdraid or dmraid to drive
Intel BIOS RAID sets. This uses pyblock, which uses libdmraid, which has no
clue we may not be using dmraid for ISW. This is not really a problem as using
libdmraid to identify these sets for filtering UI purposes is fine, and probably
a lot easier then using mdraid, but something which we should be aware of that
we are doing.

Also note that the name md is very confusing here, this is not a mdraid set, it
is a BIOS RAID set, which in some special cases may get assembled and handled
by mdraid in its special external metadata mode (the containers stuff), but
for anything but Intel these RAID sets are not handled by mdraid, please
s/md/raidset/ (or rs).


+ md.activate(mknod=True, mkparts=False)


Ok, this actually results in bringing the BIOS RAID set online, something which
I don't think we need (nor want) to do at this point. There should be other
ways to get the information you need from pyblock.


+ udev_settle()
+
+ partedDevice = md.PedDevice
+ size = int(partedDevice.getSize())
+ fstype = ""
+


Ah, this is why you do this, hmm, I guess I need to dig a bit into pyblock and come
up with an other way to get the size of the set, not sure if this will be easy to do
lets leave this as is for now, since a system should not have 1000's of BIOS RAID
sets this should not be much of an issue.


+ members = map(lambda m: m.get_devpath(), list(md.get_members()))
+ for d in raid_devices:
+ if udev_device_get_name(d) in members:
+ fstype = udev_device_get_format(d)
+ break
+
+ # biosraid devices don't really get udev data, at least not in a
+ # way that's useful to the filtering UI. So we need to fake that
+ # data now so we have something to put into the store.
+ data = {"XXX_SIZE": size, "ID_FS_TYPE": fstype, "DM_NAME": md.name,
+ "name": md.name}
+


This does not seem right, wrt ID_FS_TYPE, when calling udev_device_get_format()
on a member of for example an Intel BIOS RAID set, it will return isw_raid_member,
the fact that it is isw is already present in the md.name (which will start
with isw_), and isw_raid_member as FS_TYPE for the entire set is simply not true,
the set could contain a loopback layout with a foo filesystem, but most likely
will contain a partition table, which means that just like for a normal disk,
for the set ID_FS_TYPE will not be set.


+ tuple = (data, True, False, md.name, partedDevice.model,
+ str(size) + " MB", "", "", "", "", "", "", "", "")
+ self._addTuple(tuple)
+
+ md.deactivate()
+
+ for mpath in mpaths:
+ # We only need to grab information from the first device in the set.
+ partedDevice = parted.Device(path="/dev/" + udev_device_get_name(mpath[0]))
+ mpath[0]["XXX_SIZE"] = int(partedDevice.getSize())
+ model = partedDevice.model
+
+ # However, we do need all the paths making up this multipath set.
+ paths = "
".join(map(udev_device_get_name, mpath))
+
+ tuple = (mpath[0], True, False, "", model,
+ str(mpath[0]["XXX_SIZE"]) + " MB",
+ udev_device_get_vendor(mpath[0]),
+ udev_device_get_bus(mpath[0]),
+ udev_device_get_serial(mpath[0]),
+ udev_device_get_wwid(mpath[0]),
+ paths, "", "", "")
+ self._addTuple(tuple)
+
+ return self.vbox
+
+ def partition_list(self, pred, lst):
+ pos = []
+ neg = []
+
+ for ele in lst:
+ if pred(ele):
+ pos.append(ele)
+ else:
+ neg.append(ele)
+
+ return (pos, neg)


The name partition_list for this method is a bit confusing when
doing storage stuff, maybe name it split_list instead ?

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-02-2009, 11:08 AM
Hans de Goede
 
Default Add an early user interface for filtering storage devices.

Hi,

On 12/02/2009 12:48 PM, Hans de Goede wrote:

Hi,

Correction when trying to review patch 12/14 I decided it would be good to
look deeper into the non UI logic in this one, see my comments inline.




p.s.

All this bios raid stuff requires a fair bit of domain specific knowledge
(and with the recent move of Intel BIOS RAID using mdraid it has become
a bit muddy in some places). I think the most important thing right now
is to get the filtering stuff into place, once it is I'll start doing
various tests with both Intel and non Intel BIOS RAID and submit patches
where needed.

Regards,

Hans




On 12/01/2009 09:15 PM, Chris Lumens wrote:

<snip>


+# These are global because they need to be accessible across all
Callback
+# objects as the same values, and from the AdvancedFilterWindow
object to add
+# and remove devices when populating scrolled windows.
+totalDevices = 0
+selectedDevices = 0
+totalSize = 0
+selectedSize = 0
+
+# These are global so they can be accessed from all Callback objects.
The
+# basic callback defines its membership as anything that doesn't pass
the
+# is* methods.
+def isCCISS(info):
+ return udev_device_is_cciss(info)
+


See my previous comments on this.


+def isRAID(info):
+ return udev_device_is_md(info) or udev_device_is_biosraid(info)
+


mdraid devices use partitions, not disks, and are software raid devices,
which should not be shown in the filtering UI, just like we don't show
lv's or anything else living above the "disk" level. Note that intel
BIOS RAID is special in that it does use mdraid now a days, but
udev_device_is_biosraid(), will identify Intel BIOS RAID anyways
independent of us using mdraid or dmraid (which we can still do using
the noiswmd cmdline option) for Intel BIOS RAID.

So I believe the udev_device_is_md(info) call here should be removed.

<snip>


+ def getScreen(self, anaconda):
+ (self.xml, self.vbox) = gui.getGladeWidget("filter.glade", "vbox")
+ self.buttonBox = self.xml.get_widget("buttonBox")
+ self.notebook = self.xml.get_widget("notebook")
+
+ self.notebook.connect("switch-page", self._page_switched)
+
+ self.pages = []
+
+ self.anaconda = anaconda
+
+ # One common store that all the views on all the notebook tabs share.
+ # Yes, this means a whole lot of columns that are going to be empty or
+ # unused much of the time. Oh well.
+
+ # Object,
+ # visible, active (checked),
+ # device, model, capacity, vendor, interconnect, serial number, wwid
+ # paths, port, target, lun
+ self.store = gtk.TreeStore(gobject.TYPE_PYOBJECT,
+ gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING, gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+ self.store.set_sort_column_id(MODEL_COL, gtk.SORT_ASCENDING)
+
+ if anaconda.id.simpleFilter:
+ self.pages = [self._makeBasic()]
+ self.notebook.set_show_border(False)
+ self.notebook.set_show_tabs(False)
+ else:
+ self.pages = [self._makeBasic(), self._makeRAID(),
+ self._makeMPath(), self._makeOther(),
+ self._makeSearch()]
+
+ udev_trigger(subsystem="block")
+ all_devices = filter(udev_device_is_disk, udev_get_block_devices())
+ (all_devices, mpaths, partitions) = identifyMultipaths(all_devices)
+
+ # The device list could be really long, so we really only want to
+ # iterate over it the bare minimum of times. Dividing this list up
+ # now means fewer elements to iterate over later.
+ (raid_devices, devices) = self.partition_list(lambda d: isRAID(d)
and not isCCISS(d),
+ all_devices)
+


This seems wrong, we should not look at partitions to see if they are
BIOS RAID, only at whole
disks, nor should we look at individual mpath disks in anyway, we should
only every interact
with them at the multipath (devicemapper) device level which represent
the single disk which
is hidden behind the mpath members for that disk. And here at the
storage filtering level
we should not even do that, as we only care about the disk and not what
is on it at this
point, simply displaying the disk in the mpath tab should be enough.


+ for d in devices:
+ partedDevice = parted.Device(path="/dev/" + udev_device_get_name(d))
+ d["XXX_SIZE"] = int(partedDevice.getSize())
+
+ tuple = (d, True, False, udev_device_get_name(d),
+ partedDevice.model, str(d["XXX_SIZE"]) + " MB",
+ udev_device_get_vendor(d), udev_device_get_bus(d),
+ udev_device_get_serial(d), udev_device_get_wwid(d),
+ "", "", "", "")
+ self._addTuple(tuple)
+
+ for md in block.getRaidSets():


Ok, so this returns a list of biosraid devices, *including* Intel BIOS RAID
devices independent of whether we will be using mdraid or dmraid to drive
Intel BIOS RAID sets. This uses pyblock, which uses libdmraid, which has no
clue we may not be using dmraid for ISW. This is not really a problem as
using
libdmraid to identify these sets for filtering UI purposes is fine, and
probably
a lot easier then using mdraid, but something which we should be aware
of that
we are doing.

Also note that the name md is very confusing here, this is not a mdraid
set, it
is a BIOS RAID set, which in some special cases may get assembled and
handled
by mdraid in its special external metadata mode (the containers stuff), but
for anything but Intel these RAID sets are not handled by mdraid, please
s/md/raidset/ (or rs).


+ md.activate(mknod=True, mkparts=False)


Ok, this actually results in bringing the BIOS RAID set online,
something which
I don't think we need (nor want) to do at this point. There should be other
ways to get the information you need from pyblock.


+ udev_settle()
+
+ partedDevice = md.PedDevice
+ size = int(partedDevice.getSize())
+ fstype = ""
+


Ah, this is why you do this, hmm, I guess I need to dig a bit into
pyblock and come
up with an other way to get the size of the set, not sure if this will
be easy to do
lets leave this as is for now, since a system should not have 1000's of
BIOS RAID
sets this should not be much of an issue.


+ members = map(lambda m: m.get_devpath(), list(md.get_members()))
+ for d in raid_devices:
+ if udev_device_get_name(d) in members:
+ fstype = udev_device_get_format(d)
+ break
+
+ # biosraid devices don't really get udev data, at least not in a
+ # way that's useful to the filtering UI. So we need to fake that
+ # data now so we have something to put into the store.
+ data = {"XXX_SIZE": size, "ID_FS_TYPE": fstype, "DM_NAME": md.name,
+ "name": md.name}
+


This does not seem right, wrt ID_FS_TYPE, when calling
udev_device_get_format()
on a member of for example an Intel BIOS RAID set, it will return
isw_raid_member,
the fact that it is isw is already present in the md.name (which will start
with isw_), and isw_raid_member as FS_TYPE for the entire set is simply
not true,
the set could contain a loopback layout with a foo filesystem, but most
likely
will contain a partition table, which means that just like for a normal
disk,
for the set ID_FS_TYPE will not be set.


+ tuple = (data, True, False, md.name, partedDevice.model,
+ str(size) + " MB", "", "", "", "", "", "", "", "")
+ self._addTuple(tuple)
+
+ md.deactivate()
+
+ for mpath in mpaths:
+ # We only need to grab information from the first device in the set.
+ partedDevice = parted.Device(path="/dev/" +
udev_device_get_name(mpath[0]))
+ mpath[0]["XXX_SIZE"] = int(partedDevice.getSize())
+ model = partedDevice.model
+
+ # However, we do need all the paths making up this multipath set.
+ paths = "
".join(map(udev_device_get_name, mpath))
+
+ tuple = (mpath[0], True, False, "", model,
+ str(mpath[0]["XXX_SIZE"]) + " MB",
+ udev_device_get_vendor(mpath[0]),
+ udev_device_get_bus(mpath[0]),
+ udev_device_get_serial(mpath[0]),
+ udev_device_get_wwid(mpath[0]),
+ paths, "", "", "")
+ self._addTuple(tuple)
+
+ return self.vbox
+
+ def partition_list(self, pred, lst):
+ pos = []
+ neg = []
+
+ for ele in lst:
+ if pred(ele):
+ pos.append(ele)
+ else:
+ neg.append(ele)
+
+ return (pos, neg)


The name partition_list for this method is a bit confusing when
doing storage stuff, maybe name it split_list instead ?

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list


_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-02-2009, 05:29 PM
Chris Lumens
 
Default Add an early user interface for filtering storage devices.

> >+def isRAID(info):
> >+ return udev_device_is_md(info) or udev_device_is_biosraid(info)
> >+
>
> mdraid devices use partitions, not disks, and are software raid devices,
> which should not be shown in the filtering UI, just like we don't show
> lv's or anything else living above the "disk" level. Note that intel
> BIOS RAID is special in that it does use mdraid now a days, but
> udev_device_is_biosraid(), will identify Intel BIOS RAID anyways
> independent of us using mdraid or dmraid (which we can still do using
> the noiswmd cmdline option) for Intel BIOS RAID.

Right, I am trying to hide the components of these kind of raid devices
from the filtering UI.

> So I believe the udev_device_is_md(info) call here should be removed.

Okay, that's easy.

> This seems wrong, we should not look at partitions to see if they are BIOS RAID, only at whole
> disks, nor should we look at individual mpath disks in anyway, we should only every interact
> with them at the multipath (devicemapper) device level which represent the single disk which
> is hidden behind the mpath members for that disk. And here at the storage filtering level
> we should not even do that, as we only care about the disk and not what is on it at this
> point, simply displaying the disk in the mpath tab should be enough.

Nothing in the filtering UI works on a partition level. It's all disks.

For multipath - I want to present the multipath device itself as
something that can be filtered out, not the various disks that make it
up. Allowing filtering out just some of the underlying is going to lead
to a very confusing situation. However, I do also want to be able to
list all the paths in the UI. From customer discussions, it's clear
that they'd prefer to ignore whole multipath devices rather than picking
and choosing the components.

> Also note that the name md is very confusing here, this is not a mdraid set, it
> is a BIOS RAID set, which in some special cases may get assembled and handled
> by mdraid in its special external metadata mode (the containers stuff), but
> for anything but Intel these RAID sets are not handled by mdraid, please
> s/md/raidset/ (or rs).

Okay, I can pick some type-neutral name.

>
> >+ md.activate(mknod=True, mkparts=False)
>
> Ok, this actually results in bringing the BIOS RAID set online, something which
> I don't think we need (nor want) to do at this point. There should be other
> ways to get the information you need from pyblock.
>
> >+ udev_settle()
> >+
> >+ partedDevice = md.PedDevice
> >+ size = int(partedDevice.getSize())
> >+ fstype = ""
> >+
>
> Ah, this is why you do this, hmm, I guess I need to dig a bit into pyblock and come
> up with an other way to get the size of the set, not sure if this will be easy to do
> lets leave this as is for now, since a system should not have 1000's of BIOS RAID
> sets this should not be much of an issue.

Right, after examining the contents of the udev data and the pyblock
module, this appears to be the only way to extract this kind of
information. You simply have to have a device node to know certain
things about the raid device.

>
> >+ members = map(lambda m: m.get_devpath(), list(md.get_members()))
> >+ for d in raid_devices:
> >+ if udev_device_get_name(d) in members:
> >+ fstype = udev_device_get_format(d)
> >+ break
> >+
> >+ # biosraid devices don't really get udev data, at least not in a
> >+ # way that's useful to the filtering UI. So we need to fake that
> >+ # data now so we have something to put into the store.
> >+ data = {"XXX_SIZE": size, "ID_FS_TYPE": fstype, "DM_NAME": md.name,
> >+ "name": md.name}
> >+
>
> This does not seem right, wrt ID_FS_TYPE, when calling udev_device_get_format()
> on a member of for example an Intel BIOS RAID set, it will return isw_raid_member,
> the fact that it is isw is already present in the md.name (which will start
> with isw_), and isw_raid_member as FS_TYPE for the entire set is simply not true,
> the set could contain a loopback layout with a foo filesystem, but most likely
> will contain a partition table, which means that just like for a normal disk,
> for the set ID_FS_TYPE will not be set.

I need ID_FS_TYPE set so that when I pull this dict out of the store
later on, I can run things like udev_device_is_biosraid and
udev_device_is_multipath_member and get results instead of tracebacks.
I don't care about whether there's a loopback layout or a filesystem or
whatever on it. The filtering UI does not deal in filesystems.

> >+ def partition_list(self, pred, lst):
> >+ pos = []
> >+ neg = []
> >+
> >+ for ele in lst:
> >+ if pred(ele):
> >+ pos.append(ele)
> >+ else:
> >+ neg.append(ele)
> >+
> >+ return (pos, neg)
>
> The name partition_list for this method is a bit confusing when
> doing storage stuff, maybe name it split_list instead ?

See: http://www.standardml.org/Basis/list.html#SIG:LIST.partition:VAL
This is a fairly common name for a list functional in other languages.
I suppose I could change it if I have to.

- Chris

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-02-2009, 05:30 PM
Chris Lumens
 
Default Add an early user interface for filtering storage devices.

> All this bios raid stuff requires a fair bit of domain specific knowledge
> (and with the recent move of Intel BIOS RAID using mdraid it has become
> a bit muddy in some places). I think the most important thing right now
> is to get the filtering stuff into place, once it is I'll start doing
> various tests with both Intel and non Intel BIOS RAID and submit patches
> where needed.

I would really prefer to do this stuff myself. I'm fine with taking
suggestions and advice, but I've been spending at least the last 10
weeks on this code and know it pretty well. I'd like to see it through
to completion.

- Chris

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-02-2009, 05:49 PM
Hans de Goede
 
Default Add an early user interface for filtering storage devices.

Hi,

On 12/02/2009 07:30 PM, Chris Lumens wrote:

All this bios raid stuff requires a fair bit of domain specific knowledge
(and with the recent move of Intel BIOS RAID using mdraid it has become
a bit muddy in some places). I think the most important thing right now
is to get the filtering stuff into place, once it is I'll start doing
various tests with both Intel and non Intel BIOS RAID and submit patches
where needed.


I would really prefer to do this stuff myself. I'm fine with taking
suggestions and advice, but I've been spending at least the last 10
weeks on this code and know it pretty well. I'd like to see it through
to completion.



As you wish, I have plenty of other stuff to keep me busy As always
don't hesitate to ask questions.

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-03-2009, 08:16 AM
Hans de Goede
 
Default Add an early user interface for filtering storage devices.

Hi,

On 12/02/2009 07:29 PM, Chris Lumens wrote:

+def isRAID(info):
+ return udev_device_is_md(info) or udev_device_is_biosraid(info)
+


mdraid devices use partitions, not disks, and are software raid devices,
which should not be shown in the filtering UI, just like we don't show
lv's or anything else living above the "disk" level. Note that intel
BIOS RAID is special in that it does use mdraid now a days, but
udev_device_is_biosraid(), will identify Intel BIOS RAID anyways
independent of us using mdraid or dmraid (which we can still do using
the noiswmd cmdline option) for Intel BIOS RAID.


Right, I am trying to hide the components of these kind of raid devices
from the filtering UI.



I understand, but if a disk has an mdraid superblock on it (which can
happen when people use whole disks as raid sets) we should still show
it, just like we should still use it if the whole disk is used as a PV.

But with the udev_device_is_md(info) call removed, this wont be an issue
anymore.


So I believe the udev_device_is_md(info) call here should be removed.


Okay, that's easy.



ok


This seems wrong, we should not look at partitions to see if they are BIOS RAID, only at whole
disks, nor should we look at individual mpath disks in anyway, we should only every interact
with them at the multipath (devicemapper) device level which represent the single disk which
is hidden behind the mpath members for that disk. And here at the storage filtering level
we should not even do that, as we only care about the disk and not what is on it at this
point, simply displaying the disk in the mpath tab should be enough.


Nothing in the filtering UI works on a partition level. It's all disks.



Ah, sorry I got confused by this bit:

+ all_devices = filter(udev_device_is_disk, udev_get_block_devices())
+ (all_devices, mpaths, partitions) = identifyMultipaths(all_devices)
+
+ # The device list could be really long, so we really only want to
+ # iterate over it the bare minimum of times. Dividing this list up
+ # now means fewer elements to iterate over later.
+ (raid_devices, devices) = self.partition_list(lambda d: isRAID(d) and not isCCISS(d),
+ all_devices)

Notice how the second line of this bit, overrides all_devices to just be non multipath
disks, maybe it should then no longer be named all_devices? This confused me
into thinking that the last line of the quoted bit operated on, well, all (block) devices.


For multipath - I want to present the multipath device itself as
something that can be filtered out, not the various disks that make it
up. Allowing filtering out just some of the underlying is going to lead
to a very confusing situation. However, I do also want to be able to
list all the paths in the UI. From customer discussions, it's clear
that they'd prefer to ignore whole multipath devices rather than picking
and choosing the components.



I completely agree, me remakr about this was caused by the same confusion.


Also note that the name md is very confusing here, this is not a mdraid set, it
is a BIOS RAID set, which in some special cases may get assembled and handled
by mdraid in its special external metadata mode (the containers stuff), but
for anything but Intel these RAID sets are not handled by mdraid, please
s/md/raidset/ (or rs).


Okay, I can pick some type-neutral name.



Yes please




+ md.activate(mknod=True, mkparts=False)


Ok, this actually results in bringing the BIOS RAID set online, something which
I don't think we need (nor want) to do at this point. There should be other
ways to get the information you need from pyblock.


+ udev_settle()
+
+ partedDevice = md.PedDevice
+ size = int(partedDevice.getSize())
+ fstype = ""
+


Ah, this is why you do this, hmm, I guess I need to dig a bit into pyblock and come
up with an other way to get the size of the set, not sure if this will be easy to do
lets leave this as is for now, since a system should not have 1000's of BIOS RAID
sets this should not be much of an issue.


Right, after examining the contents of the udev data and the pyblock
module, this appears to be the only way to extract this kind of
information. You simply have to have a device node to know certain
things about the raid device.



I'm afraid that might be by far the easiest yes.




+ members = map(lambda m: m.get_devpath(), list(md.get_members()))
+ for d in raid_devices:
+ if udev_device_get_name(d) in members:
+ fstype = udev_device_get_format(d)
+ break
+
+ # biosraid devices don't really get udev data, at least not in a
+ # way that's useful to the filtering UI. So we need to fake that
+ # data now so we have something to put into the store.
+ data = {"XXX_SIZE": size, "ID_FS_TYPE": fstype, "DM_NAME": md.name,
+ "name": md.name}
+


This does not seem right, wrt ID_FS_TYPE, when calling udev_device_get_format()
on a member of for example an Intel BIOS RAID set, it will return isw_raid_member,
the fact that it is isw is already present in the md.name (which will start
with isw_), and isw_raid_member as FS_TYPE for the entire set is simply not true,
the set could contain a loopback layout with a foo filesystem, but most likely
will contain a partition table, which means that just like for a normal disk,
for the set ID_FS_TYPE will not be set.


I need ID_FS_TYPE set so that when I pull this dict out of the store
later on, I can run things like udev_device_is_biosraid and
udev_device_is_multipath_member and get results instead of tracebacks.
I don't care about whether there's a loopback layout or a filesystem or
whatever on it. The filtering UI does not deal in filesystems.



Ok, well the above code then assumes that udev_device_get_format() will return
the same for disks in a set, which it of course should, but I've sane cases where
it would not (due to a broken BIOS not writing the correct metadata). Anyways in
that case I think this is ok.


+ def partition_list(self, pred, lst):
+ pos = []
+ neg = []
+
+ for ele in lst:
+ if pred(ele):
+ pos.append(ele)
+ else:
+ neg.append(ele)
+
+ return (pos, neg)


The name partition_list for this method is a bit confusing when
doing storage stuff, maybe name it split_list instead ?


See: http://www.standardml.org/Basis/list.html#SIG:LIST.partition:VAL
This is a fairly common name for a list functional in other languages.
I suppose I could change it if I have to.


If this is a common name for such a function, then its fine to leave it
as is, lets not turn this into a color of the bikeshed discussion

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-03-2009, 02:05 PM
Chris Lumens
 
Default Add an early user interface for filtering storage devices.

> + all_devices = filter(udev_device_is_disk, udev_get_block_devices())
> + (all_devices, mpaths, partitions) = identifyMultipaths(all_devices)
> +
> + # The device list could be really long, so we really only want to
> + # iterate over it the bare minimum of times. Dividing this list up
> + # now means fewer elements to iterate over later.
> + (raid_devices, devices) = self.partition_list(lambda d: isRAID(d) and not isCCISS(d),
> + all_devices)
>
> Notice how the second line of this bit, overrides all_devices to just be non multipath
> disks, maybe it should then no longer be named all_devices? This confused me
> into thinking that the last line of the quoted bit operated on, well, all (block) devices.

Right, it becomes a problem of naming here. What do you call the first
element in the tuple returned by identifyMultipaths? devices?
simple_devices (they're not necessarily simple)? basic_devices (they're
not necessarily basic)?

Something could probably be done here.

> Ok, well the above code then assumes that udev_device_get_format() will return
> the same for disks in a set, which it of course should, but I've sane cases where
> it would not (due to a broken BIOS not writing the correct metadata). Anyways in
> that case I think this is ok.

Can I get the same information somewhere else that's going to be more
reliable?

Of course, nothing's really reliable where disks are concerned...

> >See: http://www.standardml.org/Basis/list.html#SIG:LIST.partition:VAL
> >This is a fairly common name for a list functional in other languages.
> >I suppose I could change it if I have to.
>
> If this is a common name for such a function, then its fine to leave it
> as is, lets not turn this into a color of the bikeshed discussion

Meh, I can see how it'd be confusing.

- Chris

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-04-2009, 08:08 AM
Hans de Goede
 
Default Add an early user interface for filtering storage devices.

Hi,

On 12/03/2009 04:05 PM, Chris Lumens wrote:

+ all_devices = filter(udev_device_is_disk, udev_get_block_devices())
+ (all_devices, mpaths, partitions) = identifyMultipaths(all_devices)
+
+ # The device list could be really long, so we really only want to
+ # iterate over it the bare minimum of times. Dividing this list up
+ # now means fewer elements to iterate over later.
+ (raid_devices, devices) = self.partition_list(lambda d: isRAID(d) and not isCCISS(d),
+ all_devices)

Notice how the second line of this bit, overrides all_devices to just be non multipath
disks, maybe it should then no longer be named all_devices? This confused me
into thinking that the last line of the quoted bit operated on, well, all (block) devices.


Right, it becomes a problem of naming here. What do you call the first
element in the tuple returned by identifyMultipaths? devices?
simple_devices (they're not necessarily simple)? basic_devices (they're
not necessarily basic)?

Something could probably be done here.



Maybe just devices, like you call them later on. So then devices would get
partitioned into devices and raid_devices. or just call them non_multipath_devices,
which is long, but we only use it 2 times.



Ok, well the above code then assumes that udev_device_get_format() will return
the same for disks in a set, which it of course should, but I've sane cases where
it would not (due to a broken BIOS not writing the correct metadata). Anyways in
that case I think this is ok.


Can I get the same information somewhere else that's going to be more
reliable?



I'm afraid not.


Of course, nothing's really reliable where disks are concerned...



Right, which is the real problem.

Regards,

Hans

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 
Old 12-09-2009, 05:15 PM
Chris Lumens
 
Default Add an early user interface for filtering storage devices.

> Maybe just devices, like you call them later on. So then devices would get
> partitioned into devices and raid_devices. or just call them non_multipath_devices,
> which is long, but we only use it 2 times.

I did some renaming here to make it less confusing.

- Chris

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@redhat.com
https://www.redhat.com/mailman/listinfo/anaconda-devel-list
 

Thread Tools




All times are GMT. The time now is 10:46 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org