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 ):
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)/modules.pcimap
299 <install_root>/lib/modules/(first entry)/modules.dep
301 Note, that this assumes there is only one kernel
302 that is installed. If there are more than one, then
303 only the first one in a directory listing 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.
311 # get the kernel version we are assuming
313 kernel_version= os.listdir( "%s/lib/modules/" % install_root )
317 if len(kernel_version) == 0:
320 if len(kernel_version) > 1:
321 print( "WARNING: We may be returning modules for the wrong kernel." )
323 kernel_version= kernel_version[0]
324 print( "Using kernel version %s" % kernel_version )
326 # test to make sure the three files we need are present
327 pcitable_path = "%s/usr/share/hwdata/pcitable" % install_root
328 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
329 (install_root,kernel_version)
330 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
331 (install_root,kernel_version)
333 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
334 if not os.access(path,os.R_OK):
335 print( "Unable to read %s" % path )
338 # now, with those three files, merge them all into one easy to
340 all_modules= merge_hw_tables().merge_files( modules_dep_path,
344 if all_modules is None:
345 print( "Unable to merge pci id tables." )
349 # this is the actual data structure we return
352 # these are the lists that will be in system_mods
357 # get all the system devices from lspci
358 lspci_prog= popen2.Popen3( self.LSPCI_CMD, 1 )
359 if lspci_prog is None:
360 print( "Unable to run %s with popen2.Popen3" % self.LSPCI_CMD )
363 returncode= lspci_prog.wait()
365 print( "Running %s failed" % self.LSPCI_CMD )
368 print( "Successfully ran %s" % self.LSPCI_CMD )
370 for line in lspci_prog.fromchild:
371 if string.strip(line) == "":
374 parts= string.split(line)
377 classid= self.remove_quotes(parts[2])
378 vendorid= self.remove_quotes(parts[3])
379 deviceid= self.remove_quotes(parts[4])
381 print( "Skipping invalid line:", string.strip(line) )
384 if classid not in (self.PCI_CLASS_NETWORK,
386 self.PCI_CLASS_RAID2):
389 full_deviceid= "%s:%s" % (vendorid,deviceid)
391 for module in all_modules.keys():
392 if full_deviceid in all_modules[module]:
393 if classid == self.PCI_CLASS_NETWORK:
394 network_mods.append(module)
395 elif classid in (self.PCI_CLASS_RAID,
396 self.PCI_CLASS_RAID2):
397 scsi_mods.append(module)
399 system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
400 system_mods[self.MODULE_CLASS_NETWORK]= network_mods
405 def remove_quotes( self, str ):
407 remove double quotes off of a string if they exist
409 if str == "" or str == None:
413 if str[len(str)-1] == '"':
414 str= str[:len(str)-1]
419 if __name__ == "__main__":
422 devices= info.get_block_device_list()
423 print "block devices detected:"
425 print "no devices found!"
427 for dev in devices.keys():
428 print "%s %s" % (dev, repr(devices[dev]))
432 memory= info.get_total_phsyical_mem()
434 print "unable to read /proc/meminfo for memory"
436 print "total physical memory: %d kb" % memory
440 modules= info.get_system_modules("/")
442 print "unable to list system modules"
445 if type == info.MODULE_CLASS_SCSI:
446 print( "all scsi modules:" )
447 for a_mod in modules[type]:
449 elif type == info.MODULE_CLASS_NETWORK:
450 print( "all network modules:" )
451 for a_mod in modules[type]: