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