Revert "Revert "replace deprecated popen2 with subprocess""
[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         prog = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
138     except OSError:
139         raise BootManagerException, \
140               "Unable to create instance of subprocess.Popen " \
141               "for command: %s" % cmd
142     try:
143         (stdoutdata, stderrdata) = prog.communicate()
144     except KeyboardInterrupt:
145         raise BootManagerException, "Interrupted by user"
146
147     if log is not None:
148         log.write(stdoutdata)
149
150     returncode = prog.wait()
151
152     if fsck:
153        # The exit code returned by fsck is the sum of the following conditions:
154        #      0    - No errors
155        #      1    - File system errors corrected
156        #      2    - System should be rebooted
157        #      4    - File system errors left uncorrected
158        #      8    - Operational error
159        #      16   - Usage or syntax error
160        #      32   - Fsck canceled by user request
161        #      128  - Shared library error
162        if returncode != 0 and returncode != 1:
163             raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode)
164     else:
165         if returncode != 0:
166             raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode)
167
168     prog = None
169     return 1
170
171
172 def sysexec_chroot( path, cmd, log= None ):
173     """
174     same as sysexec, but inside a chroot
175     """
176     preload = ""
177     release = os.uname()[2]
178     # 2.6.12 kernels need this
179     if release[:5] == "2.6.1":
180         library = "%s/lib/libc-opendir-hack.so" % path
181         if not os.path.exists(library):
182             shutil.copy("./libc-opendir-hack.so", library)
183         preload = "/bin/env LD_PRELOAD=/lib/libc-opendir-hack.so"
184     sysexec("chroot %s %s %s" % (path, preload, cmd), log)
185
186
187 def sysexec_chroot_noerr( path, cmd, log= None ):
188     """
189     same as sysexec_chroot, but capture boot manager exceptions
190     """
191     try:
192         rc= 0
193         rc= syexec_chroot( cmd, log )
194     except BootManagerException, e:
195         pass
196
197     return rc
198
199
200 def sysexec_noerr( cmd, log= None ):
201     """
202     same as sysexec, but capture boot manager exceptions
203     """
204     try:
205         rc= 0
206         rc= sysexec( cmd, log )
207     except BootManagerException, e:
208         pass
209
210     return rc
211
212
213
214 def chdir( dir ):
215     """
216     change to a directory, return 1 if successful, a BootManagerException if failure
217     """
218     try:
219         os.chdir( dir )
220     except OSError:
221         raise BootManagerException, "Unable to change to directory: %s" % dir
222
223     return 1
224
225
226
227 def removefile( filepath ):
228     """
229     removes a file, return 1 if successful, 0 if failure
230     """
231     try:
232         os.remove( filepath )
233     except OSError:
234         raise BootManagerException, "Unable to remove file: %s" % filepath
235
236     return 1
237
238
239
240 # from: http://forums.devshed.com/archive/t-51149/
241 #              Ethernet-card-address-Through-Python-or-C
242
243 def hexy(n):
244     return "%02x" % (ord(n))
245
246 def get_mac_from_interface(ifname):
247     """
248     given a device name, like eth0, return its mac_address.
249     return None if the device doesn't exist.
250     """
251     
252     SIOCGIFHWADDR = 0x8927 # magic number
253
254     s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
255     ifname = string.strip(ifname)
256     ifr = ifname + '\0'*(32-len(ifname))
257
258     try:
259         r= fcntl.ioctl(s.fileno(),SIOCGIFHWADDR,ifr)
260         addr = map(hexy,r[18:24])
261         ret = (':'.join(map(str, addr)))
262     except IOError, e:
263         ret = None
264         
265     return ret
266
267 def check_file_hash(filename, hash_filename):
268     """Check the file's integrity with a given hash."""
269     return sha1_file(filename) == open(hash_filename).read().split()[0].strip()
270
271 def sha1_file(filename):
272     """Calculate sha1 hash of file."""
273     try:
274         try:
275             import hashlib
276             m = hashlib.sha1()
277         except:
278             import sha
279             m=sha.new()
280         f = file(filename, 'rb')
281         while True:
282             # 256 KB seems ideal for speed/memory tradeoff
283             # It wont get much faster with bigger blocks, but
284             # heap peak grows
285             block = f.read(256 * 1024)
286             if len(block) == 0:
287                 # end of file
288                 break
289             m.update(block)
290             # Simple trick to keep total heap even lower
291             # Delete the previous block, so while next one is read
292             # we wont have two allocated blocks with same size
293             del block
294         return m.hexdigest()
295     except IOError:
296         raise BootManagerException, "Cannot calculate SHA1 hash of %s" % filename