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 for devicename in valid_blk_names.keys():
144 # devfs under 2.4 (old boot cds) used to list partitions
145 # in a format such as scsi/host0/bus0/target0/lun0/disc
146 # and /dev/sda, etc. were just symlinks
148 devfsname= os.readlink( "/dev/%s" % devicename )
149 valid_blk_names[devfsname]= devicename
153 # only do this once every system boot
154 if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
156 # this is ugly. under devfs, device
157 # entries in /dev/scsi/.. and /dev/ide/...
158 # don't show up until you attempt to read
159 # from the associated device at /dev (/dev/sda).
160 # so, lets run sfdisk -l (list partitions) against
161 # most possible block devices, that way they show
162 # up when it comes time to do the install.
163 devicenames = valid_blk_names.keys()
165 for devicename in devicenames:
166 os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
169 fb = open(DEVICES_SCANNED_FLAG,"w")
174 partitions_file= file(PROC_PARTITIONS_PATH,"r")
176 for line in partitions_file:
177 line_count= line_count + 1
179 # skip the first two lines always
183 parts= string.split(line)
190 # skip and ignore any partitions
191 if not valid_blk_names.has_key(device):
193 elif valid_blk_names[device] is not None:
194 device= valid_blk_names[device]
199 blocks= int(parts[2])
200 except ValueError, err:
203 gb_size= blocks/BLOCKS_PER_GB
205 # check to see if the blk device is readonly
207 # can we write to it?
208 dev_name= "/dev/%s" % device
209 fb = open(dev_name,"w")
213 # check if EROFS errno
214 if errno.errorcode.get(e.errno,None) == 'EROFS':
217 # got some other errno, pretend device is readonly
220 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
225 def get_system_modules( vars = {}, log = sys.stderr):
227 Return a list of kernel modules that this system requires.
228 This requires access to the installed system's root
229 directory, as the following files must exist and are used:
230 <install_root>/usr/share/hwdata/pcitable
231 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
232 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
234 If there are more than one kernels installed, and the kernel
235 version is not specified, then only the first one in
236 /lib/modules is used.
238 Returns a dictionary, keys being the type of module:
239 - scsi MODULE_CLASS_SCSI
240 - network MODULE_CLASS_NETWORK
241 The value being the kernel module name to load.
243 Some sata devices show up under an IDE device class,
244 hence the reason for checking for ide devices as well.
245 If there actually is a match in the pci -> module lookup
246 table, and its an ide device, its most likely sata,
247 as ide modules are built in to the kernel.
250 if not vars.has_key("SYSIMG_PATH"):
251 vars["SYSIMG_PATH"]="/"
252 SYSIMG_PATH=vars["SYSIMG_PATH"]
254 if not vars.has_key("NODE_MODEL_OPTIONS"):
255 vars["NODE_MODEL_OPTIONS"] = 0;
257 initrd, kernel_version = getKernelVersion(vars, log)
259 # get the kernel version we are assuming
260 if kernel_version is None:
262 kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
266 if len(kernel_version) == 0:
269 if len(kernel_version) > 1:
270 print( "WARNING: We may be returning modules for the wrong kernel." )
272 kernel_version= kernel_version[0]
274 print( "Using kernel version %s" % kernel_version )
276 # test to make sure the three files we need are present
277 pcitable_path = "%s/%s/pcitable" % (SYSIMG_PATH,hwdatapath)
278 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
279 (SYSIMG_PATH,kernel_version)
280 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
281 (SYSIMG_PATH,kernel_version)
283 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
284 if not os.access(path,os.R_OK):
285 print( "Unable to read %s" % path )
288 # now, with those three files, merge them all into one easy to
290 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
293 if all_modules is None:
294 print( "Unable to merge pci id tables." )
297 # this is the actual data structure we return
300 # these are the lists that will be in system_mods
305 # get all the system devices from lspci
306 lspci_prog= popen2.Popen3( LSPCI_CMD, 1 )
307 if lspci_prog is None:
308 print( "Unable to run %s with popen2.Popen3" % LSPCI_CMD )
311 returncode= lspci_prog.wait()
313 print( "Running %s failed" % LSPCI_CMD )
316 print( "Successfully ran %s" % LSPCI_CMD )
318 # for every lspci line, parse in the four tuple PCI id and the
319 # search for the corresponding driver from the dictionary
320 # generated by merge_hw_tables
321 for line in lspci_prog.fromchild:
324 # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
326 # Remove '"', 'Class ', and anything beginning with '-'
327 # (usually revisions and prog-if flags) so that we can
328 # split on whitespace:
330 # 00:1f.1 0101 8086 2411 8086 2411
333 line = line.replace('"', '')
334 line = line.replace('Class ', '')
335 line = re.sub('-[^ ]*', '', line)
341 classid = long(parts[1], 16)
342 vendorid = long(parts[2], 16)
343 deviceid = long(parts[3], 16)
345 print "Invalid line:", line
348 if classid not in (PCI_CLASS_NETWORK_ETHERNET,
349 PCI_CLASS_STORAGE_SCSI,
350 PCI_CLASS_STORAGE_SATA,
351 PCI_CLASS_STORAGE_RAID,
352 PCI_CLASS_STORAGE_OTHER,
353 PCI_CLASS_STORAGE_IDE):
356 # Device may have a subvendorid and subdeviceid
358 subvendorid = long(parts[4], 16)
359 subdeviceid = long(parts[5], 16)
361 subvendorid = PCI_ANY
362 subdeviceid = PCI_ANY
364 # search for driver that most closely matches the full_id
365 # to drivers that can handle any subvendor/subdevice
366 # version of the hardware.
367 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
368 (vendorid,deviceid,subvendorid,PCI_ANY),
369 (vendorid,deviceid,PCI_ANY,PCI_ANY))
371 for full_id in full_ids:
372 module = all_pci_ids.get(full_id, None)
373 if module is not None:
374 if classid == PCI_CLASS_NETWORK_ETHERNET:
375 network_mods.append(module[0])
376 elif classid in (PCI_CLASS_STORAGE_SCSI,
377 PCI_CLASS_STORAGE_SATA,
378 PCI_CLASS_STORAGE_RAID,
379 PCI_CLASS_STORAGE_OTHER,
380 PCI_CLASS_STORAGE_IDE):
381 scsi_mods.append(module[0])
383 # XXX ata_piix and ahci both claim 8086:2652 and 8086:2653,
384 # and it is usually a non-visible BIOS option that decides
385 # which is appropriate. Just load both.
386 if vendorid == 0x8086 and (deviceid == 0x2652 or deviceid == 0x2653):
387 if module[0] == "ahci":
388 scsi_mods.append("ata_piix")
389 elif module[0] == "ata_piix":
390 scsi_mods.append("ahci")
392 print "not network or scsi: 0x%x" % classid
395 system_mods[MODULE_CLASS_SCSI]= scsi_mods
396 system_mods[MODULE_CLASS_NETWORK]= network_mods
401 def getKernelVersion( vars = {} , log = sys.stderr):
402 # make sure we have the variables we need
404 SYSIMG_PATH= vars["SYSIMG_PATH"]
405 if SYSIMG_PATH == "":
406 raise ValueError, "SYSIMG_PATH"
408 NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
409 except KeyError, var:
410 raise BootManagerException, "Missing variable in vars: %s\n" % var
411 except ValueError, var:
412 raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
415 if NODE_MODEL_OPTIONS & ModelOptions.SMP:
418 os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
419 os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
421 # smp kernel is not there; remove option from modeloptions
422 # such that the rest of the code base thinks we are just
423 # using the base kernel.
424 NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
425 vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
426 log.write( "WARNING: Couldn't locate smp kernel.\n")
429 initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
430 kernel_version= initrd.replace("initrd-", "").replace(".img", "")
433 kernel_version = None
435 return (initrd, kernel_version)
438 if __name__ == "__main__":
439 devices= get_block_device_list()
440 print "block devices detected:"
442 print "no devices found!"
444 for dev in devices.keys():
445 print "%s %s" % (dev, repr(devices[dev]))
449 memory= get_total_phsyical_mem()
451 print "unable to read /proc/meminfo for memory"
453 print "total physical memory: %d kb" % memory
458 kernel_version = None
459 if len(sys.argv) > 2:
460 kernel_version = sys.argv[1]
462 modules= get_system_modules()
464 print "unable to list system modules"
467 if type == MODULE_CLASS_SCSI:
468 print( "all scsi modules:" )
469 for a_mod in modules[type]:
471 elif type == MODULE_CLASS_NETWORK:
472 print( "all network modules:" )
473 for a_mod in modules[type]: