only look at /etc/modprobe.d/{blacklist,blacklist-compat,blacklist-firewire}
[bootmanager.git] / source / systeminfo.py
1 #!/usr/bin/python2
2
3 # Copyright (c) 2003 Intel Corporation
4 # All rights reserved.
5 #
6 # Copyright (c) 2004-2006 The Trustees of Princeton University
7 # All rights reserved.
8 # expected /proc/partitions format
9
10
11 #----------------------------------------------------
12 #major minor  #blocks  name
13 #
14 #3     0   40017915 hda
15 #3     1     208813 hda1
16 #3     2   20482875 hda2
17 #3     3     522112 hda3
18 #3     4   18804082 hda4
19 #----------------------------------------------------
20
21
22 import string
23 import sys
24 import os
25 import popen2
26 import re
27 import errno
28 import ModelOptions
29 from pypci import *
30 from Exceptions import *
31
32 """
33 a utility class for finding and returning information about
34 block devices, memory, and other hardware on the system
35 """
36
37 PROC_MEMINFO_PATH= "/proc/meminfo"
38 PROC_PARTITIONS_PATH= "/proc/partitions"
39
40 # set when the sfdisk -l <dev> trick has been done to make
41 # all devices show up
42 DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
43
44 # a /proc/partitions block is 1024 bytes
45 # a GB to a HDD manufacturer is 10^9 bytes
46 BLOCKS_PER_GB = pow(10, 9) / 1024.0;
47
48
49 MODULE_CLASS_NETWORK= "network"
50 MODULE_CLASS_SCSI= "scsi"
51
52 PCI_BASE_CLASS_NETWORK=0x02L
53 PCI_BASE_CLASS_STORAGE=0x01L
54
55 PCI_ANY=0xffffffffL
56
57 def get_total_phsyical_mem(vars = {}, log = sys.stderr):
58     """
59     return the total physical memory of the machine, in kilobytes.
60
61     Return None if /proc/meminfo not readable.
62     """
63
64     try:
65         meminfo_file= file(PROC_MEMINFO_PATH,"r")
66     except IOError, e:
67         return
68
69     total_memory= None
70
71     for line in meminfo_file:
72
73         try:
74             (fieldname,value)= string.split(line,":")
75         except ValueError, e:
76             # this will happen for lines that don't have two values
77             # (like the first line on 2.4 kernels)
78             continue
79
80         fieldname= string.strip(fieldname)
81         value= string.strip(value)
82         
83         if fieldname == "MemTotal":
84             try:
85                 (total_memory,units)= string.split(value)
86             except ValueError, e:
87                 return
88
89             if total_memory == "" or total_memory == None or \
90                    units == "" or units == None:
91                 return
92
93             if string.lower(units) != "kb":
94                 return
95
96             try:
97                 total_memory= int(total_memory)
98             except ValueError, e:
99                 return
100
101             break
102
103     meminfo_file.close()
104     return total_memory
105
106 def get_block_device_list(vars = {}, log = sys.stderr):
107     """
108     get a list of block devices from this system.
109     return an associative array, where the device name
110     (full /dev/device path) is the key, and the value
111     is a tuple of (major,minor,numblocks,gb_size,readonly)
112     """
113
114     # make sure we can access to the files/directories in /proc
115     if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
116         return None
117
118     # table with valid scsi/sata/ide/raid block device names
119     valid_blk_names = {}
120     # add in valid sd and hd block device names
121     for blk_prefix in ('sd','hd'):
122         for blk_num in map ( \
123             lambda x: chr(x), range(ord('a'),ord('z')+1)):
124             devicename="%s%c" % (blk_prefix, blk_num)
125             valid_blk_names[devicename]=None
126
127     # add in valid scsi raid block device names
128     for M in range(0,1+1):
129         for N in range(0,7+1):
130             devicename = "cciss/c%dd%d" % (M,N)
131             valid_blk_names[devicename]=None
132
133     for devicename in valid_blk_names.keys():
134         # devfs under 2.4 (old boot cds) used to list partitions
135         # in a format such as scsi/host0/bus0/target0/lun0/disc
136         # and /dev/sda, etc. were just symlinks
137         try:
138             devfsname= os.readlink( "/dev/%s" % devicename )
139             valid_blk_names[devfsname]=None
140         except OSError:
141             pass
142
143     # only do this once every system boot
144     if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
145
146         # this is ugly. under devfs, device
147         # entries in /dev/scsi/.. and /dev/ide/...
148         # don't show up until you attempt to read
149         # from the associated device at /dev (/dev/sda).
150         # so, lets run sfdisk -l (list partitions) against
151         # most possible block devices, that way they show
152         # up when it comes time to do the install.
153         devicenames = valid_blk_names.keys()
154         devicenames.sort()
155         for devicename in devicenames:
156             os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
157
158         # touch file
159         fb = open(DEVICES_SCANNED_FLAG,"w")
160         fb.close()
161
162     devicelist= {}
163
164     partitions_file= file(PROC_PARTITIONS_PATH,"r")
165     line_count= 0
166     for line in partitions_file:
167         line_count= line_count + 1
168
169         # skip the first two lines always
170         if line_count < 2:
171             continue
172
173         parts= string.split(line)
174
175         if len(parts) < 4:
176             continue
177
178         device= parts[3]
179
180         # skip and ignore any partitions
181         if not valid_blk_names.has_key(device):
182             continue
183
184         try:
185             major= int(parts[0])
186             minor= int(parts[1])
187             blocks= int(parts[2])
188         except ValueError, err:
189             continue
190
191         gb_size= blocks/BLOCKS_PER_GB
192
193         # check to see if the blk device is readonly
194         try:
195             # can we write to it?
196             dev_name= "/dev/%s" % device
197             fb = open(dev_name,"w")
198             fb.close()
199             readonly=False
200         except IOError, e:
201             # check if EROFS errno
202             if errno.errorcode.get(e.errno,None) == 'EROFS':
203                 readonly=True
204             else:
205                 # got some other errno, pretend device is readonly
206                 readonly=True
207
208         devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
209
210     return devicelist
211
212
213 def get_system_modules( vars = {}, log = sys.stderr):
214     """
215     Return a list of kernel modules that this system requires.
216     This requires access to the installed system's root
217     directory, as the following file must exist and is used:
218     <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
219
220     If there are more than one kernels installed, and the kernel
221     version is not specified, then only the first one in
222     /lib/modules is used.
223
224     Returns a dictionary, keys being the type of module:
225         - scsi       MODULE_CLASS_SCSI
226         - network    MODULE_CLASS_NETWORK
227     The value being the kernel module name to load.
228
229     Some sata devices show up under an IDE device class,
230     hence the reason for checking for ide devices as well.
231     If there actually is a match in the pci -> module lookup
232     table, and its an ide device, its most likely sata,
233     as ide modules are built in to the kernel.
234     """
235
236     if not vars.has_key("SYSIMG_PATH"):
237         vars["SYSIMG_PATH"]="/"
238     SYSIMG_PATH=vars["SYSIMG_PATH"]
239
240     if not vars.has_key("NODE_MODEL_OPTIONS"):
241         vars["NODE_MODEL_OPTIONS"] = 0;
242
243     initrd, kernel_version = getKernelVersion(vars, log)
244
245     # get the kernel version we are assuming
246     if kernel_version is None:
247         try:
248             kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
249         except OSError, e:
250             return
251
252         if len(kernel_version) == 0:
253             return
254
255         if len(kernel_version) > 1:
256             print( "WARNING: We may be returning modules for the wrong kernel." )
257
258         kernel_version= kernel_version[0]
259
260     print( "Using kernel version %s" % kernel_version )
261
262     # test to make sure the file we need is present
263     modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
264                           (SYSIMG_PATH,kernel_version)
265     if not os.access(modules_pcimap_path,os.R_OK):
266         print( "Unable to read %s" % path )
267         return
268
269     pcimap = pypcimap.PCIMap(modules_pcimap_path)
270
271     # this is the actual data structure we return
272     system_mods= {}
273
274     # these are the lists that will be in system_mods
275     network_mods= []
276     scsi_mods= []
277
278     # XXX: this is really similar to what BootCD/conf_files/pl_hwinit does. merge?
279     pcidevs = get_devices()
280
281     devlist=pcidevs.keys()
282     devlist.sort()
283     for slot in devlist:
284         dev = pcidevs[slot]
285         base = (dev[4] & 0xff0000) >> 16
286         if base not in (PCI_BASE_CLASS_STORAGE,
287                         PCI_BASE_CLASS_NETWORK):
288             continue
289
290         modules = pcimap.get(dev)
291         if len(modules) > 0:
292             if base == PCI_BASE_CLASS_NETWORK:
293                 network_mods += modules
294             elif base == PCI_BASE_CLASS_STORAGE:
295                 scsi_mods += modules
296
297     system_mods[MODULE_CLASS_SCSI]= scsi_mods
298     system_mods[MODULE_CLASS_NETWORK]= network_mods
299
300     return system_mods
301
302
303 def getKernelVersion( vars = {} , log = sys.stderr):
304     # make sure we have the variables we need
305     try:
306         SYSIMG_PATH= vars["SYSIMG_PATH"]
307         if SYSIMG_PATH == "":
308             raise ValueError, "SYSIMG_PATH"
309
310         NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
311     except KeyError, var:
312         raise BootManagerException, "Missing variable in vars: %s\n" % var
313     except ValueError, var:
314         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
315
316     option = ''
317     if NODE_MODEL_OPTIONS & ModelOptions.SMP:
318         option = 'smp'
319         try:
320             os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
321             os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
322         except OSError, e:
323             # smp kernel is not there; remove option from modeloptions
324             # such that the rest of the code base thinks we are just
325             # using the base kernel.
326             NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
327             vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
328             log.write( "WARNING: Couldn't locate smp kernel.\n")
329             option = ''
330     try:
331         initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
332         kernel_version= initrd.replace("initrd-", "").replace(".img", "")    
333     except OSError, e:
334         initrd = None
335         kernel_version = None
336         
337     return (initrd, kernel_version)
338
339
340 if __name__ == "__main__":
341     devices= get_block_device_list()
342     print "block devices detected:"
343     if not devices:
344         print "no devices found!"
345     else:
346         for dev in devices.keys():
347             print "%s %s" % (dev, repr(devices[dev]))
348             
349
350     print ""
351     memory= get_total_phsyical_mem()
352     if not memory:
353         print "unable to read /proc/meminfo for memory"
354     else:
355         print "total physical memory: %d kb" % memory
356         
357
358     print ""
359
360     kernel_version = None
361     if len(sys.argv) > 2:
362         kernel_version = sys.argv[1]
363         
364     modules= get_system_modules()
365     if not modules:
366         print "unable to list system modules"
367     else:
368         for type in modules:
369             if type == MODULE_CLASS_SCSI:
370                 print( "all scsi modules:" )
371                 for a_mod in modules[type]:
372                     print a_mod
373             elif type == MODULE_CLASS_NETWORK:
374                 print( "all network modules:" )
375                 for a_mod in modules[type]:
376                     print a_mod
377