Took out the get_devices code and moved it to pypcilib. Changed import
[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 # -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 PCI_ANY=0xffffffffL
59
60 def get_total_phsyical_mem(vars = {}, log = sys.stderr):
61     """
62     return the total physical memory of the machine, in kilobytes.
63
64     Return None if /proc/meminfo not readable.
65     """
66
67     try:
68         meminfo_file= file(PROC_MEMINFO_PATH,"r")
69     except IOError, e:
70         return
71
72     total_memory= None
73
74     for line in meminfo_file:
75
76         try:
77             (fieldname,value)= string.split(line,":")
78         except ValueError, e:
79             # this will happen for lines that don't have two values
80             # (like the first line on 2.4 kernels)
81             continue
82
83         fieldname= string.strip(fieldname)
84         value= string.strip(value)
85         
86         if fieldname == "MemTotal":
87             try:
88                 (total_memory,units)= string.split(value)
89             except ValueError, e:
90                 return
91
92             if total_memory == "" or total_memory == None or \
93                    units == "" or units == None:
94                 return
95
96             if string.lower(units) != "kb":
97                 return
98
99             try:
100                 total_memory= int(total_memory)
101             except ValueError, e:
102                 return
103
104             break
105
106     meminfo_file.close()
107     return total_memory
108
109 def get_block_device_list(vars = {}, log = sys.stderr):
110     """
111     get a list of block devices from this system.
112     return an associative array, where the device name
113     (full /dev/device path) is the key, and the value
114     is a tuple of (major,minor,numblocks,gb_size,readonly)
115     """
116
117     # make sure we can access to the files/directories in /proc
118     if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
119         return None
120
121     # table with valid scsi/sata/ide/raid block device names
122     valid_blk_names = {}
123     # add in valid sd and hd block device names
124     for blk_prefix in ('sd','hd'):
125         for blk_num in map ( \
126             lambda x: chr(x), range(ord('a'),ord('z')+1)):
127             devicename="%s%c" % (blk_prefix, blk_num)
128             valid_blk_names[devicename]=None
129
130     # add in valid scsi raid block device names
131     for M in range(0,1+1):
132         for N in range(0,7+1):
133             devicename = "cciss/c%dd%d" % (M,N)
134             valid_blk_names[devicename]=None
135
136     for devicename in valid_blk_names.keys():
137         # devfs under 2.4 (old boot cds) used to list partitions
138         # in a format such as scsi/host0/bus0/target0/lun0/disc
139         # and /dev/sda, etc. were just symlinks
140         try:
141             devfsname= os.readlink( "/dev/%s" % devicename )
142             valid_blk_names[devfsname]=None
143         except OSError:
144             pass
145
146     # only do this once every system boot
147     if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
148
149         # this is ugly. under devfs, device
150         # entries in /dev/scsi/.. and /dev/ide/...
151         # don't show up until you attempt to read
152         # from the associated device at /dev (/dev/sda).
153         # so, lets run sfdisk -l (list partitions) against
154         # most possible block devices, that way they show
155         # up when it comes time to do the install.
156         devicenames = valid_blk_names.keys()
157         devicenames.sort()
158         for devicename in devicenames:
159             os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
160
161         # touch file
162         fb = open(DEVICES_SCANNED_FLAG,"w")
163         fb.close()
164
165     devicelist= {}
166
167     partitions_file= file(PROC_PARTITIONS_PATH,"r")
168     line_count= 0
169     for line in partitions_file:
170         line_count= line_count + 1
171
172         # skip the first two lines always
173         if line_count < 2:
174             continue
175
176         parts= string.split(line)
177
178         if len(parts) < 4:
179             continue
180
181         device= parts[3]
182
183         # skip and ignore any partitions
184         if not valid_blk_names.has_key(device):
185             continue
186
187         try:
188             major= int(parts[0])
189             minor= int(parts[1])
190             blocks= int(parts[2])
191         except ValueError, err:
192             continue
193
194         gb_size= blocks/BLOCKS_PER_GB
195
196         # check to see if the blk device is readonly
197         try:
198             # can we write to it?
199             dev_name= "/dev/%s" % device
200             fb = open(dev_name,"w")
201             fb.close()
202             readonly=False
203         except IOError, e:
204             # check if EROFS errno
205             if errno.errorcode.get(e.errno,None) == 'EROFS':
206                 readonly=True
207             else:
208                 # got some other errno, pretend device is readonly
209                 readonly=True
210
211         devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
212
213     return devicelist
214
215
216 def get_system_modules( vars = {}, log = sys.stderr):
217     """
218     Return a list of kernel modules that this system requires.
219     This requires access to the installed system's root
220     directory, as the following file must exist and is used:
221     <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
222
223     If there are more than one kernels installed, and the kernel
224     version is not specified, then only the first one in
225     /lib/modules is used.
226
227     Returns a dictionary, keys being the type of module:
228         - scsi       MODULE_CLASS_SCSI
229         - network    MODULE_CLASS_NETWORK
230     The value being the kernel module name to load.
231
232     Some sata devices show up under an IDE device class,
233     hence the reason for checking for ide devices as well.
234     If there actually is a match in the pci -> module lookup
235     table, and its an ide device, its most likely sata,
236     as ide modules are built in to the kernel.
237     """
238
239     if not vars.has_key("SYSIMG_PATH"):
240         vars["SYSIMG_PATH"]="/"
241     SYSIMG_PATH=vars["SYSIMG_PATH"]
242
243     if not vars.has_key("NODE_MODEL_OPTIONS"):
244         vars["NODE_MODEL_OPTIONS"] = 0;
245
246     initrd, kernel_version = getKernelVersion(vars, log)
247
248     # get the kernel version we are assuming
249     if kernel_version is None:
250         try:
251             kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
252         except OSError, e:
253             return
254
255         if len(kernel_version) == 0:
256             return
257
258         if len(kernel_version) > 1:
259             print( "WARNING: We may be returning modules for the wrong kernel." )
260
261         kernel_version= kernel_version[0]
262
263     print( "Using kernel version %s" % kernel_version )
264
265     # test to make sure the file we need is present
266     modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
267                           (SYSIMG_PATH,kernel_version)
268     if not os.access(modules_pcimap_path,os.R_OK):
269         print( "Unable to read %s" % path )
270         return
271
272     pcimap = pypcimap.PCIMap(modules_pcimap_path)
273
274     # this is the actual data structure we return
275     system_mods= {}
276
277     # these are the lists that will be in system_mods
278     network_mods= []
279     scsi_mods= []
280
281     # XXX: this is really similar to what BootCD/conf_files/pl_hwinit does. merge?
282     pcidevs = get_devices()
283
284     for (slot, dev) in pcidevs.iteritems():
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