stdoutdata can be None object
[bootmanager.git] / source / utils.py
1 #!/usr/bin/python
2 #
3 # $Id$
4 # $URL$
5 #
6 # Copyright (c) 2003 Intel Corporation
7 # All rights reserved.
8 #
9 # Copyright (c) 2004-2006 The Trustees of Princeton University
10 # All rights reserved.
11 # expected /proc/partitions format
12
13 import os, sys, shutil
14 import subprocess
15 import shlex
16 import socket
17 import fcntl
18 import string
19 import exceptions
20
21 from Exceptions import *
22
23
24 ### handling breakpoints in the startup process
25 import select, sys, string
26
27 ### global debug settings
28 # NOTE. when BREAKPOINT_MODE turns out enabled,
29 # you have to attend the boot phase, that would hang otherwise 
30
31 # enabling this will cause the node to ask for breakpoint-mode at startup
32 # production code should read False/False
33 PROMPT_MODE=False
34 # default for when prompt is turned off, or it's on but the timeout triggers
35 BREAKPOINT_MODE=False
36 VERBOSE_MODE=False
37 VERBOSE_MODE=True
38 # in seconds : if no input, proceed
39 PROMPT_TIMEOUT=5
40
41 def prompt_for_breakpoint_mode ():
42
43     global BREAKPOINT_MODE
44     if PROMPT_MODE:
45         default_answer=BREAKPOINT_MODE
46         answer=''
47         if BREAKPOINT_MODE:
48             display="[y]/n"
49         else:
50             display="y/[n]"
51         sys.stdout.write ("Want to run in breakpoint mode ? %s "%display)
52         sys.stdout.flush()
53         r,w,e = select.select ([sys.stdin],[],[],PROMPT_TIMEOUT)
54         if r:
55             answer = string.strip(sys.stdin.readline())
56         else:
57             sys.stdout.write("\nTimed-out (%d s)"%PROMPT_TIMEOUT)
58         if answer:
59             BREAKPOINT_MODE = ( answer == "y" or answer == "Y")
60         else:
61             BREAKPOINT_MODE = default_answer
62     label="Off"
63     if BREAKPOINT_MODE:
64         label="On"
65     sys.stdout.write("\nCurrent BREAKPOINT_MODE is %s\n"%label)
66
67 def breakpoint (message, cmd = None):
68
69     if BREAKPOINT_MODE:
70
71         if cmd is None:
72             cmd="/bin/sh"
73             message=message+" -- Entering bash - type ^D to proceed"
74
75         print message
76         os.system(cmd)
77
78
79 ########################################
80 def makedirs( path ):
81     """
82     from python docs for os.makedirs:
83     Throws an error exception if the leaf directory
84     already exists or cannot be created.
85
86     That is real useful. Instead, we'll create the directory, then use a
87     separate function to test for its existance.
88
89     Return 1 if the directory exists and/or has been created, a BootManagerException
90     otherwise. Does not test the writability of said directory.
91     """
92     try:
93         os.makedirs( path )
94     except OSError:
95         pass
96     try:
97         os.listdir( path )
98     except OSError:
99         raise BootManagerException, "Unable to create directory tree: %s" % path
100     
101     return 1
102
103
104
105 def removedir( path ):
106     """
107     remove a directory tree, return 1 if successful, a BootManagerException
108     if failure.
109     """
110     try:
111         os.listdir( path )
112     except OSError:
113         return 1
114
115     try:
116         shutil.rmtree( path )
117     except OSError, desc:
118         raise BootManagerException, "Unable to remove directory tree: %s" % path
119     
120     return 1
121
122
123
124 def sysexec( cmd, log= None, fsck = False ):
125     """
126     execute a system command, output the results to the logger
127     if log <> None
128
129     return 1 if command completed (return code of non-zero),
130     0 if failed. A BootManagerException is raised if the command
131     was unable to execute or was interrupted by the user with Ctrl+C
132     """
133     if VERBOSE_MODE:
134         print ("sysexec >>> %s" % cmd)
135
136     try:
137         if cmd.__contains__(">"):
138             prog = subprocess.Popen(cmd, shell=True)
139         else:
140             prog = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
141     except OSError:
142         raise BootManagerException, \
143               "Unable to create instance of subprocess.Popen " \
144               "for command: %s" % cmd
145     try:
146         (stdoutdata, stderrdata) = prog.communicate()
147     except KeyboardInterrupt:
148         raise BootManagerException, "Interrupted by user"
149
150     if log is not None:
151         if stdoutdata is not None:
152             log.write(stdoutdata)
153
154     returncode = prog.wait()
155
156     if fsck:
157        # The exit code returned by fsck is the sum of the following conditions:
158        #      0    - No errors
159        #      1    - File system errors corrected
160        #      2    - System should be rebooted
161        #      4    - File system errors left uncorrected
162        #      8    - Operational error
163        #      16   - Usage or syntax error
164        #      32   - Fsck canceled by user request
165        #      128  - Shared library error
166        if returncode != 0 and returncode != 1:
167             raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode)
168     else:
169         if returncode != 0:
170             raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode)
171
172     prog = None
173     return 1
174
175
176 def sysexec_chroot( path, cmd, log= None ):
177     """
178     same as sysexec, but inside a chroot
179     """
180     preload = ""
181     release = os.uname()[2]
182     # 2.6.12 kernels need this
183     if release[:5] == "2.6.1":
184         library = "%s/lib/libc-opendir-hack.so" % path
185         if not os.path.exists(library):
186             shutil.copy("./libc-opendir-hack.so", library)
187         preload = "/bin/env LD_PRELOAD=/lib/libc-opendir-hack.so"
188     sysexec("chroot %s %s %s" % (path, preload, cmd), log)
189
190
191 def sysexec_chroot_noerr( path, cmd, log= None ):
192     """
193     same as sysexec_chroot, but capture boot manager exceptions
194     """
195     try:
196         rc= 0
197         rc= syexec_chroot( cmd, log )
198     except BootManagerException, e:
199         pass
200
201     return rc
202
203
204 def sysexec_noerr( cmd, log= None ):
205     """
206     same as sysexec, but capture boot manager exceptions
207     """
208     try:
209         rc= 0
210         rc= sysexec( cmd, log )
211     except BootManagerException, e:
212         pass
213
214     return rc
215
216
217
218 def chdir( dir ):
219     """
220     change to a directory, return 1 if successful, a BootManagerException if failure
221     """
222     try:
223         os.chdir( dir )
224     except OSError:
225         raise BootManagerException, "Unable to change to directory: %s" % dir
226
227     return 1
228
229
230
231 def removefile( filepath ):
232     """
233     removes a file, return 1 if successful, 0 if failure
234     """
235     try:
236         os.remove( filepath )
237     except OSError:
238         raise BootManagerException, "Unable to remove file: %s" % filepath
239
240     return 1
241
242
243
244 # from: http://forums.devshed.com/archive/t-51149/
245 #              Ethernet-card-address-Through-Python-or-C
246
247 def hexy(n):
248     return "%02x" % (ord(n))
249
250 def get_mac_from_interface(ifname):
251     """
252     given a device name, like eth0, return its mac_address.
253     return None if the device doesn't exist.
254     """
255     
256     SIOCGIFHWADDR = 0x8927 # magic number
257
258     s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
259     ifname = string.strip(ifname)
260     ifr = ifname + '\0'*(32-len(ifname))
261
262     try:
263         r= fcntl.ioctl(s.fileno(),SIOCGIFHWADDR,ifr)
264         addr = map(hexy,r[18:24])
265         ret = (':'.join(map(str, addr)))
266     except IOError, e:
267         ret = None
268         
269     return ret
270
271 def check_file_hash(filename, hash_filename):
272     """Check the file's integrity with a given hash."""
273     return sha1_file(filename) == open(hash_filename).read().split()[0].strip()
274
275 def sha1_file(filename):
276     """Calculate sha1 hash of file."""
277     try:
278         try:
279             import hashlib
280             m = hashlib.sha1()
281         except:
282             import sha
283             m=sha.new()
284         f = file(filename, 'rb')
285         while True:
286             # 256 KB seems ideal for speed/memory tradeoff
287             # It wont get much faster with bigger blocks, but
288             # heap peak grows
289             block = f.read(256 * 1024)
290             if len(block) == 0:
291                 # end of file
292                 break
293             m.update(block)
294             # Simple trick to keep total heap even lower
295             # Delete the previous block, so while next one is read
296             # we wont have two allocated blocks with same size
297             del block
298         return m.hexdigest()
299     except IOError:
300         raise BootManagerException, "Cannot calculate SHA1 hash of %s" % filename