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]=None
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):
197 blocks= int(parts[2])
198 except ValueError, err:
201 gb_size= blocks/BLOCKS_PER_GB
203 # check to see if the blk device is readonly
205 # can we write to it?
206 dev_name= "/dev/%s" % device
207 fb = open(dev_name,"w")
211 # check if EROFS errno
212 if errno.errorcode.get(e.errno,None) == 'EROFS':
215 # got some other errno, pretend device is readonly
218 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
223 def get_system_modules( vars = {}, log = sys.stderr):
225 Return a list of kernel modules that this system requires.
226 This requires access to the installed system's root
227 directory, as the following files must exist and are used:
228 <install_root>/usr/share/hwdata/pcitable
229 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
230 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
232 If there are more than one kernels installed, and the kernel
233 version is not specified, then only the first one in
234 /lib/modules is used.
236 Returns a dictionary, keys being the type of module:
237 - scsi MODULE_CLASS_SCSI
238 - network MODULE_CLASS_NETWORK
239 The value being the kernel module name to load.
241 Some sata devices show up under an IDE device class,
242 hence the reason for checking for ide devices as well.
243 If there actually is a match in the pci -> module lookup
244 table, and its an ide device, its most likely sata,
245 as ide modules are built in to the kernel.
248 if not vars.has_key("SYSIMG_PATH"):
249 vars["SYSIMG_PATH"]="/"
250 SYSIMG_PATH=vars["SYSIMG_PATH"]
252 if not vars.has_key("NODE_MODEL_OPTIONS"):
253 vars["NODE_MODEL_OPTIONS"] = 0;
255 initrd, kernel_version = getKernelVersion(vars, log)
257 # get the kernel version we are assuming
258 if kernel_version is None:
260 kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
264 if len(kernel_version) == 0:
267 if len(kernel_version) > 1:
268 print( "WARNING: We may be returning modules for the wrong kernel." )
270 kernel_version= kernel_version[0]
272 print( "Using kernel version %s" % kernel_version )
274 # test to make sure the three files we need are present
275 pcitable_path = "%s/%s/pcitable" % (SYSIMG_PATH,hwdatapath)
276 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
277 (SYSIMG_PATH,kernel_version)
278 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
279 (SYSIMG_PATH,kernel_version)
281 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
282 if not os.access(path,os.R_OK):
283 print( "Unable to read %s" % path )
286 # now, with those three files, merge them all into one easy to
288 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
291 if all_modules is None:
292 print( "Unable to merge pci id tables." )
295 # this is the actual data structure we return
298 # these are the lists that will be in system_mods
303 # get all the system devices from lspci
304 lspci_prog= popen2.Popen3( LSPCI_CMD, 1 )
305 if lspci_prog is None:
306 print( "Unable to run %s with popen2.Popen3" % LSPCI_CMD )
309 returncode= lspci_prog.wait()
311 print( "Running %s failed" % LSPCI_CMD )
314 print( "Successfully ran %s" % LSPCI_CMD )
316 # for every lspci line, parse in the four tuple PCI id and the
317 # search for the corresponding driver from the dictionary
318 # generated by merge_hw_tables
319 for line in lspci_prog.fromchild:
322 # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
324 # Remove '"', 'Class ', and anything beginning with '-'
325 # (usually revisions and prog-if flags) so that we can
326 # split on whitespace:
328 # 00:1f.1 0101 8086 2411 8086 2411
331 line = line.replace('"', '')
332 line = line.replace('Class ', '')
333 line = re.sub('-[^ ]*', '', line)
339 classid = long(parts[1], 16)
340 vendorid = long(parts[2], 16)
341 deviceid = long(parts[3], 16)
343 print "Invalid line:", line
346 if classid not in (PCI_CLASS_NETWORK_ETHERNET,
347 PCI_CLASS_STORAGE_SCSI,
348 PCI_CLASS_STORAGE_SATA,
349 PCI_CLASS_STORAGE_RAID,
350 PCI_CLASS_STORAGE_OTHER,
351 PCI_CLASS_STORAGE_IDE):
354 # Device may have a subvendorid and subdeviceid
356 subvendorid = long(parts[4], 16)
357 subdeviceid = long(parts[5], 16)
359 subvendorid = PCI_ANY
360 subdeviceid = PCI_ANY
362 # search for driver that most closely matches the full_id
363 # to drivers that can handle any subvendor/subdevice
364 # version of the hardware.
365 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
366 (vendorid,deviceid,subvendorid,PCI_ANY),
367 (vendorid,deviceid,PCI_ANY,PCI_ANY))
369 for full_id in full_ids:
370 module = all_pci_ids.get(full_id, None)
371 if module is not None:
372 if classid == PCI_CLASS_NETWORK_ETHERNET:
373 network_mods.append(module[0])
374 elif classid in (PCI_CLASS_STORAGE_SCSI,
375 PCI_CLASS_STORAGE_SATA,
376 PCI_CLASS_STORAGE_RAID,
377 PCI_CLASS_STORAGE_OTHER,
378 PCI_CLASS_STORAGE_IDE):
379 scsi_mods.append(module[0])
381 # XXX ata_piix and ahci both claim 8086:2652 and 8086:2653,
382 # and it is usually a non-visible BIOS option that decides
383 # which is appropriate. Just load both.
384 if vendorid == 0x8086 and (deviceid == 0x2652 or deviceid == 0x2653):
385 if module[0] == "ahci":
386 scsi_mods.append("ata_piix")
387 elif module[0] == "ata_piix":
388 scsi_mods.append("ahci")
390 print "not network or scsi: 0x%x" % classid
393 system_mods[MODULE_CLASS_SCSI]= scsi_mods
394 system_mods[MODULE_CLASS_NETWORK]= network_mods
399 def getKernelVersion( vars = {} , log = sys.stderr):
400 # make sure we have the variables we need
402 SYSIMG_PATH= vars["SYSIMG_PATH"]
403 if SYSIMG_PATH == "":
404 raise ValueError, "SYSIMG_PATH"
406 NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
407 except KeyError, var:
408 raise BootManagerException, "Missing variable in vars: %s\n" % var
409 except ValueError, var:
410 raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
413 if NODE_MODEL_OPTIONS & ModelOptions.SMP:
416 os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
417 os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
419 # smp kernel is not there; remove option from modeloptions
420 # such that the rest of the code base thinks we are just
421 # using the base kernel.
422 NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
423 vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
424 log.write( "WARNING: Couldn't locate smp kernel.\n")
427 initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
428 kernel_version= initrd.replace("initrd-", "").replace(".img", "")
431 kernel_version = None
433 return (initrd, kernel_version)
436 if __name__ == "__main__":
437 devices= get_block_device_list()
438 print "block devices detected:"
440 print "no devices found!"
442 for dev in devices.keys():
443 print "%s %s" % (dev, repr(devices[dev]))
447 memory= get_total_phsyical_mem()
449 print "unable to read /proc/meminfo for memory"
451 print "total physical memory: %d kb" % memory
456 kernel_version = None
457 if len(sys.argv) > 2:
458 kernel_version = sys.argv[1]
460 modules= get_system_modules()
462 print "unable to list system modules"
465 if type == MODULE_CLASS_SCSI:
466 print( "all scsi modules:" )
467 for a_mod in modules[type]:
469 elif type == MODULE_CLASS_NETWORK:
470 print( "all network modules:" )
471 for a_mod in modules[type]: