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
70 hwdatapath = "usr/share/hwdata"
73 a utility class for finding and returning information about
74 block devices, memory, and other hardware on the system
77 PROC_MEMINFO_PATH= "/proc/meminfo"
80 PROC_PARTITIONS_PATH= "/proc/partitions"
82 # set when the sfdisk -l <dev> trick has been done to make
84 DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
86 # a /proc/partitions block is 1024 bytes
87 # a GB to a HDD manufacturer is 10^9 bytes
88 BLOCKS_PER_GB = pow(10, 9) / 1024.0;
91 # -n is numeric ids (no lookup), -m is machine readable
92 LSPCI_CMD= "/sbin/lspci -nm"
94 MODULE_CLASS_NETWORK= "network"
95 MODULE_CLASS_SCSI= "scsi"
97 PCI_CLASS_NETWORK_ETHERNET=0x0200L
98 PCI_CLASS_STORAGE_SCSI=0x0100L
99 PCI_CLASS_STORAGE_IDE=0x0101L
100 PCI_CLASS_STORAGE_FLOPPY=0x0102L
101 PCI_CLASS_STORAGE_IPI=0x0103L
102 PCI_CLASS_STORAGE_RAID=0x0104L
103 PCI_CLASS_STORAGE_OTHER=0x0180L
107 def get_total_phsyical_mem(self):
109 return the total physical memory of the machine, in kilobytes.
111 Return None if /proc/meminfo not readable.
115 meminfo_file= file(self.PROC_MEMINFO_PATH,"r")
121 for line in meminfo_file:
124 (fieldname,value)= string.split(line,":")
125 except ValueError, e:
126 # this will happen for lines that don't have two values
127 # (like the first line on 2.4 kernels)
130 fieldname= string.strip(fieldname)
131 value= string.strip(value)
133 if fieldname == "MemTotal":
135 (total_memory,units)= string.split(value)
136 except ValueError, e:
139 if total_memory == "" or total_memory == None or \
140 units == "" or units == None:
143 if string.lower(units) != "kb":
147 total_memory= int(total_memory)
148 except ValueError, e:
159 def get_block_device_list(self):
161 get a list of block devices from this system.
162 return an associative array, where the device name
163 (full /dev/device path) is the key, and the value
164 is a tuple of (major,minor,numblocks,gb_size,readonly)
167 # make sure we can access to the files/directories in /proc
168 if not os.access(self.PROC_PARTITIONS_PATH, os.F_OK):
172 # only do this once every system boot
173 if not os.access(self.DEVICES_SCANNED_FLAG, os.R_OK):
175 # this is ugly. under devfs, device
176 # entries in /dev/scsi/.. and /dev/ide/...
177 # don't show up until you attempt to read
178 # from the associated device at /dev (/dev/sda).
179 # so, lets run sfdisk -l (list partitions) against
180 # most possible block devices, that way they show
181 # up when it comes time to do the install.
182 for dev_prefix in ('sd','hd'):
184 while block_dev_num < 10:
185 if block_dev_num < 26:
186 devicename= "/dev/%s%c" % \
187 (dev_prefix, chr(ord('a')+block_dev_num))
189 devicename= "/dev/%s%c%c" % \
191 chr(ord('a')+((block_dev_num/26)-1)),
192 chr(ord('a')+(block_dev_num%26)) )
194 os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
195 block_dev_num = block_dev_num + 1
197 additional_scan_devices= ("/dev/cciss/c0d0p", "/dev/cciss/c0d1p",
198 "/dev/cciss/c0d2p", "/dev/cciss/c0d3p",
199 "/dev/cciss/c0d4p", "/dev/cciss/c0d5p",
200 "/dev/cciss/c0d6p", "/dev/cciss/c0d7p",
201 "/dev/cciss/c1d0p", "/dev/cciss/c1d1p",
202 "/dev/cciss/c1d2p", "/dev/cciss/c1d3p",
203 "/dev/cciss/c1d4p", "/dev/cciss/c1d5p",
204 "/dev/cciss/c1d6p", "/dev/cciss/c1d7p",)
206 for devicename in additional_scan_devices:
207 os.system( "sfdisk -l %s > /dev/null 2>&1" % devicename )
209 os.system( "touch %s" % self.DEVICES_SCANNED_FLAG )
214 partitions_file= file(self.PROC_PARTITIONS_PATH,"r")
217 for line in partitions_file:
218 line_count= line_count + 1
220 # skip the first two lines always
224 parts= string.split(line)
231 dev_name= "/dev/%s" % device
236 blocks= int(parts[2])
237 except ValueError, err:
240 # skip and ignore any partitions
244 gb_size= blocks/self.BLOCKS_PER_GB
246 # parse the output of hdparm <disk> to get the readonly flag;
247 # if this fails, assume it isn't read only
250 hdparm_cmd = popen2.Popen3("hdparm %s 2> /dev/null" % dev_name)
251 status= hdparm_cmd.wait()
253 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
255 hdparm_output= hdparm_cmd.fromchild.read()
258 # parse the output of hdparm, the lines we are interested
265 for line in string.split(hdparm_output,"\n"):
267 line= string.strip(line)
271 line_parts= string.split(line,"=")
272 if len(line_parts) < 2:
275 name= string.strip(line_parts[0])
277 if name == "readonly":
278 value= string.strip(line_parts[1])
289 devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
296 def get_system_modules( self, install_root, kernel_version= None ):
298 Return a list of kernel modules that this system requires.
299 This requires access to the installed system's root
300 directory, as the following files must exist and are used:
301 <install_root>/usr/share/hwdata/pcitable
302 <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
303 <install_root>/lib/modules/(first entry if kernel version unspecified)/modules.dep
305 If there are more than one kernels installed, and the kernel
306 version is not specified, then only the first one in
307 /lib/modules is used.
309 Returns a dictionary, keys being the type of module:
310 - scsi MODULE_CLASS_SCSI
311 - network MODULE_CLASS_NETWORK
312 The value being the kernel module name to load.
314 Some sata devices show up under an IDE device class,
315 hence the reason for checking for ide devices as well.
316 If there actually is a match in the pci -> module lookup
317 table, and its an ide device, its most likely sata,
318 as ide modules are built in to the kernel.
321 # get the kernel version we are assuming
322 if kernel_version is None:
324 kernel_version= os.listdir( "%s/lib/modules/" % install_root )
328 if len(kernel_version) == 0:
331 if len(kernel_version) > 1:
332 print( "WARNING: We may be returning modules for the wrong kernel." )
334 kernel_version= kernel_version[0]
336 print( "Using kernel version %s" % kernel_version )
338 # test to make sure the three files we need are present
339 pcitable_path = "%s/%s/pcitable" % (install_root,hwdatapath)
340 modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
341 (install_root,kernel_version)
342 modules_dep_path = "%s/lib/modules/%s/modules.dep" % \
343 (install_root,kernel_version)
345 for path in (pcitable_path,modules_pcimap_path,modules_dep_path):
346 if not os.access(path,os.R_OK):
347 print( "Unable to read %s" % path )
350 # now, with those three files, merge them all into one easy to
352 (all_pci_ids, all_modules) = merge_hw_tables.merge_files( modules_dep_path,
355 if all_modules is None:
356 print( "Unable to merge pci id tables." )
359 # this is the actual data structure we return
362 # these are the lists that will be in system_mods
367 # get all the system devices from lspci
368 lspci_prog= popen2.Popen3( self.LSPCI_CMD, 1 )
369 if lspci_prog is None:
370 print( "Unable to run %s with popen2.Popen3" % self.LSPCI_CMD )
373 returncode= lspci_prog.wait()
375 print( "Running %s failed" % self.LSPCI_CMD )
378 print( "Successfully ran %s" % self.LSPCI_CMD )
380 # for every lspci line, parse in the four tuple PCI id and the
381 # search for the corresponding driver from the dictionary
382 # generated by merge_hw_tables
383 for line in lspci_prog.fromchild:
386 # 00:1f.1 "Class 0101" "8086" "2411" -r02 -p80 "8086" "2411"
388 # Remove '"', 'Class ', and anything beginning with '-'
389 # (usually revisions and prog-if flags) so that we can
390 # split on whitespace:
392 # 00:1f.1 0101 8086 2411 8086 2411
395 line = line.replace('"', '')
396 line = line.replace('Class ', '')
397 line = re.sub('-[^ ]*', '', line)
403 classid = long(parts[1], 16)
404 vendorid = long(parts[2], 16)
405 deviceid = long(parts[3], 16)
407 print "Invalid line:", line
410 if classid not in (self.PCI_CLASS_NETWORK_ETHERNET,
411 self.PCI_CLASS_STORAGE_SCSI,
412 self.PCI_CLASS_STORAGE_RAID,
413 self.PCI_CLASS_STORAGE_OTHER,
414 self.PCI_CLASS_STORAGE_IDE):
417 # Device may have a subvendorid and subdeviceid
419 subvendorid = long(parts[4], 16)
420 subdeviceid = long(parts[5], 16)
422 subvendorid = self.PCI_ANY
423 subdeviceid = self.PCI_ANY
425 # search for driver that most closely matches the full_id
426 # to drivers that can handle any subvendor/subdevice
427 # version of the hardware.
428 full_ids = ((vendorid,deviceid,subvendorid,subdeviceid),
429 (vendorid,deviceid,subvendorid,self.PCI_ANY),
430 (vendorid,deviceid,self.PCI_ANY,self.PCI_ANY))
432 for full_id in full_ids:
433 module = all_pci_ids.get(full_id, None)
434 if module is not None:
435 if classid == self.PCI_CLASS_NETWORK_ETHERNET:
436 network_mods.append(module[0])
437 elif classid in (self.PCI_CLASS_STORAGE_SCSI,
438 self.PCI_CLASS_STORAGE_RAID,
439 self.PCI_CLASS_STORAGE_OTHER,
440 self.PCI_CLASS_STORAGE_IDE):
441 scsi_mods.append(module[0])
443 print "not network or scsi: 0x%x" % classid
446 system_mods[self.MODULE_CLASS_SCSI]= scsi_mods
447 system_mods[self.MODULE_CLASS_NETWORK]= network_mods
452 def remove_quotes( self, str ):
454 remove double quotes off of a string if they exist
456 if str == "" or str == None:
460 if str[len(str)-1] == '"':
461 str= str[:len(str)-1]
466 if __name__ == "__main__":
469 devices= info.get_block_device_list()
470 print "block devices detected:"
472 print "no devices found!"
474 for dev in devices.keys():
475 print "%s %s" % (dev, repr(devices[dev]))
479 memory= info.get_total_phsyical_mem()
481 print "unable to read /proc/meminfo for memory"
483 print "total physical memory: %d kb" % memory
489 kernel_version = None
490 if len(sys.argv) > 2:
491 kernel_version = sys.argv[1]
493 modules= info.get_system_modules("/",kernel_version)
495 print "unable to list system modules"
498 if type == info.MODULE_CLASS_SCSI:
499 print( "all scsi modules:" )
500 for a_mod in modules[type]:
502 elif type == info.MODULE_CLASS_NETWORK:
503 print( "all network modules:" )
504 for a_mod in modules[type]: