#!/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)