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