4 # THIS file used to be named 'blockdevicescan.py', but has been renamed
5 # systeminfo.py and made more generic (now includes info about memory,
6 # and other hardware info on the machine)
9 # Copyright (c) 2003 Intel Corporation
10 # All rights reserved.
12 # Redistribution and use in source and binary forms, with or without
13 # modification, are permitted provided that the following conditions are
16 # * Redistributions of source code must retain the above copyright
17 # notice, this list of conditions and the following disclaimer.
19 # * Redistributions in binary form must reproduce the above
20 # copyright notice, this list of conditions and the following
21 # disclaimer in the documentation and/or other materials provided
22 # with the distribution.
24 # * Neither the name of the Intel Corporation nor the names of its
25 # contributors may be used to endorse or promote products derived
26 # from this software without specific prior written permission.
28 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
32 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 # EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF
41 # YOUR JURISDICTION. It is licensee's responsibility to comply with any
42 # export regulations applicable in licensee's jurisdiction. Under
43 # CURRENT (May 2000) U.S. export regulations this software is eligible
44 # for export from the U.S. and can be downloaded by or otherwise
45 # exported or reexported worldwide EXCEPT to U.S. embargoed destinations
46 # which include Cuba, Iraq, Libya, North Korea, Iran, Syria, Sudan,
47 # Afghanistan and any other country to which the U.S. has embargoed
51 # expected /proc/partitions format
53 #----------------------------------------------------
54 #major minor #blocks name
61 #----------------------------------------------------
67 import merge_hw_tables
69 hwdatapath = "usr/share/hwdata"
72 a utility class for finding and returning information about
73 block devices, memory, and other hardware on the system
76 PROC_MEMINFO_PATH= "/proc/meminfo"
79 PROC_PARTITIONS_PATH= "/proc/partitions"
81 # set when the sfdisk -l <dev> trick has been done to make
83 DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
85 # a /proc/partitions block is 1024 bytes
86 # a GB to a HDD manufacturer is 10^9 bytes
87 BLOCKS_PER_GB = pow(10, 9) / 1024.0;
90 # -n is numeric ids (no lookup), -m is machine readable
91 LSPCI_CMD= "/sbin/lspci -nm"
93 MODULE_CLASS_NETWORK= "network"
94 MODULE_CLASS_SCSI= "scsi"
96 PCI_CLASS_NETWORK_ETHERNET=0x0200L
97 PCI_CLASS_STORAGE_SCSI=0x0100L
98 PCI_CLASS_STORAGE_IDE=0x0101L
99 PCI_CLASS_STORAGE_FLOPPY=0x0102L
100 PCI_CLASS_STORAGE_IPI=0x0103L
101 PCI_CLASS_STORAGE_RAID=0x0104L
102 PCI_CLASS_STORAGE_OTHER=0x0180L
106 def get_total_phsyical_mem(self):
108 return the total physical memory of the machine, in kilobytes.
110 Return None if /proc/meminfo not readable.
114 meminfo_file= file(self.PROC_MEMINFO_PATH,"r")
120 for line in meminfo_file:
123 (fieldname,value)= string.split(line,":")
124 except ValueError, e:
125 # this will happen for lines that don't have two values
126 # (like the first line on 2.4 kernels)
129 fieldname= string.strip(fieldname)
130 value= string.strip(value)
132 if fieldname == "MemTotal":
134 (total_memory,units)= string.split(value)
135 except ValueError, e:
138 if total_memory == "" or total_memory == None or \
139 units == "" or units == None:
142 if string.lower(units) != "kb":
146 total_memory= int(total_memory)
147 except ValueError, e:
158 def get_block_device_list(self):
160 get a list of block devices from this system.
161 return an associative array, where the device name
162 (full /dev/device path) is the key, and the value
163 is a tuple of (major,minor,numblocks,gb_size,readonly)
166 # make sure we can access to the files/directories in /proc
167 if not os.access(self.PROC_PARTITIONS_PATH, os.F_OK):
171 # only do this once every system boot
172 if not os.access(self.DEVICES_SCANNED_FLAG, os.R_OK):
174 # this is ugly. under devfs, device
175 # entries in /dev/scsi/.. and /dev/ide/...
176 # don't show up until you attempt to read
177 # from the associated device at /dev (/dev/sda).
178 # so, lets run sfdisk -l (list partitions) against
179 # most possible block devices, that way they show
180 # up when it comes time to do the install.
181 for dev_prefix in ('sd','hd'):
183 while block_dev_num < 10:
184 if block_dev_num < 26:
185 devicename= "/dev/%s%c" % \
186 (dev_prefix, chr(ord('a')+block_dev_num))
188 devicename= "/dev/%s%c%c" % \
190 chr(ord('a')+((block_dev_num/26)-1)),
191 chr(ord('a')+(block_dev_num%26)) )
193 os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
194 block_dev_num = block_dev_num + 1
196 additional_scan_devices= ("/dev/cciss/c0d0p", "/dev/cciss/c0d1p",
197 "/dev/cciss/c0d2p", "/dev/cciss/c0d3p",
198 "/dev/cciss/c0d4p", "/dev/cciss/c0d5p",
199 "/dev/cciss/c0d6p", "/dev/cciss/c0d7p",
200 "/dev/cciss/c1d0p", "/dev/cciss/c1d1p",
201 "/dev/cciss/c1d2p", "/dev/cciss/c1d3p",
202 "/dev/cciss/c1d4p", "/dev/cciss/c1d5p",
203 "/dev/cciss/c1d6p", "/dev/cciss/c1d7p",)
205 for devicename in additional_scan_devices:
206 os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
208 os.system( "touch %s" % self.DEVICES_SCANNED_FLAG )
213 partitions_file= file(self.PROC_PARTITIONS_PATH,"r")
216 for line in partitions_file:
217 line_count= line_count + 1
219 # skip the first two lines always
223 parts= string.split(line)
230 dev_name= "/dev/%s" % device
235 blocks= int(parts[2])
236 except ValueError, err:
239 # skip and ignore any partitions
243 gb_size= blocks/self.BLOCKS_PER_GB
245 # parse the output of hdparm <disk> to get the readonly flag;
246 # if this fails, assume it isn't read only
249 hdparm_cmd = popen2.Popen3("hdparm %s 2> /dev/null" % dev_name)
250 status= hdparm_cmd.wait()
252 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
254 hdparm_output= hdparm_cmd.fromchild.read()
257 # parse the output of hdparm, the lines we are interested
264 for line in string.split(hdparm_output,"\n"):
266 line= string.strip(line)
270 line_parts= string.split(line,"=")
271 if len(line_parts) < 2:
274 name= string.strip(line_parts[0])
276 if name == "readonly":
277 value= string.strip(line_parts[1])
288 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
295 def get_system_modules( self, install_root, kernel_version= None ):
297 Return a list of kernel modules that this system requires.
298 This requires access to the installed system's root
299 directory, as the following files must exist and are used:
300 <install_root>/usr/share/hwdata/pcitable
301 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
302 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
304 If there are more than one kernels installed, and the kernel
305 version is not specified, then only the first one in
306 /lib/modules is used.
308 Returns a dictionary, keys being the type of module:
309 - scsi MODULE_CLASS_SCSI
310 - network MODULE_CLASS_NETWORK
311 The value being the kernel module name to load.
313 Some sata devices show up under an IDE device class,
314 hence the reason for checking for ide devices as well.
315 If there actually is a match in the pci -> module lookup
316 table, and its an ide device, its most likely sata,
317 as ide modules are built in to the kernel.
320 # get the kernel version we are assuming
321 if kernel_version is None:
323 kernel_version= os.listdir( "%s/lib/modules/" % install_root )
327 if len(kernel_version) == 0:
330 if len(kernel_version) > 1:
331 print( "WARNING: We may be returning modules for the wrong kernel." )
333 kernel_version= kernel_version[0]
335 print( "Using kernel version %s" % kernel_version )
337 # test to make sure the three files we need are present
338 pcitable_path = "%s/%s/pcitable" % (install_root,hwdatapath)
339 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
340 (install_root,kernel_version)
341 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
342 (install_root,kernel_version)
344 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
345 if not os.access(path,os.R_OK):
346 print( "Unable to read %s" % path )
349 # now, with those three files, merge them all into one easy to
351 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
354 if all_modules is None:
355 print( "Unable to merge pci id tables." )
358 # this is the actual data structure we return
361 # these are the lists that will be in system_mods
366 # get all the system devices from lspci
367 lspci_prog= popen2.Popen3( self.LSPCI_CMD, 1 )
368 if lspci_prog is None:
369 print( "Unable to run %s with popen2.Popen3" % self.LSPCI_CMD )
372 returncode= lspci_prog.wait()
374 print( "Running %s failed" % self.LSPCI_CMD )
377 print( "Successfully ran %s" % self.LSPCI_CMD )
379 # for every lspci line, parse in the four tuple PCI id and the
380 # search for the corresponding driver from the dictionary
381 # generated by merge_hw_tables
382 for line in lspci_prog.fromchild:
383 if string.strip(line) == "":
386 parts= string.split(line)
389 classid= self.remove_quotes(parts[2])
390 classid= long(classid,16)
392 print( "Skipping invalid classid:", string.strip(line) )
394 except ValueError, e:
395 print( "Skipping invalid classid:", string.strip(line) )
399 if classid not in (self.PCI_CLASS_NETWORK_ETHERNET,
400 self.PCI_CLASS_STORAGE_SCSI,
401 self.PCI_CLASS_STORAGE_RAID,
402 self.PCI_CLASS_STORAGE_OTHER,
403 self.PCI_CLASS_STORAGE_IDE):
406 vendorid = self.PCI_ANY
407 deviceid = self.PCI_ANY
408 subvendorid = self.PCI_ANY
409 subdeviceid = self.PCI_ANY
413 vendorid= self.remove_quotes(parts[3])
414 vendorid= long(vendorid,16)
416 print( "Skipping invalid vendorid:", string.strip(line) )
418 except ValueError, e:
419 print( "Skipping invalid vendorid:", string.strip(line) )
424 deviceid= self.remove_quotes(parts[4])
425 deviceid= long(deviceid,16)
427 print( "Skipping invalid deviceid:", string.strip(line) )
429 except ValueError, e:
430 print( "Skipping invalid deviceid:", string.strip(line) )
433 # Now get the subvendor & subdevice portion by searching
434 # parts[5:] of the lspci output. Note that we have to skip
435 # the portions of the lspci output string that indicate
438 # parse in subvendorid
440 for i in range(5,len(parts)):
441 p = self.remove_quotes(parts[i])
446 if subvendorindex != -1:
448 subvendorid= self.remove_quotes(parts[subvendorindex])
449 subvendorid= long(subvendorid,16)
451 print( "Skipping invalid line:", string.strip(line) )
453 except ValueError, e:
454 print( "Skipping invalid line:", string.strip(line) )
457 # parse in subdeviceid
459 for i in range(subvendorindex+1,len(parts)):
460 p = self.remove_quotes(parts[i])
464 if subdeviceindex != -1:
465 error_msg = "Skipping invalid subdeviceid:"
467 subdeviceid= self.remove_quotes(parts[subdeviceindex])
468 subdeviceid= long(subdeviceid,16)
470 print( error_msg, string.strip(line) )
472 except ValueError, e:
473 print( error_msg, string.strip(line) )
476 # search for driver that most closely matches the full_id
477 # to drivers that can handle any subvendor/subdevice
478 # version of the hardware.
479 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
480 (vendorid,deviceid,subvendorid,self.PCI_ANY),
481 (vendorid,deviceid,self.PCI_ANY,self.PCI_ANY))
483 for full_id in full_ids:
484 module = all_pci_ids.get(full_id, None)
485 if module is not None:
486 if classid == self.PCI_CLASS_NETWORK_ETHERNET:
487 network_mods.append(module[0])
488 elif classid in (self.PCI_CLASS_STORAGE_SCSI,
489 self.PCI_CLASS_STORAGE_RAID,
490 self.PCI_CLASS_STORAGE_OTHER,
491 self.PCI_CLASS_STORAGE_IDE):
492 scsi_mods.append(module[0])
495 system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
496 system_mods[self.MODULE_CLASS_NETWORK]= network_mods
501 def remove_quotes( self, str ):
503 remove double quotes off of a string if they exist
505 if str == "" or str == None:
509 if str[len(str)-1] == '"':
510 str= str[:len(str)-1]
515 if __name__ == "__main__":
518 devices= info.get_block_device_list()
519 print "block devices detected:"
521 print "no devices found!"
523 for dev in devices.keys():
524 print "%s %s" % (dev, repr(devices[dev]))
528 memory= info.get_total_phsyical_mem()
530 print "unable to read /proc/meminfo for memory"
532 print "total physical memory: %d kb" % memory
538 kernel_version = None
539 if len(sys.argv) > 2:
540 kernel_version = sys.argv[1]
542 modules= info.get_system_modules("/",kernel_version)
544 print "unable to list system modules"
547 if type == info.MODULE_CLASS_SCSI:
548 print( "all scsi modules:" )
549 for a_mod in modules[type]:
551 elif type == info.MODULE_CLASS_NETWORK:
552 print( "all network modules:" )
553 for a_mod in modules[type]: