X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=source%2Futils.py;h=c97d8be28c95f629347a75e781e95a143f39d622;hb=67804cf640321ef514677de8f5f925d71ab45707;hp=5da5fcdf657adab7c593a36eb7b48a75609656e7;hpb=874e2525a59815be6a39e12f580337f9c44ff6cb;p=bootmanager.git diff --git a/source/utils.py b/source/utils.py index 5da5fcd..c97d8be 100644 --- a/source/utils.py +++ b/source/utils.py @@ -1,56 +1,91 @@ +# # Copyright (c) 2003 Intel Corporation # All rights reserved. +# +# Copyright (c) 2004-2006 The Trustees of Princeton University +# All rights reserved. +# expected /proc/partitions format + +# pylint: disable=c0111 -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: - -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. - -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. - -# * Neither the name of the Intel Corporation nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF -# YOUR JURISDICTION. It is licensee's responsibility to comply with any -# export regulations applicable in licensee's jurisdiction. Under -# CURRENT (May 2000) U.S. export regulations this software is eligible -# for export from the U.S. and can be downloaded by or otherwise -# exported or reexported worldwide EXCEPT to U.S. embargoed destinations -# which include Cuba, Iraq, Libya, North Korea, Iran, Syria, Sudan, -# Afghanistan and any other country to which the U.S. has embargoed -# goods and services. - - -import os, sys, shutil -import popen2 +from __future__ import print_function + +import sys +import os +import shutil +import subprocess +import shlex import socket import fcntl -import string -import exceptions +# handling breakpoints in the startup process +import select from Exceptions import * - -def makedirs( path ): +#################### +# the simplest way to debug is to let the node take off, +# ssh into it as root using the debug ssh key in /etc/planetlab +# then go to /tmp/source +# edit this file locally to turn on breakpoints if needed, then run +# ./BootManager.py +#################### + +### global debugging settings + +# enabling this will cause the node to ask for breakpoint-mode at startup +# production code should read False/False +PROMPT_MODE = False +# default for when prompt is turned off, or it's on but the timeout triggers +BREAKPOINT_MODE = False + +# verbose mode is just fine +VERBOSE_MODE = True +# in seconds : if no input, proceed +PROMPT_TIMEOUT = 5 + +def prompt_for_breakpoint_mode(): + + global BREAKPOINT_MODE + if PROMPT_MODE: + default_answer = BREAKPOINT_MODE + answer = '' + if BREAKPOINT_MODE: + display = "[y]/n" + else: + display = "y/[n]" + sys.stdout.write("Want to run in breakpoint mode ? {} ".format(display)) + sys.stdout.flush() + r, w, e = select.select([sys.stdin], [], [], PROMPT_TIMEOUT) + if r: + answer = sys.stdin.readline().strip() + else: + sys.stdout.write("\nTimed-out ({}s)".format(PROMPT_TIMEOUT)) + if answer: + BREAKPOINT_MODE = (answer in 'yY') + else: + BREAKPOINT_MODE = default_answer + label = "Off" + if BREAKPOINT_MODE: + label = "On" + sys.stdout.write("\nCurrent BREAKPOINT_MODE is {}\n".format(label)) + +def breakpoint (message, cmd=None): + + if BREAKPOINT_MODE: + + if cmd is None: + cmd = "/bin/sh" + message = message + " -- Entering bash - type ^D to proceed" + + print(message) + os.system(cmd) + + else: + print("Ignoring breakpoint (BREAKPOINT_MODE=False) : {}".format(message)) + + +######################################## +def makedirs(path): """ from python docs for os.makedirs: Throws an error exception if the leaf directory @@ -63,38 +98,37 @@ def makedirs( path ): otherwise. Does not test the writability of said directory. """ try: - os.makedirs( path ) + os.makedirs(path) except OSError: pass try: - os.listdir( path ) + os.listdir(path) except OSError: - raise BootManagerException, "Unable to create directory tree: %s" % path - + raise BootManagerException("Unable to create directory tree: {}".format(path)) + return 1 -def removedir( path ): +def removedir(path): """ remove a directory tree, return 1 if successful, a BootManagerException if failure. """ try: - os.listdir( path ) + os.listdir(path) except OSError: return 1 try: - shutil.rmtree( path ) - except OSError, desc: - raise BootManagerException, "Unable to remove directory tree: %s" % path - - return 1 + shutil.rmtree(path) + except OSError: + raise BootManagerException("Unable to remove directory tree: {}".format(path)) + return 1 -def sysexec( cmd, log= None ): +def sysexec(cmd, log=None, fsck=False, shell=False): """ execute a system command, output the results to the logger if log <> None @@ -103,62 +137,116 @@ def sysexec( cmd, log= None ): 0 if failed. A BootManagerException is raised if the command was unable to execute or was interrupted by the user with Ctrl+C """ - prog= popen2.Popen4( cmd, 0 ) - if prog is None: - raise BootManagerException, \ - "Unable to create instance of popen2.Popen3 " \ - "for command: %s" % cmd + try: + # Thierry - Jan. 6 2011 + # would probably make sense to look for | here as well + # however this is fragile and hard to test thoroughly + # let the caller set 'shell' when that is desirable + if shell or cmd.__contains__(">"): + prog = subprocess.Popen(cmd, shell=True) + if log is not None: + log.write("sysexec (shell mode) >>> {}".format(cmd)) + if VERBOSE_MODE: + print("sysexec (shell mode) >>> {}".format(cmd)) + else: + prog = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if log is not None: + log.write("sysexec >>> {}\n".format(cmd)) + if VERBOSE_MODE: + print("sysexec >>> {}".format(cmd)) + except OSError: + raise BootManagerException( + "Unable to create instance of subprocess.Popen " + "for command: {}".format(cmd)) + try: + (stdoutdata, stderrdata) = prog.communicate() + except KeyboardInterrupt: + raise BootManagerException("Interrupted by user") + # log stdout & stderr if log is not None: - try: - for line in prog.fromchild: - log.write( line ) - except KeyboardInterrupt: - raise BootManagerException, "Interrupted by user" + if stdoutdata: + log.write("==========stdout\n" + stdoutdata) + if stderrdata: + log.write("==========stderr\n" + stderrdata) + + returncode = prog.wait() + + if fsck: + # The exit code returned by fsck is the sum of the following conditions: + # 0 - No errors + # 1 - File system errors corrected + # 2 - System should be rebooted + # 4 - File system errors left uncorrected + # 8 - Operational error + # 16 - Usage or syntax error + # 32 - Fsck canceled by user request + # 128 - Shared library error + if returncode not in (0, 1): + raise BootManagerException("Running {} failed (rc={})".format(cmd, returncode)) + else: + if returncode != 0: + raise BootManagerException("Running {} failed (rc={})".format(cmd, returncode)) + + prog = None + return 1 - returncode= prog.wait() - if returncode != 0: - raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode) - prog= None - return 1 +def sysexec_chroot(path, cmd, log=None, shell=False): + """ + same as sysexec, but inside a chroot + """ + return sysexec("chroot {} {}".format(path, cmd), log, shell=shell) -def sysexec_noerr( cmd, log= None ): +def sysexec_chroot_noerr(path, cmd, log=None, shell=False): + """ + same as sysexec_chroot, but capture boot manager exceptions + """ + try: + rc = 0 + rc = sysexec_chroot(path, cmd, log, shell=shell) + except BootManagerException as e: + pass + + return rc + + +def sysexec_noerr(cmd, log=None, shell=False): """ same as sysexec, but capture boot manager exceptions """ try: - rc= 0 - rc= sysexec( cmd, log ) - except BootManagerException, e: + rc = 0 + rc = sysexec(cmd, log, shell=shell) + except BootManagerException as e: pass return rc -def chdir( dir ): +def chdir(dir): """ change to a directory, return 1 if successful, a BootManagerException if failure """ try: - os.chdir( dir ) + os.chdir(dir) except OSError: - raise BootManagerException, "Unable to change to directory: %s" % dir + raise BootManagerException("Unable to change to directory: {}".format(dir)) return 1 -def removefile( filepath ): +def removefile(filepath): """ removes a file, return 1 if successful, 0 if failure """ try: - os.remove( filepath ) + os.remove(filepath) except OSError: - raise BootManagerException, "Unable to remove file: %s" % filepath + raise BootManagerException("Unable to remove file: {}".format(filepath)) return 1 @@ -167,27 +255,62 @@ def removefile( filepath ): # from: http://forums.devshed.com/archive/t-51149/ # Ethernet-card-address-Through-Python-or-C -def hexy(n): - return "%02x" % (ord(n)) - def get_mac_from_interface(ifname): """ given a device name, like eth0, return its mac_address. return None if the device doesn't exist. """ - + SIOCGIFHWADDR = 0x8927 # magic number - s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) - ifname = string.strip(ifname) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ifname = ifname.strip() ifr = ifname + '\0'*(32-len(ifname)) try: - r= fcntl.ioctl(s.fileno(),SIOCGIFHWADDR,ifr) - addr = map(hexy,r[18:24]) - ret = (':'.join(map(str, addr))) - except IOError, e: + r = fcntl.ioctl(s.fileno(), SIOCGIFHWADDR,ifr) + ret = ':'.join(["{:02x}".format(ord(n)) for n in r[18:24]]) + except IOError as e: ret = None - + return ret +def check_file_hash(filename, hash_filename): + """Check the file's integrity with a given hash.""" + return sha1_file(filename) == open(hash_filename).read().split()[0].strip() + +def sha1_file(filename): + """Calculate sha1 hash of file.""" + try: + try: + import hashlib + m = hashlib.sha1() + except: + import sha + m = sha.new() + f = file(filename, 'rb') + while True: + # 256 KB seems ideal for speed/memory tradeoff + # It wont get much faster with bigger blocks, but + # heap peak grows + block = f.read(256 * 1024) + if len(block) == 0: + # end of file + break + m.update(block) + # Simple trick to keep total heap even lower + # Delete the previous block, so while next one is read + # we wont have two allocated blocks with same size + del block + return m.hexdigest() + except IOError: + raise BootManagerException("Cannot calculate SHA1 hash of {}".format(filename)) + +def display_disks_status(PARTITIONS, message, log): + log.write("{} - PARTITIONS status - BEG\n".format(message)) + sysexec_noerr("vgdisplay", log) + sysexec_noerr("pvdisplay", log) + for name, path in PARTITIONS.items(): + log.write("PARTITIONS[{}]={}\n".format(name,path)) + sysexec_noerr("ls -l {}".format(path), log) + log.write("{} - PARTITIONS status - END\n".format(message))