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