Use the PCI slot to order the interfaces.
[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     for slot in sorted(pcidevs.keys()):
282         dev = pcidevs[slot]
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 type in modules:
367             if type == MODULE_CLASS_SCSI:
368                 print( "all scsi modules:" )
369                 for a_mod in modules[type]:
370                     print a_mod
371             elif type == MODULE_CLASS_NETWORK:
372                 print( "all network modules:" )
373                 for a_mod in modules[type]:
374                     print a_mod
375