3 # Copyright (c) 2003 Intel Corporation
6 # Copyright (c) 2004-2006 The Trustees of Princeton University
8 # expected /proc/partitions format
11 #----------------------------------------------------
12 #major minor #blocks name
19 #----------------------------------------------------
26 import merge_hw_tables
30 from Exceptions import *
32 hwdatapath = "usr/share/hwdata"
34 a utility class for finding and returning information about
35 block devices, memory, and other hardware on the system
38 PROC_MEMINFO_PATH= "/proc/meminfo"
39 PROC_PARTITIONS_PATH= "/proc/partitions"
41 # set when the sfdisk -l <dev> trick has been done to make
43 DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
45 # a /proc/partitions block is 1024 bytes
46 # a GB to a HDD manufacturer is 10^9 bytes
47 BLOCKS_PER_GB = pow(10, 9) / 1024.0;
50 # -n is numeric ids (no lookup), -m is machine readable
51 LSPCI_CMD= "/sbin/lspci -nm"
53 MODULE_CLASS_NETWORK= "network"
54 MODULE_CLASS_SCSI= "scsi"
56 PCI_CLASS_NETWORK_ETHERNET=0x0200L
57 PCI_CLASS_STORAGE_SCSI=0x0100L
58 PCI_CLASS_STORAGE_IDE=0x0101L
59 PCI_CLASS_STORAGE_FLOPPY=0x0102L
60 PCI_CLASS_STORAGE_IPI=0x0103L
61 PCI_CLASS_STORAGE_RAID=0x0104L
62 PCI_CLASS_STORAGE_OTHER=0x0180L
66 def get_total_phsyical_mem(vars = {}, log = sys.stderr):
68 return the total physical memory of the machine, in kilobytes.
70 Return None if /proc/meminfo not readable.
74 meminfo_file= file(PROC_MEMINFO_PATH,"r")
80 for line in meminfo_file:
83 (fieldname,value)= string.split(line,":")
85 # this will happen for lines that don't have two values
86 # (like the first line on 2.4 kernels)
89 fieldname= string.strip(fieldname)
90 value= string.strip(value)
92 if fieldname == "MemTotal":
94 (total_memory,units)= string.split(value)
98 if total_memory == "" or total_memory == None or \
99 units == "" or units == None:
102 if string.lower(units) != "kb":
106 total_memory= int(total_memory)
107 except ValueError, e:
115 def get_block_device_list(vars = {}, log = sys.stderr):
117 get a list of block devices from this system.
118 return an associative array, where the device name
119 (full /dev/device path) is the key, and the value
120 is a tuple of (major,minor,numblocks,gb_size,readonly)
123 # make sure we can access to the files/directories in /proc
124 if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
127 # table with valid scsi/sata/ide/raid block device names
129 # add in valid sd and hd block device names
130 for blk_prefix in ('sd','hd'):
131 for blk_num in map ( \
132 lambda x: chr(x), range(ord('a'),ord('z')+1)):
133 devicename="%s%c" % (blk_prefix, blk_num)
134 valid_blk_names[devicename]=None
136 # add in valid scsi raid block device names
137 for M in range(0,1+1):
138 for N in range(0,7+1):
139 devicename = "cciss/c%dd%d" % (M,N)
140 valid_blk_names[devicename]=None
142 # only do this once every system boot
143 if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
145 # this is ugly. under devfs, device
146 # entries in /dev/scsi/.. and /dev/ide/...
147 # don't show up until you attempt to read
148 # from the associated device at /dev (/dev/sda).
149 # so, lets run sfdisk -l (list partitions) against
150 # most possible block devices, that way they show
151 # up when it comes time to do the install.
152 devicenames = valid_blk_names.keys()
154 for devicename in devicenames:
155 os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
158 fb = open(DEVICES_SCANNED_FLAG,"w")
163 partitions_file= file(PROC_PARTITIONS_PATH,"r")
165 for line in partitions_file:
166 line_count= line_count + 1
168 # skip the first two lines always
172 parts= string.split(line)
179 # skip and ignore any partitions
180 if not valid_blk_names.has_key(device):
186 blocks= int(parts[2])
187 except ValueError, err:
190 gb_size= blocks/BLOCKS_PER_GB
192 # check to see if the blk device is readonly
194 # can we write to it?
195 dev_name= "/dev/%s" % device
196 fb = open(dev_name,"w")
200 # check if EROFS errno
201 if errno.errorcode.get(e.errno,None) == 'EROFS':
204 # got some other errno, pretend device is readonly
207 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
212 def get_system_modules( vars = {}, log = sys.stderr):
214 Return a list of kernel modules that this system requires.
215 This requires access to the installed system's root
216 directory, as the following files must exist and are used:
217 <install_root>/usr/share/hwdata/pcitable
218 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
219 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
221 If there are more than one kernels installed, and the kernel
222 version is not specified, then only the first one in
223 /lib/modules is used.
225 Returns a dictionary, keys being the type of module:
226 - scsi MODULE_CLASS_SCSI
227 - network MODULE_CLASS_NETWORK
228 The value being the kernel module name to load.
230 Some sata devices show up under an IDE device class,
231 hence the reason for checking for ide devices as well.
232 If there actually is a match in the pci -> module lookup
233 table, and its an ide device, its most likely sata,
234 as ide modules are built in to the kernel.
237 if not vars.has_key("SYSIMG_PATH"):
238 vars["SYSIMG_PATH"]="/"
239 SYSIMG_PATH=vars["SYSIMG_PATH"]
241 if not vars.has_key("NODE_MODEL_OPTIONS"):
242 vars["NODE_MODEL_OPTIONS"] = 0;
244 initrd, kernel_version = getKernelVersion(vars, log)
246 # get the kernel version we are assuming
247 if kernel_version is None:
249 kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
253 if len(kernel_version) == 0:
256 if len(kernel_version) > 1:
257 print( "WARNING: We may be returning modules for the wrong kernel." )
259 kernel_version= kernel_version[0]
261 print( "Using kernel version %s" % kernel_version )
263 # test to make sure the three files we need are present
264 pcitable_path = "%s/%s/pcitable" % (SYSIMG_PATH,hwdatapath)
265 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
266 (SYSIMG_PATH,kernel_version)
267 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
268 (SYSIMG_PATH,kernel_version)
270 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
271 if not os.access(path,os.R_OK):
272 print( "Unable to read %s" % path )
275 # now, with those three files, merge them all into one easy to
277 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
280 if all_modules is None:
281 print( "Unable to merge pci id tables." )
284 # this is the actual data structure we return
287 # these are the lists that will be in system_mods
292 # get all the system devices from lspci
293 lspci_prog= popen2.Popen3( LSPCI_CMD, 1 )
294 if lspci_prog is None:
295 print( "Unable to run %s with popen2.Popen3" % LSPCI_CMD )
298 returncode= lspci_prog.wait()
300 print( "Running %s failed" % LSPCI_CMD )
303 print( "Successfully ran %s" % LSPCI_CMD )
305 # for every lspci line, parse in the four tuple PCI id and the
306 # search for the corresponding driver from the dictionary
307 # generated by merge_hw_tables
308 for line in lspci_prog.fromchild:
311 # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
313 # Remove '"', 'Class ', and anything beginning with '-'
314 # (usually revisions and prog-if flags) so that we can
315 # split on whitespace:
317 # 00:1f.1 0101 8086 2411 8086 2411
320 line = line.replace('"', '')
321 line = line.replace('Class ', '')
322 line = re.sub('-[^ ]*', '', line)
328 classid = long(parts[1], 16)
329 vendorid = long(parts[2], 16)
330 deviceid = long(parts[3], 16)
332 print "Invalid line:", line
335 if classid not in (PCI_CLASS_NETWORK_ETHERNET,
336 PCI_CLASS_STORAGE_SCSI,
337 PCI_CLASS_STORAGE_RAID,
338 PCI_CLASS_STORAGE_OTHER,
339 PCI_CLASS_STORAGE_IDE):
342 # Device may have a subvendorid and subdeviceid
344 subvendorid = long(parts[4], 16)
345 subdeviceid = long(parts[5], 16)
347 subvendorid = PCI_ANY
348 subdeviceid = PCI_ANY
350 # search for driver that most closely matches the full_id
351 # to drivers that can handle any subvendor/subdevice
352 # version of the hardware.
353 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
354 (vendorid,deviceid,subvendorid,PCI_ANY),
355 (vendorid,deviceid,PCI_ANY,PCI_ANY))
357 for full_id in full_ids:
358 module = all_pci_ids.get(full_id, None)
359 if module is not None:
360 if classid == PCI_CLASS_NETWORK_ETHERNET:
361 network_mods.append(module[0])
362 elif classid in (PCI_CLASS_STORAGE_SCSI,
363 PCI_CLASS_STORAGE_RAID,
364 PCI_CLASS_STORAGE_OTHER,
365 PCI_CLASS_STORAGE_IDE):
366 scsi_mods.append(module[0])
368 print "not network or scsi: 0x%x" % classid
371 system_mods[MODULE_CLASS_SCSI]= scsi_mods
372 system_mods[MODULE_CLASS_NETWORK]= network_mods
377 def getKernelVersion( vars = {} , log = sys.stderr):
378 # make sure we have the variables we need
380 SYSIMG_PATH= vars["SYSIMG_PATH"]
381 if SYSIMG_PATH == "":
382 raise ValueError, "SYSIMG_PATH"
384 NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
385 except KeyError, var:
386 raise BootManagerException, "Missing variable in vars: %s\n" % var
387 except ValueError, var:
388 raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
391 if NODE_MODEL_OPTIONS & ModelOptions.SMP:
394 os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
395 os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
397 # smp kernel is not there; remove option from modeloptions
398 # such that the rest of the code base thinks we are just
399 # using the base kernel.
400 NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
401 vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
402 log.write( "WARNING: Couldn't locate smp kernel.\n")
405 initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
406 kernel_version= initrd.replace("initrd-", "").replace(".img", "")
409 kernel_version = None
411 return (initrd, kernel_version)
414 if __name__ == "__main__":
415 devices= get_block_device_list()
416 print "block devices detected:"
418 print "no devices found!"
420 for dev in devices.keys():
421 print "%s %s" % (dev, repr(devices[dev]))
425 memory= get_total_phsyical_mem()
427 print "unable to read /proc/meminfo for memory"
429 print "total physical memory: %d kb" % memory
434 kernel_version = None
435 if len(sys.argv) > 2:
436 kernel_version = sys.argv[1]
438 modules= get_system_modules()
440 print "unable to list system modules"
443 if type == MODULE_CLASS_SCSI:
444 print( "all scsi modules:" )
445 for a_mod in modules[type]:
447 elif type == MODULE_CLASS_NETWORK:
448 print( "all network modules:" )
449 for a_mod in modules[type]: