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_SATA=0x0106L
59 PCI_CLASS_STORAGE_IDE=0x0101L
60 PCI_CLASS_STORAGE_FLOPPY=0x0102L
61 PCI_CLASS_STORAGE_IPI=0x0103L
62 PCI_CLASS_STORAGE_RAID=0x0104L
63 PCI_CLASS_STORAGE_OTHER=0x0180L
67 def get_total_phsyical_mem(vars = {}, log = sys.stderr):
69 return the total physical memory of the machine, in kilobytes.
71 Return None if /proc/meminfo not readable.
75 meminfo_file= file(PROC_MEMINFO_PATH,"r")
81 for line in meminfo_file:
84 (fieldname,value)= string.split(line,":")
86 # this will happen for lines that don't have two values
87 # (like the first line on 2.4 kernels)
90 fieldname= string.strip(fieldname)
91 value= string.strip(value)
93 if fieldname == "MemTotal":
95 (total_memory,units)= string.split(value)
99 if total_memory == "" or total_memory == None or \
100 units == "" or units == None:
103 if string.lower(units) != "kb":
107 total_memory= int(total_memory)
108 except ValueError, e:
116 def get_block_device_list(vars = {}, log = sys.stderr):
118 get a list of block devices from this system.
119 return an associative array, where the device name
120 (full /dev/device path) is the key, and the value
121 is a tuple of (major,minor,numblocks,gb_size,readonly)
124 # make sure we can access to the files/directories in /proc
125 if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
128 # table with valid scsi/sata/ide/raid block device names
130 # add in valid sd and hd block device names
131 for blk_prefix in ('sd','hd'):
132 for blk_num in map ( \
133 lambda x: chr(x), range(ord('a'),ord('z')+1)):
134 devicename="%s%c" % (blk_prefix, blk_num)
135 valid_blk_names[devicename]=None
137 # add in valid scsi raid block device names
138 for M in range(0,1+1):
139 for N in range(0,7+1):
140 devicename = "cciss/c%dd%d" % (M,N)
141 valid_blk_names[devicename]=None
143 # only do this once every system boot
144 if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
146 # this is ugly. under devfs, device
147 # entries in /dev/scsi/.. and /dev/ide/...
148 # don't show up until you attempt to read
149 # from the associated device at /dev (/dev/sda).
150 # so, lets run sfdisk -l (list partitions) against
151 # most possible block devices, that way they show
152 # up when it comes time to do the install.
153 devicenames = valid_blk_names.keys()
155 for devicename in devicenames:
156 os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
159 fb = open(DEVICES_SCANNED_FLAG,"w")
164 partitions_file= file(PROC_PARTITIONS_PATH,"r")
166 for line in partitions_file:
167 line_count= line_count + 1
169 # skip the first two lines always
173 parts= string.split(line)
180 # skip and ignore any partitions
181 if not valid_blk_names.has_key(device):
187 blocks= int(parts[2])
188 except ValueError, err:
191 gb_size= blocks/BLOCKS_PER_GB
193 # check to see if the blk device is readonly
195 # can we write to it?
196 dev_name= "/dev/%s" % device
197 fb = open(dev_name,"w")
201 # check if EROFS errno
202 if errno.errorcode.get(e.errno,None) == 'EROFS':
205 # got some other errno, pretend device is readonly
208 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
213 def get_system_modules( vars = {}, log = sys.stderr):
215 Return a list of kernel modules that this system requires.
216 This requires access to the installed system's root
217 directory, as the following files must exist and are used:
218 <install_root>/usr/share/hwdata/pcitable
219 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
220 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
222 If there are more than one kernels installed, and the kernel
223 version is not specified, then only the first one in
224 /lib/modules is used.
226 Returns a dictionary, keys being the type of module:
227 - scsi MODULE_CLASS_SCSI
228 - network MODULE_CLASS_NETWORK
229 The value being the kernel module name to load.
231 Some sata devices show up under an IDE device class,
232 hence the reason for checking for ide devices as well.
233 If there actually is a match in the pci -> module lookup
234 table, and its an ide device, its most likely sata,
235 as ide modules are built in to the kernel.
238 if not vars.has_key("SYSIMG_PATH"):
239 vars["SYSIMG_PATH"]="/"
240 SYSIMG_PATH=vars["SYSIMG_PATH"]
242 if not vars.has_key("NODE_MODEL_OPTIONS"):
243 vars["NODE_MODEL_OPTIONS"] = 0;
245 initrd, kernel_version = getKernelVersion(vars, log)
247 # get the kernel version we are assuming
248 if kernel_version is None:
250 kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
254 if len(kernel_version) == 0:
257 if len(kernel_version) > 1:
258 print( "WARNING: We may be returning modules for the wrong kernel." )
260 kernel_version= kernel_version[0]
262 print( "Using kernel version %s" % kernel_version )
264 # test to make sure the three files we need are present
265 pcitable_path = "%s/%s/pcitable" % (SYSIMG_PATH,hwdatapath)
266 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
267 (SYSIMG_PATH,kernel_version)
268 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
269 (SYSIMG_PATH,kernel_version)
271 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
272 if not os.access(path,os.R_OK):
273 print( "Unable to read %s" % path )
276 # now, with those three files, merge them all into one easy to
278 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
281 if all_modules is None:
282 print( "Unable to merge pci id tables." )
285 # this is the actual data structure we return
288 # these are the lists that will be in system_mods
293 # get all the system devices from lspci
294 lspci_prog= popen2.Popen3( LSPCI_CMD, 1 )
295 if lspci_prog is None:
296 print( "Unable to run %s with popen2.Popen3" % LSPCI_CMD )
299 returncode= lspci_prog.wait()
301 print( "Running %s failed" % LSPCI_CMD )
304 print( "Successfully ran %s" % LSPCI_CMD )
306 # for every lspci line, parse in the four tuple PCI id and the
307 # search for the corresponding driver from the dictionary
308 # generated by merge_hw_tables
309 for line in lspci_prog.fromchild:
312 # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
314 # Remove '"', 'Class ', and anything beginning with '-'
315 # (usually revisions and prog-if flags) so that we can
316 # split on whitespace:
318 # 00:1f.1 0101 8086 2411 8086 2411
321 line = line.replace('"', '')
322 line = line.replace('Class ', '')
323 line = re.sub('-[^ ]*', '', line)
329 classid = long(parts[1], 16)
330 vendorid = long(parts[2], 16)
331 deviceid = long(parts[3], 16)
333 print "Invalid line:", line
336 if classid not in (PCI_CLASS_NETWORK_ETHERNET,
337 PCI_CLASS_STORAGE_SCSI,
338 PCI_CLASS_STORAGE_SATA,
339 PCI_CLASS_STORAGE_RAID,
340 PCI_CLASS_STORAGE_OTHER,
341 PCI_CLASS_STORAGE_IDE):
344 # Device may have a subvendorid and subdeviceid
346 subvendorid = long(parts[4], 16)
347 subdeviceid = long(parts[5], 16)
349 subvendorid = PCI_ANY
350 subdeviceid = PCI_ANY
352 # search for driver that most closely matches the full_id
353 # to drivers that can handle any subvendor/subdevice
354 # version of the hardware.
355 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
356 (vendorid,deviceid,subvendorid,PCI_ANY),
357 (vendorid,deviceid,PCI_ANY,PCI_ANY))
359 for full_id in full_ids:
360 module = all_pci_ids.get(full_id, None)
361 if module is not None:
362 if classid == PCI_CLASS_NETWORK_ETHERNET:
363 network_mods.append(module[0])
364 elif classid in (PCI_CLASS_STORAGE_SCSI,
365 PCI_CLASS_STORAGE_SATA,
366 PCI_CLASS_STORAGE_RAID,
367 PCI_CLASS_STORAGE_OTHER,
368 PCI_CLASS_STORAGE_IDE):
369 scsi_mods.append(module[0])
371 print "not network or scsi: 0x%x" % classid
374 system_mods[MODULE_CLASS_SCSI]= scsi_mods
375 system_mods[MODULE_CLASS_NETWORK]= network_mods
380 def getKernelVersion( vars = {} , log = sys.stderr):
381 # make sure we have the variables we need
383 SYSIMG_PATH= vars["SYSIMG_PATH"]
384 if SYSIMG_PATH == "":
385 raise ValueError, "SYSIMG_PATH"
387 NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
388 except KeyError, var:
389 raise BootManagerException, "Missing variable in vars: %s\n" % var
390 except ValueError, var:
391 raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
394 if NODE_MODEL_OPTIONS & ModelOptions.SMP:
397 os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
398 os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
400 # smp kernel is not there; remove option from modeloptions
401 # such that the rest of the code base thinks we are just
402 # using the base kernel.
403 NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
404 vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
405 log.write( "WARNING: Couldn't locate smp kernel.\n")
408 initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
409 kernel_version= initrd.replace("initrd-", "").replace(".img", "")
412 kernel_version = None
414 return (initrd, kernel_version)
417 if __name__ == "__main__":
418 devices= get_block_device_list()
419 print "block devices detected:"
421 print "no devices found!"
423 for dev in devices.keys():
424 print "%s %s" % (dev, repr(devices[dev]))
428 memory= get_total_phsyical_mem()
430 print "unable to read /proc/meminfo for memory"
432 print "total physical memory: %d kb" % memory
437 kernel_version = None
438 if len(sys.argv) > 2:
439 kernel_version = sys.argv[1]
441 modules= get_system_modules()
443 print "unable to list system modules"
446 if type == MODULE_CLASS_SCSI:
447 print( "all scsi modules:" )
448 for a_mod in modules[type]:
450 elif type == MODULE_CLASS_NETWORK:
451 print( "all network modules:" )
452 for a_mod in modules[type]: