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