Fix for adding new disks to system:
[bootmanager.git] / source / systeminfo.py
index 8170ba2..43c70c8 100755 (executable)
 import string
 import os
 import popen2
-from merge_hw_tables import merge_hw_tables
-
+import merge_hw_tables
+import re
+import errno
 
+hwdatapath = "usr/share/hwdata"
 class systeminfo:
     """
     a utility class for finding and returning information about
@@ -93,10 +95,15 @@ class systeminfo:
     MODULE_CLASS_NETWORK= "network"
     MODULE_CLASS_SCSI= "scsi"
 
-    PCI_CLASS_NETWORK= "0200"
-    PCI_CLASS_RAID= "0104"
-    PCI_CLASS_RAID2= "0100"
-    PCI_CLASS_IDE= "0101"
+    PCI_CLASS_NETWORK_ETHERNET=0x0200L
+    PCI_CLASS_STORAGE_SCSI=0x0100L
+    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(self):
         """
@@ -162,6 +169,20 @@ class systeminfo:
         if not os.access(self.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
 
         # only do this once every system boot
         if not os.access(self.DEVICES_SCANNED_FLAG, os.R_OK):
@@ -173,41 +194,19 @@ class systeminfo:
             # 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.
-            for dev_prefix in ('sd','hd'):
-                block_dev_num= 0
-                while block_dev_num < 10:
-                    if block_dev_num < 26:
-                        devicename= "/dev/%s%c" % \
-                                    (dev_prefix, chr(ord('a')+block_dev_num))
-                    else:
-                        devicename= "/dev/%s%c%c" % \
-                                    ( dev_prefix,
-                                      chr(ord('a')+((block_dev_num/26)-1)),
-                                      chr(ord('a')+(block_dev_num%26)) )
-
-                    os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
-                    block_dev_num = block_dev_num + 1
-
-            additional_scan_devices= ("/dev/cciss/c0d0p", "/dev/cciss/c0d1p",
-                                      "/dev/cciss/c0d2p", "/dev/cciss/c0d3p",
-                                      "/dev/cciss/c0d4p", "/dev/cciss/c0d5p",
-                                      "/dev/cciss/c0d6p", "/dev/cciss/c0d7p",
-                                      "/dev/cciss/c1d0p", "/dev/cciss/c1d1p",
-                                      "/dev/cciss/c1d2p", "/dev/cciss/c1d3p",
-                                      "/dev/cciss/c1d4p", "/dev/cciss/c1d5p",
-                                      "/dev/cciss/c1d6p", "/dev/cciss/c1d7p",)
-            
-            for devicename in additional_scan_devices:
-                os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
+            devicenames = valid_blk_names.keys()
+            devicenames.sort()
+            for devicename in devicenames:
+                os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
 
-            os.system( "touch %s" % self.DEVICES_SCANNED_FLAG )
-            
+            # touch file
+            fb = open(self.DEVICES_SCANNED_FLAG,"w")
+            fb.close()
 
         devicelist= {}
 
         partitions_file= file(self.PROC_PARTITIONS_PATH,"r")
         line_count= 0
-        
         for line in partitions_file:
             line_count= line_count + 1
 
@@ -221,15 +220,11 @@ class systeminfo:
                 continue
             
             device= parts[3]
-
-            # if the last char in device is a number, its
-            # a partition, and we ignore it
             
-            if device[len(device)-1].isdigit():
+            # skip and ignore any partitions
+            if not valid_blk_names.has_key(device):
                 continue
 
-            dev_name= "/dev/%s" % device
-            
             try:
                 major= int(parts[0])
                 minor= int(parts[1])
@@ -239,52 +234,23 @@ class systeminfo:
 
             gb_size= blocks/self.BLOCKS_PER_GB
             
-            # parse the output of hdparm <disk> to get the readonly flag;
-            # if this fails, assume it isn't read only
-            readonly= 0
-            
-            hdparm_cmd = popen2.Popen3("hdparm %s 2> /dev/null" % dev_name)
-            status= hdparm_cmd.wait()
-
-            if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
-                try:
-                    hdparm_output= hdparm_cmd.fromchild.read()
-                    hdparm_cmd= None
-                    
-                    # parse the output of hdparm, the lines we are interested
-                    # in look
-                    # like:
-                    #
-                    #  readonly     =  0 (off)
-                    #
-                    
-                    for line in string.split(hdparm_output,"\n"):
-                        
-                        line= string.strip(line)
-                        if line == "":
-                            continue
-                        
-                        line_parts= string.split(line,"=")
-                        if len(line_parts) < 2:
-                            continue
-
-                        name= string.strip(line_parts[0])
-
-                        if name == "readonly":
-                            value= string.strip(line_parts[1])
-                            if len(value) == 0:
-                                break
-
-                            if value[0] == "1":
-                                readonly= 1
-                                break
-
-                except IOError:
-                    pass
+            # 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
 
 
@@ -332,7 +298,7 @@ class systeminfo:
         print( "Using kernel version %s" % kernel_version )
 
         # test to make sure the three files we need are present
-        pcitable_path = "%s/usr/share/hwdata/pcitable" % install_root
+        pcitable_path = "%s/%s/pcitable" % (install_root,hwdatapath)
         modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
                               (install_root,kernel_version)
         modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
@@ -345,15 +311,13 @@ class systeminfo:
 
         # now, with those three files, merge them all into one easy to
         # use lookup table
-        all_modules= merge_hw_tables().merge_files( modules_dep_path,
-                                                    modules_pcimap_path,
-                                                    pcitable_path )
-
+        (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= {}
 
@@ -375,37 +339,72 @@ class systeminfo:
         else:
             print( "Successfully ran %s" % self.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:
-            if string.strip(line) == "":
-                continue
-    
-            parts= string.split(line)
-
+            # 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:
-                classid= self.remove_quotes(parts[2])
-                vendorid= self.remove_quotes(parts[3])
-                deviceid= self.remove_quotes(parts[4])
-            except IndexError:
-                print( "Skipping invalid line:", string.strip(line) )
+                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 (self.PCI_CLASS_NETWORK,
-                               self.PCI_CLASS_RAID,
-                               self.PCI_CLASS_RAID2,
-                               self.PCI_CLASS_IDE):
+            if classid not in (self.PCI_CLASS_NETWORK_ETHERNET,
+                               self.PCI_CLASS_STORAGE_SCSI,
+                               self.PCI_CLASS_STORAGE_RAID,
+                               self.PCI_CLASS_STORAGE_OTHER,
+                               self.PCI_CLASS_STORAGE_IDE):
                 continue
-            
-            full_deviceid= "%s:%s" % (vendorid,deviceid)
-
-            for module in all_modules.keys():
-                if full_deviceid in all_modules[module]:
-                    if classid == self.PCI_CLASS_NETWORK:
-                        network_mods.append(module)
-                    elif classid in (self.PCI_CLASS_RAID,
-                                     self.PCI_CLASS_RAID2,
-                                     self.PCI_CLASS_IDE):
-                        scsi_mods.append(module)
-                        
+
+            # Device may have a subvendorid and subdeviceid
+            try:
+                subvendorid = long(parts[4], 16)
+                subdeviceid = long(parts[5], 16)
+            except:
+                subvendorid = self.PCI_ANY
+                subdeviceid = self.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,self.PCI_ANY),
+                        (vendorid,deviceid,self.PCI_ANY,self.PCI_ANY))
+
+            for full_id in full_ids:
+                module = all_pci_ids.get(full_id, None)
+                if module is not None:
+                    if classid == self.PCI_CLASS_NETWORK_ETHERNET:
+                        network_mods.append(module[0])
+                    elif classid in (self.PCI_CLASS_STORAGE_SCSI,
+                                     self.PCI_CLASS_STORAGE_RAID,
+                                     self.PCI_CLASS_STORAGE_OTHER,
+                                     self.PCI_CLASS_STORAGE_IDE):
+                        scsi_mods.append(module[0])
+                    else:
+                        print "not network or scsi: 0x%x" % classid
+                    break
+
         system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
         system_mods[self.MODULE_CLASS_NETWORK]= network_mods
         
@@ -447,7 +446,13 @@ if __name__ == "__main__":
         
 
     print ""
-    modules= info.get_system_modules("/")
+
+    import sys
+    kernel_version = None
+    if len(sys.argv) > 2:
+        kernel_version = sys.argv[1]
+        
+    modules= info.get_system_modules("/",kernel_version)
     if not modules:
         print "unable to list system modules"
     else: