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 from merge_hw_tables 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 # if the last char in device is a number, its
226 # a partition, and we ignore it
228 if device[len(device)-1].isdigit():
231 dev_name= "/dev/%s" % device
236 blocks= int(parts[2])
237 except ValueError, err:
240 gb_size= blocks/self.BLOCKS_PER_GB
242 # parse the output of hdparm <disk> to get the readonly flag;
243 # if this fails, assume it isn't read only
246 hdparm_cmd = popen2.Popen3("hdparm %s 2> /dev/null" % dev_name)
247 status= hdparm_cmd.wait()
249 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
251 hdparm_output= hdparm_cmd.fromchild.read()
254 # parse the output of hdparm, the lines we are interested
261 for line in string.split(hdparm_output,"\n"):
263 line= string.strip(line)
267 line_parts= string.split(line,"=")
268 if len(line_parts) < 2:
271 name= string.strip(line_parts[0])
273 if name == "readonly":
274 value= string.strip(line_parts[1])
285 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
292 def get_system_modules( self, install_root, kernel_version= None ):
294 Return a list of kernel modules that this system requires.
295 This requires access to the installed system's root
296 directory, as the following files must exist and are used:
297 <install_root>/usr/share/hwdata/pcitable
298 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
299 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
301 If there are more than one kernels installed, and the kernel
302 version is not specified, then only the first one in
303 /lib/modules is used.
305 Returns a dictionary, keys being the type of module:
306 - scsi MODULE_CLASS_SCSI
307 - network MODULE_CLASS_NETWORK
308 The value being the kernel module name to load.
310 Some sata devices show up under an IDE device class,
311 hence the reason for checking for ide devices as well.
312 If there actually is a match in the pci -> module lookup
313 table, and its an ide device, its most likely sata,
314 as ide modules are built in to the kernel.
317 # get the kernel version we are assuming
318 if kernel_version is None:
320 kernel_version= os.listdir( "%s/lib/modules/" % install_root )
324 if len(kernel_version) == 0:
327 if len(kernel_version) > 1:
328 print( "WARNING: We may be returning modules for the wrong kernel." )
330 kernel_version= kernel_version[0]
332 print( "Using kernel version %s" % kernel_version )
334 # test to make sure the three files we need are present
335 pcitable_path = "%s/usr/share/hwdata/pcitable" % install_root
336 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
337 (install_root,kernel_version)
338 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
339 (install_root,kernel_version)
341 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
342 if not os.access(path,os.R_OK):
343 print( "Unable to read %s" % path )
346 # now, with those three files, merge them all into one easy to
348 all_modules= merge_hw_tables().merge_files( modules_dep_path,
352 if all_modules is None:
353 print( "Unable to merge pci id tables." )
357 # this is the actual data structure we return
360 # these are the lists that will be in system_mods
365 # get all the system devices from lspci
366 lspci_prog= popen2.Popen3( self.LSPCI_CMD, 1 )
367 if lspci_prog is None:
368 print( "Unable to run %s with popen2.Popen3" % self.LSPCI_CMD )
371 returncode= lspci_prog.wait()
373 print( "Running %s failed" % self.LSPCI_CMD )
376 print( "Successfully ran %s" % self.LSPCI_CMD )
378 for line in lspci_prog.fromchild:
379 if string.strip(line) == "":
382 parts= string.split(line)
385 classid= self.remove_quotes(parts[2])
386 vendorid= self.remove_quotes(parts[3])
387 deviceid= self.remove_quotes(parts[4])
389 print( "Skipping invalid line:", string.strip(line) )
392 if classid not in (self.PCI_CLASS_NETWORK,
394 self.PCI_CLASS_RAID2,
398 full_deviceid= "%s:%s" % (vendorid,deviceid)
400 for module in all_modules.keys():
401 if full_deviceid in all_modules[module]:
402 if classid == self.PCI_CLASS_NETWORK:
403 network_mods.append(module)
404 elif classid in (self.PCI_CLASS_RAID,
405 self.PCI_CLASS_RAID2,
407 scsi_mods.append(module)
409 system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
410 system_mods[self.MODULE_CLASS_NETWORK]= network_mods
415 def remove_quotes( self, str ):
417 remove double quotes off of a string if they exist
419 if str == "" or str == None:
423 if str[len(str)-1] == '"':
424 str= str[:len(str)-1]
429 if __name__ == "__main__":
432 devices= info.get_block_device_list()
433 print "block devices detected:"
435 print "no devices found!"
437 for dev in devices.keys():
438 print "%s %s" % (dev, repr(devices[dev]))
442 memory= info.get_total_phsyical_mem()
444 print "unable to read /proc/meminfo for memory"
446 print "total physical memory: %d kb" % memory
450 modules= info.get_system_modules("/")
452 print "unable to list system modules"
455 if type == info.MODULE_CLASS_SCSI:
456 print( "all scsi modules:" )
457 for a_mod in modules[type]:
459 elif type == info.MODULE_CLASS_NETWORK:
460 print( "all network modules:" )
461 for a_mod in modules[type]: