| #!/usr/bin/python | 
 | # Licensed to the Apache Software Foundation (ASF) under one | 
 | # or more contributor license agreements.  See the NOTICE file | 
 | # distributed with this work for additional information | 
 | # regarding copyright ownership.  The ASF licenses this file | 
 | # to you under the Apache License, Version 2.0 (the | 
 | # "License"); you may not use this file except in compliance | 
 | # with the License.  You may obtain a copy of the License at | 
 | #  | 
 | #   http://www.apache.org/licenses/LICENSE-2.0 | 
 | #  | 
 | # Unless required by applicable law or agreed to in writing, | 
 | # software distributed under the License is distributed on an | 
 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | # KIND, either express or implied.  See the License for the | 
 | # specific language governing permissions and limitations | 
 | # under the License. | 
 |  | 
 | import sys, os | 
 | from subprocess import PIPE, Popen | 
 | import logging | 
 | import traceback | 
 | from os.path import exists, join | 
 | from signal import alarm, signal, SIGALRM, SIGKILL | 
 |  | 
 | class CloudRuntimeException(Exception): | 
 |     def __init__(self, errMsg): | 
 |         self.errMsg = errMsg | 
 |     def __str__(self): | 
 |         return self.errMsg | 
 | def formatExceptionInfo(maxTBlevel=5): | 
 |     cla, exc, trbk = sys.exc_info() | 
 |     excTb = traceback.format_tb(trbk, maxTBlevel) | 
 |     msg = str(exc) + "\n" | 
 |     for tb in excTb: | 
 |         msg += tb | 
 |     return msg | 
 |  | 
 | class bash: | 
 |     def __init__(self, args, timeout=600): | 
 |         self.args = args | 
 |         logging.debug("execute:%s"%args) | 
 |         self.timeout = timeout | 
 |         self.process = None | 
 |         self.success = False | 
 |         self.run() | 
 |  | 
 |     def run(self): | 
 |         class Alarm(Exception): | 
 |             pass | 
 |         def alarm_handler(signum, frame): | 
 |             raise Alarm | 
 |  | 
 |         try: | 
 |             self.process = Popen(self.args, shell=True, stdout=PIPE, stderr=PIPE) | 
 |             if self.timeout != -1: | 
 |                 signal(SIGALRM, alarm_handler) | 
 |                 alarm(self.timeout) | 
 |  | 
 |             try: | 
 |                 self.stdout, self.stderr = self.process.communicate() | 
 |                 if self.timeout != -1: | 
 |                     alarm(0) | 
 |             except Alarm: | 
 |                 os.kill(self.process.pid, SIGKILL) | 
 |                 raise  CloudRuntimeException("Timeout during command execution") | 
 |  | 
 |             self.success = self.process.returncode == 0 | 
 |         except: | 
 |             raise  CloudRuntimeException(formatExceptionInfo()) | 
 |  | 
 | #        if not self.success:  | 
 | #            raise  CloudRuntimeException(self.getStderr()) | 
 |  | 
 |     def isSuccess(self): | 
 |         return self.success | 
 |      | 
 |     def getStdout(self): | 
 |         return self.stdout.strip("\n") | 
 |      | 
 |     def getLines(self): | 
 |         return self.stdout.split("\n") | 
 |  | 
 |     def getStderr(self): | 
 |         return self.stderr.strip("\n") | 
 |  | 
 |  | 
 | def initLoging(logFile=None): | 
 |     try: | 
 |         if logFile is None: | 
 |             logging.basicConfig(level=logging.DEBUG)  | 
 |         else:  | 
 |             logging.basicConfig(filename=logFile, level=logging.DEBUG)  | 
 |     except: | 
 |         logging.basicConfig(level=logging.DEBUG)  | 
 |  | 
 | def writeProgressBar(msg, result=None):     | 
 |     if msg is not None: | 
 |         output = "%-80s"%msg | 
 |     elif result is True: | 
 |         output = "[ \033[92m%-2s\033[0m ]\n"%"OK" | 
 |     elif result is False: | 
 |         output = "[ \033[91m%-6s\033[0m ]\n"%"FAILED" | 
 |     sys.stdout.write(output) | 
 |     sys.stdout.flush() | 
 |      | 
 | def printError(msg): | 
 |     sys.stderr.write(msg) | 
 |     sys.stderr.write("\n") | 
 |     sys.stderr.flush() | 
 |  | 
 | def printMsg(msg): | 
 |     sys.stdout.write(msg+"\n") | 
 |     sys.stdout.flush() | 
 |  | 
 | def checkRpm(pkgName): | 
 |     chkPkg = bash("rpm -q %s"%pkgName) | 
 |     writeProgressBar("Checking %s"%pkgName, None) | 
 |     if not chkPkg.isSuccess(): | 
 |         writeProgressBar(None, False) | 
 |         printError("%s is not found, please make sure it is installed. You may try 'yum install %s'\n"%(pkgName, pkgName)) | 
 |         return False | 
 |     else: | 
 |         writeProgressBar(None, True) | 
 |         return True | 
 |        | 
 | def checkEnv(): | 
 |    writeProgressBar("Checking is root") | 
 |    ret = bash("whoami") | 
 |    if ret.getStdout() != "root": | 
 |        writeProgressBar(None, False) | 
 |        printError("This script must run as root") | 
 |        return False | 
 |    else: | 
 |        writeProgressBar(None, True) | 
 |         | 
 |    pkgList = ['tftp-server', 'syslinux', 'xinetd', 'chkconfig', 'dhcp'] | 
 |    for pkg in pkgList: | 
 |        if not checkRpm(pkg): | 
 |            return False | 
 |    return True | 
 |  | 
 | def exitIfFail(ret): | 
 |     if not ret: sys.exit(1)  | 
 |      | 
 | def bashWithResult(cmd): | 
 |     writeProgressBar("Executing '%s'"%cmd) | 
 |     ret = bash(cmd) | 
 |     if not ret.isSuccess(): | 
 |         writeProgressBar(None, False) | 
 |         writeProgressBar(ret.getStderr() + '\n') | 
 |         return False | 
 |     else: | 
 |         writeProgressBar(None, True) | 
 |         return True | 
 |      | 
 | def configurePxeStuff():  | 
 |     stuff = ['tftp', 'xinetd', 'dhcpd'] | 
 |     cmds = ['chkconfig --level 345 %s on' % i for i in stuff] | 
 |     cmds.append('/etc/init.d/xinetd restart') | 
 |      | 
 |     for cmd in cmds: | 
 |         if not bashWithResult(cmd): return False | 
 |          | 
 |     chkIptable = bash('chkconfig --list iptables') | 
 |     if 'on' in chkIptable.getStdout(): | 
 |         printMsg("Detected iptables is running, need to open tftp port 69") | 
 |         if not bashWithResult('iptables -I INPUT 1 -p udp --dport 69 -j ACCEPT'): return False | 
 |         if not bashWithResult('/etc/init.d/iptables save'): return False | 
 |          | 
 |     return True   | 
 |      | 
 | def getTftpRootDir(tftpRootDirList): | 
 |     tftpRoot = bash("cat /etc/xinetd.d/tftp | grep server_args") | 
 |     if not tftpRoot.isSuccess(): | 
 |         printError("Cannot get tftp root directory from /etc/xinetd.d/tftp, here may be something wrong with your tftp-server, try reinstall it\n") | 
 |         return False | 
 |     tftpRootDir = tftpRoot.getStdout() | 
 |     index = tftpRootDir.find("/") | 
 |     if index == -1: | 
 |         printError("Wrong server_arg in /etc/xinetd.d/tftp (%s)"%tftpRootDir) | 
 |         return False | 
 |     tftpRootDir = tftpRootDir[index:] | 
 |     tftpRootDirList.append(tftpRootDir) | 
 |     return True | 
 |  | 
 | def preparePING(tftpRootDir): | 
 |     pingFiles = ['boot.msg', 'initrd.gz', 'kernel', 'pxelinux.0'] | 
 |     pingDir = "/usr/share/PING" | 
 |      | 
 |     for f in pingFiles: | 
 |         path = join(pingDir, f) | 
 |         if not exists(path): | 
 |             printError("Cannot find %s, please make sure PING-3.01 is installed"%path) | 
 |             return False | 
 |         if not bashWithResult("cp -f %s %s"%(path, tftpRootDir)): return False | 
 |       | 
 |     if not bashWithResult("mkdir -p %s/pxelinux.cfg"%tftpRootDir): return False | 
 |      | 
 |     return True | 
 |              | 
 |          | 
 | if __name__ == "__main__": | 
 |     initLoging("/tmp/cloud-setup-baremetal.log") | 
 |     tftpRootDirList = [] | 
 |      | 
 |     exitIfFail(checkEnv()) | 
 |     exitIfFail(configurePxeStuff()) | 
 |     exitIfFail(getTftpRootDir(tftpRootDirList)) | 
 |      | 
 |     tftpRootDir = tftpRootDirList[0].strip() | 
 |     exitIfFail(preparePING(tftpRootDir)) | 
 |     printMsg("") | 
 |     printMsg("Setup BareMetal PXE server successfully") | 
 |     printMsg("TFTP root directory is: %s\n"%tftpRootDir) | 
 |     sys.exit(0) | 
 |      |