Adds backward compatibility to the BootManager for older bootcds. Rather than
authorStephen Soltesz <soltesz@cs.princeton.edu>
Wed, 20 Feb 2008 18:22:51 +0000 (18:22 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Wed, 20 Feb 2008 18:22:51 +0000 (18:22 +0000)
failing to load the new pciscan library, it will succeed with the old
behavior.  In particular some devices will fail to be detected using the old
bootcd.  But, this shouldn't be that much of a problem.

Daniel has proposed that the pypciscan.so library simply be re-written in
python by parsing /proc/bus/pci/devices to create a structure like:

    pypciscan.get_devices()
returns {'<domain>:<bus>:<dev>.<func>' :
(<vendor id>, <device id>, <subvendor id>, <subdevice id>, <device class << 8 | progif>)}
for all PCI devices.

I agree that this would be a better approach.  Would have to check that the
older bootcd kernel exports all of this information, then rewrite the module.

source/merge_hw_tables.py [new file with mode: 0755]
source/steps/StartDebug.py
source/systeminfo.py
source/systeminfo_30.py [new file with mode: 0755]
source/systeminfo_34.py [new file with mode: 0755]

diff --git a/source/merge_hw_tables.py b/source/merge_hw_tables.py
new file mode 100755 (executable)
index 0000000..dfe3378
--- /dev/null
@@ -0,0 +1,332 @@
+#!/usr/bin/python2
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+
+
+"""
+The point of this small utility is to take a file in the format
+of /lib/modules/`uname -r`/modules.pcimap and /usr/share/hwdata/pcitable
+and output a condensed, more easily used format for module detection. This is
+done by first getting a list of all the built modules, then loading the
+pci ids for each of those modules from modules.pcimap, then finally merging
+in the contents of pcitable (for built modules only). The result should be
+a file with a pretty comprehensive mapping of pci ids to module names.
+
+The output is used by the PlanetLab boot cd (3.0+) and the pl_hwinit script
+to load all the applicable modules by scanning lspci output.
+
+
+
+Expected format of file modules.dep includes lines of:
+
+/full/module/path/mod.ko: <dependencies>
+
+Expected format of file modules.pcimap includes lines of:
+
+# pci_module vendor device subvendor subdevice class class_mask driver_data
+cciss 0x00000e11 0x0000b060 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
+cciss 0x00000e11 0x0000b178 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
+
+Expected format of file pcitable includes lines of:
+
+# ("%d\t%d\t%s\t"%s"\n", vendid, devid, moduleName, cardDescription)
+# or ("%d\t%d\t%d\t%d\t%s\t"%s"\n", vendid, devid, subvendid, subdevid, moduleNa
+# me, cardDescription)
+0x0e11  0x0508  "tmspci"        "Compaq|Netelligent 4/16 Token Ring"
+0x1000  0x0407  0x8086  0x0532  "megaraid"      "Storage RAID Controller SRCU42X"
+
+Lines listing a module name of ignore or unknown from pcitable are skipped
+
+
+
+Output format, for each line that matches the above lines:
+cciss 0e11:b060 0e11:b178
+
+"""
+
+import os, sys
+import string
+import StringIO
+
+PCI_ANY = 0xffffffffL
+
+def merge_files(modules_dep_path, modules_pcimap_path, pcitable_path):
+    """
+    merge the three files as described above, and return a dictionary.
+    keys are module names, value is a list of pci ids for that module,
+    in the form "0e11:b178"
+    """
+
+    try:
+        modulesdep_file= file(modules_dep_path,"r")
+    except IOError:
+        sys.stderr.write( "Unable to open modules.dep: %s\n" %
+                          modules_dep_path )
+        return
+
+    try:
+        pcimap_file= file(modules_pcimap_path,"r")
+    except IOError:
+        sys.stderr.write( "Unable to open modules.pcimap: %s\n" %
+                          modules_pcimap_path )
+        return
+
+    try:
+        pcitable_file= file(pcitable_path,"r")
+    except IOError:
+        sys.stderr.write( "Unable to open pcitable: %s\n" %
+                          pcitable_path )
+       pcitable_file=StringIO.StringIO()
+
+    # associative array to store all matches of module -> ['vendor:device',..]
+    # entries
+    all_modules= {}
+    all_pci_ids= {}
+
+    # first step, create an associative array of all the built modules
+    for line in modulesdep_file:
+        parts= string.split(line,":")
+        if len(parts) < 2:
+            continue
+
+        full_mod_path= parts[0]
+        parts= string.split(full_mod_path,"/")
+        module= parts[len(parts)-1]
+        module_len= len(module)
+        if module[module_len-3:] == ".ko":
+            module= module[:-3]
+            all_modules[module]= []
+
+    modulesdep_file.close()
+
+    # now, parse the pcimap and add devices
+    line_num= 0
+    for line in pcimap_file:
+        line_num= line_num+1
+
+        # skip blank lines, or lines that begin with # (comments)
+        line= string.strip(line)
+        if len(line) == 0:
+            continue
+
+        if line[0] == "#":
+            continue
+
+        line_parts= string.split(line)
+        if line_parts is None or len(line_parts) != 8:
+            sys.stderr.write( "Skipping line %d in pcimap " \
+                              "(incorrect format %s)\n" % (line_num,line) )
+            continue
+
+        # first two parts are always vendor / device id
+        module= line_parts[0]
+
+        # XXX In kernel versions <2.6.14, mptscsih is the actual
+        # module that should be loaded instead of mptbase.
+        if module == "mptbase":
+            module= "mptscsih"
+
+        try:
+            vendor_id= long(line_parts[1],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect vendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[1]))
+            continue
+
+        try:
+            device_id= long(line_parts[2],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect device id format %s)\n" % (line_num,modules_pcimap_path,line_parts[2]))
+            continue
+
+        try:
+            subvendor_id= long(line_parts[3],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect subvendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[3]))
+            continue
+
+        try:
+            subdevice_id= long(line_parts[4],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect subdevice id format %s)\n" % (line_num,modules_pcimap_path,line_parts[4]))
+            continue
+
+        full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
+        if not all_modules.has_key(module):
+            # normally shouldn't get here, as the list is
+            # prepopulated with all the built modules
+
+            # XXX we probably shouldn't be doing this at all
+            all_modules[module] = [full_id]
+        else:
+            all_modules[module].append(full_id)
+            
+        if all_pci_ids.has_key(full_id):
+            # conflict as there are multiple modules that support
+            # particular pci device
+            all_pci_ids[full_id].append(module)
+        else:
+            all_pci_ids[full_id]= [module]
+
+    pcimap_file.close()
+
+    # parse pcitable, add any more ids for the devices
+    # We make the (potentially risky) assumption that pcitable contains
+    # only unique (vendor,device,subvendor,subdevice) entries.
+    line_num= 0
+    for line in pcitable_file:
+        line_num= line_num+1
+
+        # skip blank lines, or lines that begin with # (comments)
+        line= string.strip(line)
+        if len(line) == 0:
+            continue
+
+        if line[0] == "#":
+            continue
+
+        line_parts= string.split(line)
+        if line_parts is None or len(line_parts) <= 2:
+            sys.stderr.write( "Skipping line %d in pcitable " \
+                              "(incorrect format 1)\n" % line_num )
+            continue
+
+        # vendor id is always the first field, device the second. also,
+        # strip off first two chars (the 0x)
+        try:
+            vendor_id= long(line_parts[0],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping vendor_id %s in %s on line %d\n" \
+                              % (line_parts[0],pcitable_path,line_num))
+            continue
+
+        try:
+            device_id= long(line_parts[1],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping device %s in %s on line %d\n" \
+                              % (line_parts[1],pcitable_path,line_num))
+            continue
+
+        # if the first char of the third field is a double
+        # quote, the third field is a module, else if the first
+        # char of the third field is a 0 (zero), the fifth field is
+        # the module name. it would nice if there was any easy way
+        # to split a string on spaces, but recognize quoted strings,
+        # so they wouldn't be split up. that is the reason for this wierd check
+        if line_parts[2][0] == '"':
+            module= line_parts[2]
+
+            subvendor_id=PCI_ANY
+            subdevice_id=PCI_ANY
+        elif line_parts[2][0] == '0':
+            try:
+                module= line_parts[4]
+            except ValueError, e:
+                sys.stderr.write( "Skipping line %d in pcitable " \
+                                  "(incorrect format 2)\n" % line_num )
+                continue
+            try:
+                subvendor_id= long(line_parts[2],16)
+            except ValueError, e:
+                sys.stderr.write( "Skipping line %d in pcitable " \
+                                  "(incorrect format 2a)\n" % line_num )
+                continue
+            
+            try:
+                subdevice_id= long(line_parts[3],16)
+            except ValueError, e:
+                sys.stderr.write( "Skipping line %d in pcitable " \
+                                  "(incorrect format 2b)\n" % line_num )
+
+        else:
+            sys.stderr.write( "Skipping line %d in pcitable " \
+                              "(incorrect format 3)\n" % line_num )
+            continue
+
+        # remove the first and last char of module (quote marks)
+        module= module[1:]
+        module= module[:len(module)-1]
+
+        full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
+
+        if not all_modules.has_key(module):
+            # Do not process any modules listed in pcitable for which
+            # we do not have a prebuilt module.
+            continue
+
+        if not full_id in all_modules[module]:
+            all_modules[module].append(full_id)
+
+        if all_pci_ids.has_key(full_id):
+            if not module in all_pci_ids[full_id]:
+                all_pci_ids[full_id].append(module)
+            
+            # check if there are duplicate mappings between modules
+            # and full_ids
+            if len(all_pci_ids[full_id])>1:
+                # collect the set of modules that are different than what
+                # is listed in the pcitables file
+                other_modules = []
+                for other_module in all_pci_ids[full_id]:
+                    if other_module != module:
+                        other_modules.append(other_module)
+
+                # remove full_id from the set of other modules in all_modules {}
+                for other_module in other_modules:
+                    all_modules[other_module].remove(full_id)
+
+                # ensure that there is only one full_id -> module 
+                all_pci_ids[full_id] = [module]
+
+        else:
+            all_pci_ids[full_id] = [module]
+                
+    pcitable_file.close()
+
+    return (all_pci_ids,all_modules)
+
+if __name__ == "__main__":
+    def usage():
+        print( "\nUsage:" )
+        print( "%s <modules.dep> <modules.pcimap> " \
+               "<pcitable> [<output>]" % sys.argv[0] )
+        print( "" )
+        
+    if len(sys.argv) < 4:
+        usage()
+        sys.exit(1)
+
+
+    if len(sys.argv) > 4:
+        output_file_name= sys.argv[4]
+        try:
+            output_file= file(output_file_name,"w")
+        except IOError:
+            sys.stderr.write( "Unable to open %s for writing.\n" % output_file )
+            sys.exit(1)
+    else:
+        output_file= sys.stdout
+
+
+    (all_pci_ids,all_modules)=merge_files( sys.argv[1],
+                                           sys.argv[2],
+                                           sys.argv[3] )
+    if all_modules is not None:
+        for module in all_modules.keys():
+            pci_ids = all_modules[module]
+            if len(pci_ids)>0:
+                output_file.write("%s" % module)
+                for pci_id in pci_ids:
+                    output_file.write(" %x:%x:%x:%x" % pci_id)
+                output_file.write(" \n")
+    else:
+        sys.stderr.write( "Unable to list modules.\n" )
+
+    output_file.close()
index 286e5fc..7796ec8 100644 (file)
@@ -75,6 +75,9 @@ def Run( vars, log ):
         pass
 
     if not sshd_started:
+        # NOTE: these commands hang if ssh_host_*_key files exist, b/c 
+        #     ssh-keygen asks for user input to confirm the overwrite.  
+               #     could fix this with "echo 'y' | "
         log.write( "Creating ssh host keys\n" )
         
         utils.makedirs( ssh_dir )
index 31f8bb2..ea31085 100755 (executable)
 # Copyright (c) 2003 Intel Corporation
 # All rights reserved.
 #
-# Copyright (c) 2004-2006 The Trustees of Princeton University
+# Copyright (c) 2004-2008 The Trustees of Princeton University
 # All rights reserved.
-# expected /proc/partitions format
 
-
-#----------------------------------------------------
-#major minor  #blocks  name
-#
-#3     0   40017915 hda
-#3     1     208813 hda1
-#3     2   20482875 hda2
-#3     3     522112 hda3
-#3     4   18804082 hda4
-#----------------------------------------------------
-
-
-import string
-import sys
-import os
-import popen2
-import re
-import errno
-import ModelOptions
-import pypciscan
-import pypcimap
-from Exceptions import *
-
-"""
-a utility class for finding and returning information about
-block devices, memory, and other hardware on the system
-"""
-
-PROC_MEMINFO_PATH= "/proc/meminfo"
-PROC_PARTITIONS_PATH= "/proc/partitions"
-
-# set when the sfdisk -l <dev> trick has been done to make
-# all devices show up
-DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
-
-# a /proc/partitions block is 1024 bytes
-# a GB to a HDD manufacturer is 10^9 bytes
-BLOCKS_PER_GB = pow(10, 9) / 1024.0;
-
-
-# -n is numeric ids (no lookup), -m is machine readable
-LSPCI_CMD= "/sbin/lspci -nm"
-
-MODULE_CLASS_NETWORK= "network"
-MODULE_CLASS_SCSI= "scsi"
-
-PCI_BASE_CLASS_NETWORK=0x02L
-PCI_BASE_CLASS_STORAGE=0x01L
-
-PCI_ANY=0xffffffffL
-
-def get_total_phsyical_mem(vars = {}, log = sys.stderr):
-    """
-    return the total physical memory of the machine, in kilobytes.
-
-    Return None if /proc/meminfo not readable.
-    """
-
-    try:
-        meminfo_file= file(PROC_MEMINFO_PATH,"r")
-    except IOError, e:
-        return
-
-    total_memory= None
-
-    for line in meminfo_file:
-
-        try:
-            (fieldname,value)= string.split(line,":")
-        except ValueError, e:
-            # this will happen for lines that don't have two values
-            # (like the first line on 2.4 kernels)
-            continue
-
-        fieldname= string.strip(fieldname)
-        value= string.strip(value)
-        
-        if fieldname == "MemTotal":
-            try:
-                (total_memory,units)= string.split(value)
-            except ValueError, e:
-                return
-
-            if total_memory == "" or total_memory == None or \
-                   units == "" or units == None:
-                return
-
-            if string.lower(units) != "kb":
-                return
-
-            try:
-                total_memory= int(total_memory)
-            except ValueError, e:
-                return
-
-            break
-
-    meminfo_file.close()
-    return total_memory
-
-def get_block_device_list(vars = {}, log = sys.stderr):
-    """
-    get a list of block devices from this system.
-    return an associative array, where the device name
-    (full /dev/device path) is the key, and the value
-    is a tuple of (major,minor,numblocks,gb_size,readonly)
-    """
-
-    # make sure we can access to the files/directories in /proc
-    if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
-        return None
-
-    # table with valid scsi/sata/ide/raid block device names
-    valid_blk_names = {}
-    # add in valid sd and hd block device names
-    for blk_prefix in ('sd','hd'):
-        for blk_num in map ( \
-            lambda x: chr(x), range(ord('a'),ord('z')+1)):
-            devicename="%s%c" % (blk_prefix, blk_num)
-            valid_blk_names[devicename]=None
-
-    # add in valid scsi raid block device names
-    for M in range(0,1+1):
-        for N in range(0,7+1):
-            devicename = "cciss/c%dd%d" % (M,N)
-            valid_blk_names[devicename]=None
-
-    for devicename in valid_blk_names.keys():
-        # devfs under 2.4 (old boot cds) used to list partitions
-        # in a format such as scsi/host0/bus0/target0/lun0/disc
-        # and /dev/sda, etc. were just symlinks
-        try:
-            devfsname= os.readlink( "/dev/%s" % devicename )
-            valid_blk_names[devfsname]=None
-        except OSError:
-            pass
-
-    # only do this once every system boot
-    if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
-
-        # this is ugly. under devfs, device
-        # entries in /dev/scsi/.. and /dev/ide/...
-        # don't show up until you attempt to read
-        # from the associated device at /dev (/dev/sda).
-        # so, lets run sfdisk -l (list partitions) against
-        # most possible block devices, that way they show
-        # up when it comes time to do the install.
-        devicenames = valid_blk_names.keys()
-        devicenames.sort()
-        for devicename in devicenames:
-            os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
-
-        # touch file
-        fb = open(DEVICES_SCANNED_FLAG,"w")
-        fb.close()
-
-    devicelist= {}
-
-    partitions_file= file(PROC_PARTITIONS_PATH,"r")
-    line_count= 0
-    for line in partitions_file:
-        line_count= line_count + 1
-
-        # skip the first two lines always
-        if line_count < 2:
-            continue
-
-        parts= string.split(line)
-
-        if len(parts) < 4:
-            continue
-
-        device= parts[3]
-
-        # skip and ignore any partitions
-        if not valid_blk_names.has_key(device):
-            continue
-
-        try:
-            major= int(parts[0])
-            minor= int(parts[1])
-            blocks= int(parts[2])
-        except ValueError, err:
-            continue
-
-        gb_size= blocks/BLOCKS_PER_GB
-
-        # check to see if the blk device is readonly
-        try:
-            # can we write to it?
-            dev_name= "/dev/%s" % device
-            fb = open(dev_name,"w")
-            fb.close()
-            readonly=False
-        except IOError, e:
-            # check if EROFS errno
-            if errno.errorcode.get(e.errno,None) == 'EROFS':
-                readonly=True
-            else:
-                # got some other errno, pretend device is readonly
-                readonly=True
-
-        devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
-
-    return devicelist
-
-
-def get_system_modules( vars = {}, log = sys.stderr):
-    """
-    Return a list of kernel modules that this system requires.
-    This requires access to the installed system's root
-    directory, as the following file must exist and is used:
-    <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
-
-    If there are more than one kernels installed, and the kernel
-    version is not specified, then only the first one in
-    /lib/modules is used.
-
-    Returns a dictionary, keys being the type of module:
-        - scsi       MODULE_CLASS_SCSI
-        - network    MODULE_CLASS_NETWORK
-    The value being the kernel module name to load.
-
-    Some sata devices show up under an IDE device class,
-    hence the reason for checking for ide devices as well.
-    If there actually is a match in the pci -> module lookup
-    table, and its an ide device, its most likely sata,
-    as ide modules are built in to the kernel.
-    """
-
-    if not vars.has_key("SYSIMG_PATH"):
-        vars["SYSIMG_PATH"]="/"
-    SYSIMG_PATH=vars["SYSIMG_PATH"]
-
-    if not vars.has_key("NODE_MODEL_OPTIONS"):
-        vars["NODE_MODEL_OPTIONS"] = 0;
-
-    initrd, kernel_version = getKernelVersion(vars, log)
-
-    # get the kernel version we are assuming
-    if kernel_version is None:
-        try:
-            kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
-        except OSError, e:
-            return
-
-        if len(kernel_version) == 0:
-            return
-
-        if len(kernel_version) > 1:
-            print( "WARNING: We may be returning modules for the wrong kernel." )
-
-        kernel_version= kernel_version[0]
-
-    print( "Using kernel version %s" % kernel_version )
-
-    # test to make sure the file we need is present
-    modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
-                          (SYSIMG_PATH,kernel_version)
-    if not os.access(modules_pcimap_path,os.R_OK):
-        print( "Unable to read %s" % path )
-        return
-
-    pcimap = pypcimap.PCIMap(modules_pcimap_path)
-
-    # this is the actual data structure we return
-    system_mods= {}
-
-    # these are the lists that will be in system_mods
-    network_mods= []
-    scsi_mods= []
-
-    # XXX: this is really similar to what BootCD/conf_files/pl_hwinit does. merge?
-    pcidevs = pypciscan.get_devices()
-
-    for (slot, dev) in pcidevs.iteritems():
-        base = (dev[4] & 0xff0000) >> 16
-        if base not in (PCI_BASE_CLASS_STORAGE,
-                        PCI_BASE_CLASS_NETWORK):
-            continue
-
-        modules = pcimap.get(dev)
-        if len(modules) > 0:
-            if base == PCI_BASE_CLASS_NETWORK:
-                network_mods += modules
-            elif base == PCI_BASE_CLASS_STORAGE:
-                scsi_mods += modules
-
-    system_mods[MODULE_CLASS_SCSI]= scsi_mods
-    system_mods[MODULE_CLASS_NETWORK]= network_mods
-
-    return system_mods
-
-
-def getKernelVersion( vars = {} , log = sys.stderr):
-    # make sure we have the variables we need
-    try:
-        SYSIMG_PATH= vars["SYSIMG_PATH"]
-        if SYSIMG_PATH == "":
-            raise ValueError, "SYSIMG_PATH"
-
-        NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
-    except KeyError, var:
-        raise BootManagerException, "Missing variable in vars: %s\n" % var
-    except ValueError, var:
-        raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
-
-    option = ''
-    if NODE_MODEL_OPTIONS & ModelOptions.SMP:
-        option = 'smp'
-        try:
-            os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
-            os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
-        except OSError, e:
-            # smp kernel is not there; remove option from modeloptions
-            # such that the rest of the code base thinks we are just
-            # using the base kernel.
-            NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
-            vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
-            log.write( "WARNING: Couldn't locate smp kernel.\n")
-            option = ''
-    try:
-        initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
-        kernel_version= initrd.replace("initrd-", "").replace(".img", "")    
-    except OSError, e:
-        initrd = None
-        kernel_version = None
-        
-    return (initrd, kernel_version)
-
-
-if __name__ == "__main__":
-    devices= get_block_device_list()
-    print "block devices detected:"
-    if not devices:
-        print "no devices found!"
-    else:
-        for dev in devices.keys():
-            print "%s %s" % (dev, repr(devices[dev]))
-            
-
-    print ""
-    memory= get_total_phsyical_mem()
-    if not memory:
-        print "unable to read /proc/meminfo for memory"
-    else:
-        print "total physical memory: %d kb" % memory
-        
-
-    print ""
-
-    kernel_version = None
-    if len(sys.argv) > 2:
-        kernel_version = sys.argv[1]
-        
-    modules= get_system_modules()
-    if not modules:
-        print "unable to list system modules"
-    else:
-        for type in modules:
-            if type == MODULE_CLASS_SCSI:
-                print( "all scsi modules:" )
-                for a_mod in modules[type]:
-                    print a_mod
-            elif type == MODULE_CLASS_NETWORK:
-                print( "all network modules:" )
-                for a_mod in modules[type]:
-                    print a_mod
-                
+try:
+       from systeminfo_34 import *
+except:
+       from systeminfo_30 import *
diff --git a/source/systeminfo_30.py b/source/systeminfo_30.py
new file mode 100755 (executable)
index 0000000..2f0ade6
--- /dev/null
@@ -0,0 +1,473 @@
+#!/usr/bin/python2
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+# expected /proc/partitions format
+
+
+#----------------------------------------------------
+#major minor  #blocks  name
+#
+#3     0   40017915 hda
+#3     1     208813 hda1
+#3     2   20482875 hda2
+#3     3     522112 hda3
+#3     4   18804082 hda4
+#----------------------------------------------------
+
+
+import string
+import sys
+import os
+import popen2
+import merge_hw_tables
+import re
+import errno
+import ModelOptions
+from Exceptions import *
+
+hwdatapath = "usr/share/hwdata"
+"""
+a utility class for finding and returning information about
+block devices, memory, and other hardware on the system
+"""
+
+PROC_MEMINFO_PATH= "/proc/meminfo"
+PROC_PARTITIONS_PATH= "/proc/partitions"
+
+# set when the sfdisk -l <dev> trick has been done to make
+# all devices show up
+DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
+
+# a /proc/partitions block is 1024 bytes
+# a GB to a HDD manufacturer is 10^9 bytes
+BLOCKS_PER_GB = pow(10, 9) / 1024.0;
+
+
+# -n is numeric ids (no lookup), -m is machine readable
+LSPCI_CMD= "/sbin/lspci -nm"
+
+MODULE_CLASS_NETWORK= "network"
+MODULE_CLASS_SCSI= "scsi"
+
+PCI_CLASS_NETWORK_ETHERNET=0x0200L
+PCI_CLASS_STORAGE_SCSI=0x0100L
+PCI_CLASS_STORAGE_SATA=0x0106L
+PCI_CLASS_STORAGE_IDE=0x0101L
+PCI_CLASS_STORAGE_FLOPPY=0x0102L
+PCI_CLASS_STORAGE_IPI=0x0103L
+PCI_CLASS_STORAGE_RAID=0x0104L
+PCI_CLASS_STORAGE_OTHER=0x0180L
+
+PCI_ANY=0xffffffffL
+
+def get_total_phsyical_mem(vars = {}, log = sys.stderr):
+    """
+    return the total physical memory of the machine, in kilobytes.
+
+    Return None if /proc/meminfo not readable.
+    """
+
+    try:
+        meminfo_file= file(PROC_MEMINFO_PATH,"r")
+    except IOError, e:
+        return
+
+    total_memory= None
+
+    for line in meminfo_file:
+
+        try:
+            (fieldname,value)= string.split(line,":")
+        except ValueError, e:
+            # this will happen for lines that don't have two values
+            # (like the first line on 2.4 kernels)
+            continue
+
+        fieldname= string.strip(fieldname)
+        value= string.strip(value)
+        
+        if fieldname == "MemTotal":
+            try:
+                (total_memory,units)= string.split(value)
+            except ValueError, e:
+                return
+
+            if total_memory == "" or total_memory == None or \
+                   units == "" or units == None:
+                return
+
+            if string.lower(units) != "kb":
+                return
+
+            try:
+                total_memory= int(total_memory)
+            except ValueError, e:
+                return
+
+            break
+
+    meminfo_file.close()
+    return total_memory
+
+def get_block_device_list(vars = {}, log = sys.stderr):
+    """
+    get a list of block devices from this system.
+    return an associative array, where the device name
+    (full /dev/device path) is the key, and the value
+    is a tuple of (major,minor,numblocks,gb_size,readonly)
+    """
+
+    # make sure we can access to the files/directories in /proc
+    if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
+        return None
+
+    # table with valid scsi/sata/ide/raid block device names
+    valid_blk_names = {}
+    # add in valid sd and hd block device names
+    for blk_prefix in ('sd','hd'):
+        for blk_num in map ( \
+            lambda x: chr(x), range(ord('a'),ord('z')+1)):
+            devicename="%s%c" % (blk_prefix, blk_num)
+            valid_blk_names[devicename]=None
+
+    # add in valid scsi raid block device names
+    for M in range(0,1+1):
+        for N in range(0,7+1):
+            devicename = "cciss/c%dd%d" % (M,N)
+            valid_blk_names[devicename]=None
+
+    for devicename in valid_blk_names.keys():
+        # devfs under 2.4 (old boot cds) used to list partitions
+        # in a format such as scsi/host0/bus0/target0/lun0/disc
+        # and /dev/sda, etc. were just symlinks
+        try:
+            devfsname= os.readlink( "/dev/%s" % devicename )
+            valid_blk_names[devfsname]=None
+        except OSError:
+            pass
+
+    # only do this once every system boot
+    if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
+
+        # this is ugly. under devfs, device
+        # entries in /dev/scsi/.. and /dev/ide/...
+        # don't show up until you attempt to read
+        # from the associated device at /dev (/dev/sda).
+        # so, lets run sfdisk -l (list partitions) against
+        # most possible block devices, that way they show
+        # up when it comes time to do the install.
+        devicenames = valid_blk_names.keys()
+        devicenames.sort()
+        for devicename in devicenames:
+            os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
+
+        # touch file
+        fb = open(DEVICES_SCANNED_FLAG,"w")
+        fb.close()
+
+    devicelist= {}
+
+    partitions_file= file(PROC_PARTITIONS_PATH,"r")
+    line_count= 0
+    for line in partitions_file:
+        line_count= line_count + 1
+
+        # skip the first two lines always
+        if line_count < 2:
+            continue
+
+        parts= string.split(line)
+
+        if len(parts) < 4:
+            continue
+
+        device= parts[3]
+
+        # skip and ignore any partitions
+        if not valid_blk_names.has_key(device):
+            continue
+
+        try:
+            major= int(parts[0])
+            minor= int(parts[1])
+            blocks= int(parts[2])
+        except ValueError, err:
+            continue
+
+        gb_size= blocks/BLOCKS_PER_GB
+
+        # check to see if the blk device is readonly
+        try:
+            # can we write to it?
+            dev_name= "/dev/%s" % device
+            fb = open(dev_name,"w")
+            fb.close()
+            readonly=False
+        except IOError, e:
+            # check if EROFS errno
+            if errno.errorcode.get(e.errno,None) == 'EROFS':
+                readonly=True
+            else:
+                # got some other errno, pretend device is readonly
+                readonly=True
+
+        devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
+
+    return devicelist
+
+
+def get_system_modules( vars = {}, log = sys.stderr):
+    """
+    Return a list of kernel modules that this system requires.
+    This requires access to the installed system's root
+    directory, as the following files must exist and are used:
+    <install_root>/usr/share/hwdata/pcitable
+    <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
+    <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
+
+    If there are more than one kernels installed, and the kernel
+    version is not specified, then only the first one in
+    /lib/modules is used.
+
+    Returns a dictionary, keys being the type of module:
+        - scsi       MODULE_CLASS_SCSI
+        - network    MODULE_CLASS_NETWORK
+    The value being the kernel module name to load.
+
+    Some sata devices show up under an IDE device class,
+    hence the reason for checking for ide devices as well.
+    If there actually is a match in the pci -> module lookup
+    table, and its an ide device, its most likely sata,
+    as ide modules are built in to the kernel.
+    """
+
+    if not vars.has_key("SYSIMG_PATH"):
+        vars["SYSIMG_PATH"]="/"
+    SYSIMG_PATH=vars["SYSIMG_PATH"]
+
+    if not vars.has_key("NODE_MODEL_OPTIONS"):
+        vars["NODE_MODEL_OPTIONS"] = 0;
+
+    initrd, kernel_version = getKernelVersion(vars, log)
+
+    # get the kernel version we are assuming
+    if kernel_version is None:
+        try:
+            kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
+        except OSError, e:
+            return
+
+        if len(kernel_version) == 0:
+            return
+
+        if len(kernel_version) > 1:
+            print( "WARNING: We may be returning modules for the wrong kernel." )
+
+        kernel_version= kernel_version[0]
+
+    print( "Using kernel version %s" % kernel_version )
+
+    # test to make sure the three files we need are present
+    pcitable_path = "%s/%s/pcitable" % (SYSIMG_PATH,hwdatapath)
+    modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
+                          (SYSIMG_PATH,kernel_version)
+    modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
+                       (SYSIMG_PATH,kernel_version)
+
+    for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
+        if not os.access(path,os.R_OK):
+            print( "Unable to read %s" % path )
+            return
+
+    # now, with those three files, merge them all into one easy to
+    # use lookup table
+    (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
+                                                              modules_pcimap_path,
+                                                              pcitable_path )
+    if all_modules is None:
+        print( "Unable to merge pci id tables." )
+        return
+
+    # this is the actual data structure we return
+    system_mods= {}
+
+    # these are the lists that will be in system_mods
+    network_mods= []
+    scsi_mods= []
+
+
+    # get all the system devices from lspci
+    lspci_prog= popen2.Popen3( LSPCI_CMD, 1 )
+    if lspci_prog is None:
+        print( "Unable to run %s with popen2.Popen3" % LSPCI_CMD )
+        return
+
+    returncode= lspci_prog.wait()
+    if returncode != 0:
+        print( "Running %s failed" % LSPCI_CMD )
+        return
+    else:
+        print( "Successfully ran %s" % LSPCI_CMD )
+
+    # for every lspci line, parse in the four tuple PCI id and the
+    # search for the corresponding driver from the dictionary
+    # generated by merge_hw_tables
+    for line in lspci_prog.fromchild:
+        # A sample line:
+        #
+        # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
+        #
+        # Remove '"', 'Class ', and anything beginning with '-'
+        # (usually revisions and prog-if flags) so that we can
+        # split on whitespace:
+        #
+        # 00:1f.1 0101 8086 2411 8086 2411
+        #
+        line = line.strip()
+        line = line.replace('"', '')
+        line = line.replace('Class ', '')
+        line = re.sub('-[^ ]*', '', line)
+
+        parts = line.split()
+        try:
+            if len(parts) < 4:
+                raise
+            classid = long(parts[1], 16)
+            vendorid = long(parts[2], 16)
+            deviceid = long(parts[3], 16)
+        except:
+            print "Invalid line:", line
+            continue
+
+        if classid not in (PCI_CLASS_NETWORK_ETHERNET,
+                           PCI_CLASS_STORAGE_SCSI,
+                           PCI_CLASS_STORAGE_SATA,
+                           PCI_CLASS_STORAGE_RAID,
+                           PCI_CLASS_STORAGE_OTHER,
+                           PCI_CLASS_STORAGE_IDE):
+            continue
+
+        # Device may have a subvendorid and subdeviceid
+        try:
+            subvendorid = long(parts[4], 16)
+            subdeviceid = long(parts[5], 16)
+        except:
+            subvendorid = PCI_ANY
+            subdeviceid = PCI_ANY
+
+        # search for driver that most closely matches the full_id
+        # to drivers that can handle any subvendor/subdevice
+        # version of the hardware.
+        full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
+                    (vendorid,deviceid,subvendorid,PCI_ANY),
+                    (vendorid,deviceid,PCI_ANY,PCI_ANY))
+
+        for full_id in full_ids:
+            module = all_pci_ids.get(full_id, None)
+            if module is not None:
+                if classid == PCI_CLASS_NETWORK_ETHERNET:
+                    network_mods.append(module[0])
+                elif classid in (PCI_CLASS_STORAGE_SCSI,
+                                 PCI_CLASS_STORAGE_SATA,
+                                 PCI_CLASS_STORAGE_RAID,
+                                 PCI_CLASS_STORAGE_OTHER,
+                                 PCI_CLASS_STORAGE_IDE):
+                    scsi_mods.append(module[0])
+
+                    # XXX ata_piix and ahci both claim 8086:2652 and 8086:2653,
+                    # and it is usually a non-visible BIOS option that decides
+                    # which is appropriate. Just load both.
+                    if vendorid == 0x8086 and (deviceid == 0x2652 or deviceid == 0x2653):
+                        if module[0] == "ahci":
+                            scsi_mods.append("ata_piix")
+                        elif module[0] == "ata_piix":
+                            scsi_mods.append("ahci")
+                else:
+                    print "not network or scsi: 0x%x" % classid
+                break
+
+    system_mods[MODULE_CLASS_SCSI]= scsi_mods
+    system_mods[MODULE_CLASS_NETWORK]= network_mods
+
+    return system_mods
+
+
+def getKernelVersion( vars = {} , log = sys.stderr):
+    # make sure we have the variables we need
+    try:
+        SYSIMG_PATH= vars["SYSIMG_PATH"]
+        if SYSIMG_PATH == "":
+            raise ValueError, "SYSIMG_PATH"
+
+        NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
+    except KeyError, var:
+        raise BootManagerException, "Missing variable in vars: %s\n" % var
+    except ValueError, var:
+        raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
+
+    option = ''
+    if NODE_MODEL_OPTIONS & ModelOptions.SMP:
+        option = 'smp'
+        try:
+            os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
+            os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
+        except OSError, e:
+            # smp kernel is not there; remove option from modeloptions
+            # such that the rest of the code base thinks we are just
+            # using the base kernel.
+            NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
+            vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
+            log.write( "WARNING: Couldn't locate smp kernel.\n")
+            option = ''
+    try:
+        initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
+        kernel_version= initrd.replace("initrd-", "").replace(".img", "")    
+    except OSError, e:
+        initrd = None
+        kernel_version = None
+        
+    return (initrd, kernel_version)
+
+
+if __name__ == "__main__":
+    devices= get_block_device_list()
+    print "block devices detected:"
+    if not devices:
+        print "no devices found!"
+    else:
+        for dev in devices.keys():
+            print "%s %s" % (dev, repr(devices[dev]))
+            
+
+    print ""
+    memory= get_total_phsyical_mem()
+    if not memory:
+        print "unable to read /proc/meminfo for memory"
+    else:
+        print "total physical memory: %d kb" % memory
+        
+
+    print ""
+
+    kernel_version = None
+    if len(sys.argv) > 2:
+        kernel_version = sys.argv[1]
+        
+    modules= get_system_modules()
+    if not modules:
+        print "unable to list system modules"
+    else:
+        for type in modules:
+            if type == MODULE_CLASS_SCSI:
+                print( "all scsi modules:" )
+                for a_mod in modules[type]:
+                    print a_mod
+            elif type == MODULE_CLASS_NETWORK:
+                print( "all network modules:" )
+                for a_mod in modules[type]:
+                    print a_mod
+                
diff --git a/source/systeminfo_34.py b/source/systeminfo_34.py
new file mode 100755 (executable)
index 0000000..31f8bb2
--- /dev/null
@@ -0,0 +1,378 @@
+#!/usr/bin/python2
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+# expected /proc/partitions format
+
+
+#----------------------------------------------------
+#major minor  #blocks  name
+#
+#3     0   40017915 hda
+#3     1     208813 hda1
+#3     2   20482875 hda2
+#3     3     522112 hda3
+#3     4   18804082 hda4
+#----------------------------------------------------
+
+
+import string
+import sys
+import os
+import popen2
+import re
+import errno
+import ModelOptions
+import pypciscan
+import pypcimap
+from Exceptions import *
+
+"""
+a utility class for finding and returning information about
+block devices, memory, and other hardware on the system
+"""
+
+PROC_MEMINFO_PATH= "/proc/meminfo"
+PROC_PARTITIONS_PATH= "/proc/partitions"
+
+# set when the sfdisk -l <dev> trick has been done to make
+# all devices show up
+DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
+
+# a /proc/partitions block is 1024 bytes
+# a GB to a HDD manufacturer is 10^9 bytes
+BLOCKS_PER_GB = pow(10, 9) / 1024.0;
+
+
+# -n is numeric ids (no lookup), -m is machine readable
+LSPCI_CMD= "/sbin/lspci -nm"
+
+MODULE_CLASS_NETWORK= "network"
+MODULE_CLASS_SCSI= "scsi"
+
+PCI_BASE_CLASS_NETWORK=0x02L
+PCI_BASE_CLASS_STORAGE=0x01L
+
+PCI_ANY=0xffffffffL
+
+def get_total_phsyical_mem(vars = {}, log = sys.stderr):
+    """
+    return the total physical memory of the machine, in kilobytes.
+
+    Return None if /proc/meminfo not readable.
+    """
+
+    try:
+        meminfo_file= file(PROC_MEMINFO_PATH,"r")
+    except IOError, e:
+        return
+
+    total_memory= None
+
+    for line in meminfo_file:
+
+        try:
+            (fieldname,value)= string.split(line,":")
+        except ValueError, e:
+            # this will happen for lines that don't have two values
+            # (like the first line on 2.4 kernels)
+            continue
+
+        fieldname= string.strip(fieldname)
+        value= string.strip(value)
+        
+        if fieldname == "MemTotal":
+            try:
+                (total_memory,units)= string.split(value)
+            except ValueError, e:
+                return
+
+            if total_memory == "" or total_memory == None or \
+                   units == "" or units == None:
+                return
+
+            if string.lower(units) != "kb":
+                return
+
+            try:
+                total_memory= int(total_memory)
+            except ValueError, e:
+                return
+
+            break
+
+    meminfo_file.close()
+    return total_memory
+
+def get_block_device_list(vars = {}, log = sys.stderr):
+    """
+    get a list of block devices from this system.
+    return an associative array, where the device name
+    (full /dev/device path) is the key, and the value
+    is a tuple of (major,minor,numblocks,gb_size,readonly)
+    """
+
+    # make sure we can access to the files/directories in /proc
+    if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
+        return None
+
+    # table with valid scsi/sata/ide/raid block device names
+    valid_blk_names = {}
+    # add in valid sd and hd block device names
+    for blk_prefix in ('sd','hd'):
+        for blk_num in map ( \
+            lambda x: chr(x), range(ord('a'),ord('z')+1)):
+            devicename="%s%c" % (blk_prefix, blk_num)
+            valid_blk_names[devicename]=None
+
+    # add in valid scsi raid block device names
+    for M in range(0,1+1):
+        for N in range(0,7+1):
+            devicename = "cciss/c%dd%d" % (M,N)
+            valid_blk_names[devicename]=None
+
+    for devicename in valid_blk_names.keys():
+        # devfs under 2.4 (old boot cds) used to list partitions
+        # in a format such as scsi/host0/bus0/target0/lun0/disc
+        # and /dev/sda, etc. were just symlinks
+        try:
+            devfsname= os.readlink( "/dev/%s" % devicename )
+            valid_blk_names[devfsname]=None
+        except OSError:
+            pass
+
+    # only do this once every system boot
+    if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
+
+        # this is ugly. under devfs, device
+        # entries in /dev/scsi/.. and /dev/ide/...
+        # don't show up until you attempt to read
+        # from the associated device at /dev (/dev/sda).
+        # so, lets run sfdisk -l (list partitions) against
+        # most possible block devices, that way they show
+        # up when it comes time to do the install.
+        devicenames = valid_blk_names.keys()
+        devicenames.sort()
+        for devicename in devicenames:
+            os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
+
+        # touch file
+        fb = open(DEVICES_SCANNED_FLAG,"w")
+        fb.close()
+
+    devicelist= {}
+
+    partitions_file= file(PROC_PARTITIONS_PATH,"r")
+    line_count= 0
+    for line in partitions_file:
+        line_count= line_count + 1
+
+        # skip the first two lines always
+        if line_count < 2:
+            continue
+
+        parts= string.split(line)
+
+        if len(parts) < 4:
+            continue
+
+        device= parts[3]
+
+        # skip and ignore any partitions
+        if not valid_blk_names.has_key(device):
+            continue
+
+        try:
+            major= int(parts[0])
+            minor= int(parts[1])
+            blocks= int(parts[2])
+        except ValueError, err:
+            continue
+
+        gb_size= blocks/BLOCKS_PER_GB
+
+        # check to see if the blk device is readonly
+        try:
+            # can we write to it?
+            dev_name= "/dev/%s" % device
+            fb = open(dev_name,"w")
+            fb.close()
+            readonly=False
+        except IOError, e:
+            # check if EROFS errno
+            if errno.errorcode.get(e.errno,None) == 'EROFS':
+                readonly=True
+            else:
+                # got some other errno, pretend device is readonly
+                readonly=True
+
+        devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
+
+    return devicelist
+
+
+def get_system_modules( vars = {}, log = sys.stderr):
+    """
+    Return a list of kernel modules that this system requires.
+    This requires access to the installed system's root
+    directory, as the following file must exist and is used:
+    <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
+
+    If there are more than one kernels installed, and the kernel
+    version is not specified, then only the first one in
+    /lib/modules is used.
+
+    Returns a dictionary, keys being the type of module:
+        - scsi       MODULE_CLASS_SCSI
+        - network    MODULE_CLASS_NETWORK
+    The value being the kernel module name to load.
+
+    Some sata devices show up under an IDE device class,
+    hence the reason for checking for ide devices as well.
+    If there actually is a match in the pci -> module lookup
+    table, and its an ide device, its most likely sata,
+    as ide modules are built in to the kernel.
+    """
+
+    if not vars.has_key("SYSIMG_PATH"):
+        vars["SYSIMG_PATH"]="/"
+    SYSIMG_PATH=vars["SYSIMG_PATH"]
+
+    if not vars.has_key("NODE_MODEL_OPTIONS"):
+        vars["NODE_MODEL_OPTIONS"] = 0;
+
+    initrd, kernel_version = getKernelVersion(vars, log)
+
+    # get the kernel version we are assuming
+    if kernel_version is None:
+        try:
+            kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
+        except OSError, e:
+            return
+
+        if len(kernel_version) == 0:
+            return
+
+        if len(kernel_version) > 1:
+            print( "WARNING: We may be returning modules for the wrong kernel." )
+
+        kernel_version= kernel_version[0]
+
+    print( "Using kernel version %s" % kernel_version )
+
+    # test to make sure the file we need is present
+    modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
+                          (SYSIMG_PATH,kernel_version)
+    if not os.access(modules_pcimap_path,os.R_OK):
+        print( "Unable to read %s" % path )
+        return
+
+    pcimap = pypcimap.PCIMap(modules_pcimap_path)
+
+    # this is the actual data structure we return
+    system_mods= {}
+
+    # these are the lists that will be in system_mods
+    network_mods= []
+    scsi_mods= []
+
+    # XXX: this is really similar to what BootCD/conf_files/pl_hwinit does. merge?
+    pcidevs = pypciscan.get_devices()
+
+    for (slot, dev) in pcidevs.iteritems():
+        base = (dev[4] & 0xff0000) >> 16
+        if base not in (PCI_BASE_CLASS_STORAGE,
+                        PCI_BASE_CLASS_NETWORK):
+            continue
+
+        modules = pcimap.get(dev)
+        if len(modules) > 0:
+            if base == PCI_BASE_CLASS_NETWORK:
+                network_mods += modules
+            elif base == PCI_BASE_CLASS_STORAGE:
+                scsi_mods += modules
+
+    system_mods[MODULE_CLASS_SCSI]= scsi_mods
+    system_mods[MODULE_CLASS_NETWORK]= network_mods
+
+    return system_mods
+
+
+def getKernelVersion( vars = {} , log = sys.stderr):
+    # make sure we have the variables we need
+    try:
+        SYSIMG_PATH= vars["SYSIMG_PATH"]
+        if SYSIMG_PATH == "":
+            raise ValueError, "SYSIMG_PATH"
+
+        NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
+    except KeyError, var:
+        raise BootManagerException, "Missing variable in vars: %s\n" % var
+    except ValueError, var:
+        raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
+
+    option = ''
+    if NODE_MODEL_OPTIONS & ModelOptions.SMP:
+        option = 'smp'
+        try:
+            os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
+            os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
+        except OSError, e:
+            # smp kernel is not there; remove option from modeloptions
+            # such that the rest of the code base thinks we are just
+            # using the base kernel.
+            NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
+            vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
+            log.write( "WARNING: Couldn't locate smp kernel.\n")
+            option = ''
+    try:
+        initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
+        kernel_version= initrd.replace("initrd-", "").replace(".img", "")    
+    except OSError, e:
+        initrd = None
+        kernel_version = None
+        
+    return (initrd, kernel_version)
+
+
+if __name__ == "__main__":
+    devices= get_block_device_list()
+    print "block devices detected:"
+    if not devices:
+        print "no devices found!"
+    else:
+        for dev in devices.keys():
+            print "%s %s" % (dev, repr(devices[dev]))
+            
+
+    print ""
+    memory= get_total_phsyical_mem()
+    if not memory:
+        print "unable to read /proc/meminfo for memory"
+    else:
+        print "total physical memory: %d kb" % memory
+        
+
+    print ""
+
+    kernel_version = None
+    if len(sys.argv) > 2:
+        kernel_version = sys.argv[1]
+        
+    modules= get_system_modules()
+    if not modules:
+        print "unable to list system modules"
+    else:
+        for type in modules:
+            if type == MODULE_CLASS_SCSI:
+                print( "all scsi modules:" )
+                for a_mod in modules[type]:
+                    print a_mod
+            elif type == MODULE_CLASS_NETWORK:
+                print( "all network modules:" )
+                for a_mod in modules[type]:
+                    print a_mod
+