merge to HEAD as of 2006-08-21
[bootmanager.git] / source / steps / CheckHardwareRequirements.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 import os
11 import popen2
12 import string
13
14 import systeminfo
15 from Exceptions import *
16 import utils
17 import notify_messages
18 import BootAPI
19
20
21 def Run( vars, log ):
22     """
23     Make sure the hardware we are running on is sufficient for
24     the PlanetLab OS to be installed on. In the process, identify
25     the list of block devices that may be used for a node installation,
26     and identify the cdrom device that we booted off of.
27
28     Return 1 if requiremenst met, 0 if requirements not met. Raise
29     BootManagerException if any problems occur that prevent the requirements
30     from being checked.
31
32     Expect the following variables from the store:
33
34     MINIMUM_MEMORY          minimum amount of memory in kb required
35                             for install
36     NODE_ID                 the node_id from the database for this node
37     MINIMUM_DISK_SIZE       any disks smaller than this size, in GB, are not used
38     TOTAL_MINIMUM_DISK_SIZE total disk size in GB, if all usable disks
39                             meet this number, there isn't enough disk space for
40                             this node to be usable after install
41     SKIP_HARDWARE_REQUIREMENT_CHECK
42                             If set, don't check if minimum requirements are met
43     BOOT_CD_VERSION          A tuple of the current bootcd version                            
44     Sets the following variables:
45     INSTALL_BLOCK_DEVICES    list of block devices to install onto
46     """
47
48     log.write( "\n\nStep: Checking if hardware requirements met.\n" )        
49         
50     try:
51         MINIMUM_MEMORY= int(vars["MINIMUM_MEMORY"])
52         if MINIMUM_MEMORY == "":
53             raise ValueError, "MINIMUM_MEMORY"
54
55         NODE_ID= vars["NODE_ID"]
56         if NODE_ID == "":
57             raise ValueError("NODE_ID")
58
59         MINIMUM_DISK_SIZE= int(vars["MINIMUM_DISK_SIZE"])
60
61         TOTAL_MINIMUM_DISK_SIZE= \
62                    int(vars["TOTAL_MINIMUM_DISK_SIZE"])
63
64         SKIP_HARDWARE_REQUIREMENT_CHECK= \
65                    int(vars["SKIP_HARDWARE_REQUIREMENT_CHECK"])
66         
67         BOOT_CD_VERSION= vars["BOOT_CD_VERSION"]
68         if BOOT_CD_VERSION == "":
69             raise ValueError, "BOOT_CD_VERSION"
70
71     except KeyError, var:
72         raise BootManagerException, \
73               "Missing variable in install store: %s" % var
74     except ValueError, var:
75         raise BootManagerException, \
76               "Variable in install store blank, shouldn't be: %s" % var
77
78     # lets see if we have enough memory to run
79     log.write( "Checking for available memory.\n" )
80
81     total_mem= systeminfo.get_total_phsyical_mem(vars, log)
82     if total_mem is None:
83         raise BootManagerException, "Unable to read total physical memory"
84         
85     if total_mem < MINIMUM_MEMORY:
86         if not SKIP_HARDWARE_REQUIREMENT_CHECK:
87             log.write( "Insufficient memory to run node: %s kb\n" % total_mem )
88             log.write( "Required memory: %s kb\n" % MINIMUM_MEMORY )
89
90             include_pis= 0
91             include_techs= 1
92             include_support= 0
93             
94             sent= 0
95             try:
96                 sent= BootAPI.call_api_function( vars, "BootNotifyOwners",
97                                          (notify_messages.MSG_INSUFFICIENT_MEMORY,
98                                           include_pis,
99                                           include_techs,
100                                           include_support) )
101             except BootManagerException, e:
102                 log.write( "Call to BootNotifyOwners failed: %s.\n" % e )
103                 
104             if sent == 0:
105                 log.write( "Unable to notify site contacts of problem.\n" )
106             else:
107                 log.write( "Notified contacts of problem.\n" )
108                 
109             return 0
110         else:
111             log.write( "Memory requirements not met, but running anyway: %s kb\n"
112                        % total_mem )
113     else:
114         log.write( "Looks like we have enough memory: %s kb\n" % total_mem )
115
116
117
118     # get a list of block devices to attempt to install on
119     # (may include cdrom devices)
120     install_devices= systeminfo.get_block_device_list(vars, log)
121
122     # save the list of block devices in the log
123     log.write( "Detected block devices:\n" )
124     log.write( repr(install_devices) + "\n" )
125
126     if not install_devices or len(install_devices) == 0:
127         log.write( "No block devices detected.\n" )
128         
129         include_pis= 0
130         include_techs= 1
131         include_support= 0
132         
133         sent= 0
134         try:
135             sent= BootAPI.call_api_function( vars, "BootNotifyOwners",
136                                        (notify_messages.MSG_INSUFFICIENT_DISK,
137                                         include_pis,
138                                         include_techs,
139                                         include_support) )
140         except BootManagerException, e:
141             log.write( "Call to BootNotifyOwners failed: %s.\n" % e )
142             
143         if sent == 0:
144             log.write( "Unable to notify site contacts of problem.\n" )
145
146         return 0
147
148     # now, lets remove any block devices we know won't work (readonly,cdroms),
149     # or could be other writable removable disks (usb keychains, zip disks, etc)
150     # i'm not aware of anything that helps with the latter test, so,
151     # what we'll probably do is simply not use any block device below
152     # some size threshold (set in installstore)
153
154     # also, keep track of the total size for all devices that appear usable
155     total_size= 0
156
157     for device in install_devices.keys():
158
159         (major,minor,blocks,gb_size,readonly)= install_devices[device]
160         
161         # if the device string starts with
162         # planetlab or dm- (device mapper), ignore it (could be old lvm setup)
163         if device[:14] == "/dev/planetlab" or device[:8] == "/dev/dm-":
164             del install_devices[device]
165             continue
166
167         if gb_size < MINIMUM_DISK_SIZE:
168             log.write( "Device is too small to use: %s \n(appears" \
169                            " to be %4.2f GB)\n" % (device,gb_size) )
170             try:
171                 del install_devices[device]
172             except KeyError, e:
173                 pass
174             continue
175
176         if readonly:
177             log.write( "Device is readonly, not using: %s\n" % device )
178             try:
179                 del install_devices[device]
180             except KeyError, e:
181                 pass
182             continue
183             
184         # add this sector count to the total count of usable
185         # sectors we've found.
186         total_size= total_size + gb_size
187
188
189     if len(install_devices) == 0:
190         log.write( "No suitable block devices found for install.\n" )
191
192         include_pis= 0
193         include_techs= 1
194         include_support= 0
195         
196         sent= 0
197         try:
198             sent= BootAPI.call_api_function( vars, "BootNotifyOwners",
199                                        (notify_messages.MSG_INSUFFICIENT_DISK,
200                                         include_pis,
201                                         include_techs,
202                                         include_support) )
203         except BootManagerException, e:
204             log.write( "Call to BootNotifyOwners failed: %s.\n" % e )
205             
206         if sent == 0:
207             log.write( "Unable to notify site contacts of problem.\n" )
208
209         return 0
210
211
212     # show the devices we found that are usable
213     log.write( "Usable block devices:\n" )
214     log.write( repr(install_devices.keys()) + "\n" )
215
216     # save the list of devices for the following steps
217     vars["INSTALL_BLOCK_DEVICES"]= install_devices.keys()
218
219
220     # ensure the total disk size is large enough. if
221     # not, we need to email the tech contacts the problem, and
222     # put the node into debug mode.
223     if total_size < TOTAL_MINIMUM_DISK_SIZE:
224         if not SKIP_HARDWARE_REQUIREMENT_CHECK:
225             log.write( "The total usable disk size of all disks is " \
226                        "insufficient to be usable as a PlanetLab node.\n" )
227             include_pis= 0
228             include_techs= 1
229             include_support= 0
230             
231             sent= 0
232             try:
233                 sent= BootAPI.call_api_function( vars, "BootNotifyOwners",
234                                             (notify_messages.MSG_INSUFFICIENT_DISK,
235                                              include_pis,
236                                              include_techs,
237                                              include_support) )
238             except BootManagerException, e:
239                 log.write( "Call to BootNotifyOwners failed: %s.\n" % e )
240             
241             if sent == 0:
242                 log.write( "Unable to notify site contacts of problem.\n" )
243
244             return 0
245         
246         else:
247             log.write( "The total usable disk size of all disks is " \
248                        "insufficient, but running anyway.\n" )
249             
250     log.write( "Total size for all usable block devices: %4.2f GB\n" % total_size )
251
252     # turn off UDMA for all block devices on 2.x cds (2.4 kernel)
253     if BOOT_CD_VERSION[0] == 2:
254         for device in install_devices:
255             log.write( "Disabling UDMA on %s\n" % device )
256             utils.sysexec_noerr( "/sbin/hdparm -d0 %s" % device, log )
257
258     return 1