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
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= "0200"
97 PCI_CLASS_RAID= "0104"
98 PCI_CLASS_RAID2= "0100"
101 def get_total_phsyical_mem(self):
103 return the total physical memory of the machine, in kilobytes.
105 Return None if /proc/meminfo not readable.
109 meminfo_file= file(self.PROC_MEMINFO_PATH,"r")
115 for line in meminfo_file:
118 (fieldname,value)= string.split(line,":")
119 except ValueError, e:
120 # this will happen for lines that don't have two values
121 # (like the first line on 2.4 kernels)
124 fieldname= string.strip(fieldname)
125 value= string.strip(value)
127 if fieldname == "MemTotal":
129 (total_memory,units)= string.split(value)
130 except ValueError, e:
133 if total_memory == "" or total_memory == None or \
134 units == "" or units == None:
137 if string.lower(units) != "kb":
141 total_memory= int(total_memory)
142 except ValueError, e:
153 def get_block_device_list(self):
155 get a list of block devices from this system.
156 return an associative array, where the device name
157 (full /dev/device path) is the key, and the value
158 is a tuple of (major,minor,numblocks,gb_size,readonly)
161 # make sure we can access to the files/directories in /proc
162 if not os.access(self.PROC_PARTITIONS_PATH, os.F_OK):
166 # only do this once every system boot
167 if not os.access(self.DEVICES_SCANNED_FLAG, os.R_OK):
169 # this is ugly. under devfs, device
170 # entries in /dev/scsi/.. and /dev/ide/...
171 # don't show up until you attempt to read
172 # from the associated device at /dev (/dev/sda).
173 # so, lets run sfdisk -l (list partitions) against
174 # most possible block devices, that way they show
175 # up when it comes time to do the install.
176 for dev_prefix in ('sd','hd'):
178 while block_dev_num < 10:
179 if block_dev_num < 26:
180 devicename= "/dev/%s%c" % \
181 (dev_prefix, chr(ord('a')+block_dev_num))
183 devicename= "/dev/%s%c%c" % \
185 chr(ord('a')+((block_dev_num/26)-1)),
186 chr(ord('a')+(block_dev_num%26)) )
188 os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
189 block_dev_num = block_dev_num + 1
191 additional_scan_devices= ("/dev/cciss/c0d0p", "/dev/cciss/c0d1p",
192 "/dev/cciss/c0d2p", "/dev/cciss/c0d3p",
193 "/dev/cciss/c0d4p", "/dev/cciss/c0d5p",
194 "/dev/cciss/c0d6p", "/dev/cciss/c0d7p",
195 "/dev/cciss/c1d0p", "/dev/cciss/c1d1p",
196 "/dev/cciss/c1d2p", "/dev/cciss/c1d3p",
197 "/dev/cciss/c1d4p", "/dev/cciss/c1d5p",
198 "/dev/cciss/c1d6p", "/dev/cciss/c1d7p",)
200 for devicename in additional_scan_devices:
201 os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
203 os.system( "touch %s" % self.DEVICES_SCANNED_FLAG )
208 partitions_file= file(self.PROC_PARTITIONS_PATH,"r")
211 for line in partitions_file:
212 line_count= line_count + 1
214 # skip the first two lines always
218 parts= string.split(line)
225 dev_name= "/dev/%s" % device
230 blocks= int(parts[2])
231 except ValueError, err:
234 # skip and ignore any partitions
238 gb_size= blocks/self.BLOCKS_PER_GB
240 # parse the output of hdparm <disk> to get the readonly flag;
241 # if this fails, assume it isn't read only
244 hdparm_cmd = popen2.Popen3("hdparm %s 2> /dev/null" % dev_name)
245 status= hdparm_cmd.wait()
247 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
249 hdparm_output= hdparm_cmd.fromchild.read()
252 # parse the output of hdparm, the lines we are interested
259 for line in string.split(hdparm_output,"\n"):
261 line= string.strip(line)
265 line_parts= string.split(line,"=")
266 if len(line_parts) < 2:
269 name= string.strip(line_parts[0])
271 if name == "readonly":
272 value= string.strip(line_parts[1])
283 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
290 def get_system_modules( self, install_root, kernel_version= None ):
292 Return a list of kernel modules that this system requires.
293 This requires access to the installed system's root
294 directory, as the following files must exist and are used:
295 <install_root>/usr/share/hwdata/pcitable
296 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
297 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
299 If there are more than one kernels installed, and the kernel
300 version is not specified, then only the first one in
301 /lib/modules is used.
303 Returns a dictionary, keys being the type of module:
304 - scsi MODULE_CLASS_SCSI
305 - network MODULE_CLASS_NETWORK
306 The value being the kernel module name to load.
308 Some sata devices show up under an IDE device class,
309 hence the reason for checking for ide devices as well.
310 If there actually is a match in the pci -> module lookup
311 table, and its an ide device, its most likely sata,
312 as ide modules are built in to the kernel.
315 # get the kernel version we are assuming
316 if kernel_version is None:
318 kernel_version= os.listdir( "%s/lib/modules/" % install_root )
322 if len(kernel_version) == 0:
325 if len(kernel_version) > 1:
326 print( "WARNING: We may be returning modules for the wrong kernel." )
328 kernel_version= kernel_version[0]
330 print( "Using kernel version %s" % kernel_version )
332 # test to make sure the three files we need are present
333 pcitable_path = "%s/usr/share/hwdata/pcitable" % install_root
334 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
335 (install_root,kernel_version)
336 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
337 (install_root,kernel_version)
339 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
340 if not os.access(path,os.R_OK):
341 print( "Unable to read %s" % path )
344 # now, with those three files, merge them all into one easy to
346 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
349 if all_modules is None:
350 print( "Unable to merge pci id tables." )
353 # this is the actual data structure we return
356 # these are the lists that will be in system_mods
361 # get all the system devices from lspci
362 lspci_prog= popen2.Popen3( self.LSPCI_CMD, 1 )
363 if lspci_prog is None:
364 print( "Unable to run %s with popen2.Popen3" % self.LSPCI_CMD )
367 returncode= lspci_prog.wait()
369 print( "Running %s failed" % self.LSPCI_CMD )
372 print( "Successfully ran %s" % self.LSPCI_CMD )
374 for line in lspci_prog.fromchild:
375 if string.strip(line) == "":
378 parts= string.split(line)
381 classid= self.remove_quotes(parts[2])
383 print( "Skipping invalid line:", string.strip(line) )
386 if classid not in (self.PCI_CLASS_NETWORK,
388 self.PCI_CLASS_RAID2,
393 vendorid= self.remove_quotes(parts[3])
394 vendorid= long(vendorid,16)
395 deviceid= self.remove_quotes(parts[4])
396 deviceid= long(deviceid,16)
398 print( "Skipping invalid line:", string.strip(line) )
400 except ValueError, e:
401 print( "Skipping invalid line:", string.strip(line) )
405 # full device id with subvednor & subdevice set to ANY
406 PCI_ANY = 0xffffffffL
407 full_id= (vendorid,deviceid,PCI_ANY,PCI_ANY)
408 module = all_pci_ids.get(full_id, None)
410 # continue searching parts[5:] for module with
411 # subvendor first and then subdevice
414 for i in range(5,len(parts)):
415 p = self.remove_quotes(parts[i])
420 if subvendorindex != -1:
422 subvendorid= self.remove_quotes(parts[subvendorindex])
423 subvendorid= long(subvendorid,16)
425 print( "Skipping invalid line:", string.strip(line) )
427 except ValueError, e:
428 print( "Skipping invalid line:", string.strip(line) )
431 full_id=(vendorid,deviceid,subvendorid,PCI_ANY)
432 module = all_pci_ids.get(full_id, None)
434 for i in range(subvendorindex+1,len(parts)):
435 p = self.remove_quotes(parts[i])
440 if subdeviceindex != -1:
442 subdeviceid= self.remove_quotes(parts[subdeviceindex])
443 subdeviceid= long(subdeviceid,16)
445 print( "Skipping invalid line:", string.strip(line) )
447 except ValueError, e:
448 print( "Skipping invalid line:", string.strip(line) )
451 full_id= (vendorid,deviceid,subvendorid,subdeviceid)
452 module = all_pci_ids.get(full_id, None)
456 if classid == self.PCI_CLASS_NETWORK:
457 network_mods.append(module[0])
458 elif classid in (self.PCI_CLASS_RAID,
459 self.PCI_CLASS_RAID2,
461 scsi_mods.append(module[0])
463 system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
464 system_mods[self.MODULE_CLASS_NETWORK]= network_mods
469 def remove_quotes( self, str ):
471 remove double quotes off of a string if they exist
473 if str == "" or str == None:
477 if str[len(str)-1] == '"':
478 str= str[:len(str)-1]
483 if __name__ == "__main__":
486 devices= info.get_block_device_list()
487 print "block devices detected:"
489 print "no devices found!"
491 for dev in devices.keys():
492 print "%s %s" % (dev, repr(devices[dev]))
496 memory= info.get_total_phsyical_mem()
498 print "unable to read /proc/meminfo for memory"
500 print "total physical memory: %d kb" % memory
506 kernel_version = None
507 if len(sys.argv) > 2:
508 kernel_version = sys.argv[1]
510 modules= info.get_system_modules("/",kernel_version)
512 print "unable to list system modules"
515 if type == info.MODULE_CLASS_SCSI:
516 print( "all scsi modules:" )
517 for a_mod in modules[type]:
519 elif type == info.MODULE_CLASS_NETWORK:
520 print( "all network modules:" )
521 for a_mod in modules[type]: