diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in index 6e9b448370b..b11fdcd3bec 100755 --- a/agent/bindir/cloud-setup-agent.in +++ b/agent/bindir/cloud-setup-agent.in @@ -1,148 +1,107 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/python +import os +import logging +import sys +import socket +from cloud.cloudException import CloudRuntimeException, CloudInternalException +from cloud.utilities import initLoging +from cloud.configFileOps import configFileOps +from cloud.globalEnv import globalEnv +from cloud.networkConfig import networkConfig +from cloud.syscfg import sysConfigFactory -import sys, os, subprocess, errno, re, traceback, getopt +from optparse import OptionParser + +def getUserInputs(): + print "Welcome to myCloud Setup:" -# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- -# ---- We do this so cloud_utils can be looked up in the following order: -# ---- 1) Sources directory -# ---- 2) waf configured PYTHONDIR -# ---- 3) System Python path -for pythonpath in ( - "@PYTHONDIR@", - os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), - ): - if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) -# ---- End snippet of code ---- -import cloud_utils -from cloud_utils import stderr,CheckFailed,TaskFailed,backup_etc,restore_etc -from cloud_utils import setup_agent_config,stop_service,enable_service -from cloud_utils import exit as bail -from cloud_utils import all, any + cfo = configFileOps("/etc/cloud/agent/agent.properties") + oldMgt = cfo.getEntry("host") + mgtSvr = raw_input("Please input the Management Server Name/IP:[%s]"%oldMgt) + if mgtSvr == "": + mgtSvr = oldMgt + try: + socket.getaddrinfo(mgtSvr, 443) + except: + print "Failed to resolve %s. Please input correct server name or IP."%mgtSvr + exit(1) -#--------------- procedure starts here ------------ + oldToken = cfo.getEntry("zone") + zoneToken = raw_input("Please input the Zone Token:[%s]"%oldToken) + + if zoneToken == "": + zoneToken = oldToken -# FÏXME for backup and restore: collect service state for all services so we can restore the system's runtime state back to what it was before -# possible exit states: -# a. system configuration needs administrator attention -# b. automatic reconfiguration failed -# c. process interrupted -# d. everything was configured properly (exit status 0) + try: + defaultNic = networkConfig.getDefaultNetwork() + except: + print "Failed to get default route. Please configure your network to have a default route" + exit(1) + + defNic = defaultNic.name + network = raw_input("Please choose which network used to create VM:[%s]"%defNic) + if network == "": + if defNic == "": + print "You need to specifiy one of Nic or bridge on your system" + exit(1) + elif network == "": + network = defNic -brname = "@PACKAGE@br0" -servicename = "@PACKAGE@-agent" -configfile = "@AGENTSYSCONFDIR@/agent.properties" -backupdir = "@SHAREDSTATEDIR@/@AGENTPATH@/etcbackup" + return [mgtSvr, zoneToken, network] -#=================== the magic happens here ==================== +if __name__ == '__main__': + initLoging("/var/log/cloud/setupAgent.log") + glbEnv = globalEnv() + glbEnv.mode = "Agent" + glbEnv.agentMode = "Agent" + parser = OptionParser() + parser.add_option("-a", action="store_true", dest="auto", help="auto mode") + parser.add_option("-m", "--host", dest="mgt", help="management server name or IP") + parser.add_option("-z", "--zone", dest="zone", help="zone id") + parser.add_option("-p", "--pod", dest="pod", help="pod id") + parser.add_option("-c", "--cluster", dest="cluster", help="cluster id") + parser.add_option("-g", "--guid", dest="guid", help="guid") + parser.add_option("--pubNic", dest="pubNic", help="public nic") + parser.add_option("--prvNic", dest="prvNic", help="private nic") + parser.add_option("--guestNic", dest="guestNic", help="guest nic") -try: -# parse cmd line - opts, args = getopt.getopt(sys.argv[1:], "a", ["host=", "zone=", "pod=", "cluster=", "no-kvm", "guid=", "pubNic=", "prvNic="]) - host=None - zone=None - pod=None - cluster=None - guid=None - pubNic=None - prvNic=None - autoMode=False - do_check_kvm = True - for opt, arg in opts: - if opt == "--host": - if arg != "": - host = arg - elif opt == "--zone": - if arg != "": - zone = arg - elif opt == "--pod": - if arg != "": - pod = arg - elif opt == "--cluster": - if arg != "": - cluster = arg - elif opt == "--guid": - if arg != "": - guid = arg - elif opt == "--pubNic": - pubNic = arg - elif opt == "--prvNic": - prvNic = arg - elif opt == "--no-kvm": - do_check_kvm = False - elif opt == "-a": - autoMode=True - - if autoMode: - cloud_utils.setLogFile("/var/log/cloud/setupAgent.log") - - stderr("Welcome to the Cloud Agent setup") - stderr("") - # pre-flight checks for things that the administrator must fix - try: - for f,n in cloud_utils.preflight_checks( - do_check_kvm=do_check_kvm - ): - stderr(n) - f() - except CheckFailed,e: - stderr(str(e)) - bail(cloud_utils.E_NEEDSMANUALINTERVENTION, - "Cloud Agent setup cannot continue until these issues have been addressed") - - # system configuration tasks that our Cloud Agent setup performs - - try: - tasks = cloud_utils.config_tasks(brname, pubNic, prvNic) - for t in tasks: - t.setAutoMode(autoMode) - if all( [ t.done() for t in tasks ] ): - - stderr("All configuration tasks have been performed already") - - else: - - backup_etc(backupdir) - try: - # run all tasks that have not been done - for t in [ n for n in tasks if not n.done() ]: - t.run() - except: - # oops, something wrong, restore system to earlier state and re-raise - stderr("A fatal issue has been detected -- restoring system configuration.\nPlease be patient; *do not* interrupt this process.") - restore_etc(backupdir) - for t in [ n for n in tasks if hasattr(n,"restore_state") ]: - t.restore_state() - raise - - except (TaskFailed,CheckFailed),e: - # some configuration task or post-flight check failed, we exit right away - stderr(str(e)) - bail(cloud_utils.E_SETUPFAILED,"Cloud Agent setup failed") - - setup_agent_config(configfile, host, zone, pod, cluster, guid, pubNic, prvNic) - stderr("Enabling and starting the Cloud Agent") - stop_service(servicename) - enable_service(servicename) - stderr("Cloud Agent restarted") - -except KeyboardInterrupt,e: - # user interrupted, we exit right away - bail(cloud_utils.E_INTERRUPTED,"Cloud Agent setup interrupted") -except SystemExit,e: - # process above handled a failure then called bail(), which raises a SystemExit on CentOS - sys.exit(e.code) -except Exception,e: - # at ths point, any exception has been dealt with cleanly by restoring system config from a backup - # we just inform the user that there was a problem - # and bail prematurely - stderr("Cloud Agent setup has experienced an unrecoverable error. Please report the following technical details to Cloud.com.") - traceback.print_exc() - bail(cloud_utils.E_UNHANDLEDEXCEPTION,"Cloud Agent setup ended prematurely") + (options, args) = parser.parse_args() + if options.auto is None: + userInputs = getUserInputs() + glbEnv.mgtSvr = userInputs[0] + glbEnv.zone = userInputs[1] + glbEnv.defaultNic = userInputs[2] + #generate UUID + glbEnv.uuid = configFileOps("/etc/cloud/agent/agent.properties").getEntry("guid") + if glbEnv.uuid == "": + glbEnv.uuid = bash("uuidgen").getStdout() + else: + for para, value in options.__dict__.items(): + if value is None: + print "Missing operand:%s"%para + print "Try %s --help for more information"%sys.argv[0] + sys.exit(1) -stderr("") -stderr("Cloud Agent setup completed successfully") - -# ========================= end program ======================== + glbEnv.uuid = options.guid + glbEnv.mgtSvr = options.mgt + glbEnv.zone = options.zone + glbEnv.pod = options.pod + glbEnv.cluster = options.cluster + glbEnv.nics.append(options.prvNic) + glbEnv.nics.append(options.pubNic) + glbEnv.nics.append(options.guestNic) + + print "Starting to configure your system:" + syscfg = sysConfigFactory.getSysConfigFactory(glbEnv) + try: + syscfg.config() + print "Cloud Agent setup is Done!" + except (CloudRuntimeException,CloudInternalException), e: + print e + print "Try to restore your system:" + try: + syscfg.restore() + except: + pass diff --git a/client/bindir/cloud-setup-management.in b/client/bindir/cloud-setup-management.in index 8bee81a7744..e471feef15c 100755 --- a/client/bindir/cloud-setup-management.in +++ b/client/bindir/cloud-setup-management.in @@ -1,263 +1,22 @@ -#!/usr/bin/env python - -import sys, os, subprocess, errno, re - -# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- -# ---- We do this so cloud_utils can be looked up in the following order: -# ---- 1) Sources directory -# ---- 2) waf configured PYTHONDIR -# ---- 3) System Python path -for pythonpath in ( - "@PYTHONDIR@", - os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), - ): - if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) -# ---- End snippet of code ---- -from cloud_utils import check_selinux, CheckFailed - -E_GENERIC= 1 -E_NOKVM = 2 -E_NODEFROUTE = 3 -E_DHCP = 4 -E_NOPERSISTENTNET = 5 -E_VIRTRECONFIGFAILED = 7 -E_FWRECONFIGFAILED = 8 -E_AGENTRECONFIGFAILED = 9 -E_AGENTFAILEDTOSTART = 10 -E_NOFQDN = 11 -E_OSUNSUPP = 11 -E_SUDORECONFIGFAILED = 6 -E_SELINUXENABLED=12 - - -def stderr(msgfmt,*args): - msgfmt += "\n" - if args: sys.stderr.write(msgfmt%args) - else: sys.stderr.write(msgfmt) - sys.stderr.flush() - -def bail(errno=E_GENERIC,message=None,*args): - if message: stderr(message,*args) - stderr("CloudStack Management Server setup aborted") - sys.exit(errno) - - -#---------------- boilerplate for python 2.4 support - - -# CENTOS does not have this -- we have to put this here -try: - from subprocess import check_call - from subprocess import CalledProcessError -except ImportError: - def check_call(*popenargs, **kwargs): - import subprocess - retcode = subprocess.call(*popenargs, **kwargs) - cmd = kwargs.get("args") - if cmd is None: cmd = popenargs[0] - if retcode: raise CalledProcessError(retcode, cmd) - return retcode - - class CalledProcessError(Exception): - def __init__(self, returncode, cmd): - self.returncode = returncode ; self.cmd = cmd - def __str__(self): return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) - -# ------------ end boilerplate ------------------------- - - -def check_kvm(): return check_call(["kvm-ok"]) -def check_hostname(): return check_call(["hostname",'--fqdn']) - -class Command: - def __init__(self,name,parent=None): - self.__name = name - self.__parent = parent - def __getattr__(self,name): - if name == "_print": name = "print" - return Command(name,self) - def __call__(self,*args): - cmd = self.__get_recursive_name() + list(args) - #print " ",cmd - popen = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) - m = popen.communicate() - ret = popen.wait() - if ret: - e = CalledProcessError(ret,cmd) - e.stdout,e.stderr = m - raise e - class CommandOutput: - def __init__(self,stdout,stderr): - self.stdout = stdout - self.stderr = stderr - return CommandOutput(*m) - def __lt__(self,other): - cmd = self.__get_recursive_name() - #print " ",cmd,"<",other - popen = subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) - m = popen.communicate(other) - ret = popen.wait() - if ret: - e = CalledProcessError(ret,cmd) - e.stdout,e.stderr = m - raise e - class CommandOutput: - def __init__(self,stdout,stderr): - self.stdout = stdout - self.stderr = stderr - return CommandOutput(*m) - - def __get_recursive_name(self,sep=None): - m = self - l = [] - while m is not None: - l.append(m.__name) - m = m.__parent - l.reverse() - if sep: return sep.join(l) - else: return l - def __str__(self): - return ''%self.__get_recursive_name(sep=" ") - - def __repr__(self): return self.__str__() - -def checkselinux(): - try: - check_selinux() - except CheckFailed,e: - bail(E_SELINUXENABLED,"SELINUX is set to enforcing. Please set it to permissive in /etc/selinux/config. Then reboot or run setenforce permissive. Then run this program again.") - - -ip = Command("ip") -service = Command("/sbin/service") -chkconfig = Command("/sbin/chkconfig") -updatercd = Command("update-rc.d") -ufw = Command("ufw") -iptables = Command("/sbin/iptables") -iptables_save = Command("/sbin/iptables-save") -kvmok = Command("kvm-ok") -ifconfig = Command("/sbin/ifconfig") -uuidgen = Command("uuidgen") - -Fedora = os.path.exists("/etc/fedora-release") -CentOS = os.path.exists("/etc/centos-release") or ( os.path.exists("/etc/redhat-release") and not os.path.exists("/etc/fedora-release") ) - -#--------------- procedure starts here ------------ - -stderr("Welcome to the CloudStack Management setup") -stderr("") - -try: - check_hostname() - stderr("The hostname of this machine is properly set up") -except CalledProcessError,e: - bail(E_NOFQDN,"This machine does not have an FQDN (fully-qualified domain name) for a hostname") - -if Fedora or CentOS: - checkselinux() - -try: - service("@PACKAGE@-management","status") - stderr("Stopping the CloudStack Management Server") - m = service("@PACKAGE@-management","stop") - print m.stdout + m.stderr - stderr("CloudStack Management Server stopped") -except CalledProcessError,e: - pass - -sudoerstext = file("/etc/sudoers").readlines() -def restore(): - try: file("/etc/sudoers","w").write(''.join(sudoerstext)) - except OSError,e: raise - -try: - stderr("Configure /etc/sudoers") - newtext = [] - clouder = "cloud ALL = NOPASSWD : ALL" - hasClouder = False - for line in sudoerstext: - if "Defaults" in line and "requiretty" in line: - continue - if clouder in line: - hasClouder = True - newtext.append(line) - if not hasClouder: - newtext.append(clouder+'\n') - file("/etc/sudoers", "w").write(''.join(newtext)) -except: - restore() - bail(E_SUDORECONFIGFAILED,"sudoers reconfiguration failed") - -ports = "80 8080 8096 8250 9090".split() -if Fedora or CentOS: - try: - o = chkconfig("--list","iptables") - iptableschkconfig = True - except CalledProcessError,e: - stderr("No need to set up iptables as the service is not registered in the SysV init system") - iptableschkconfig = False - if iptableschkconfig is True: - try: - o = chkconfig("--list","iptables") - if ":on" in o.stdout and os.path.exists("/etc/sysconfig/iptables"): - stderr("Setting up firewall rules to permit traffic to CloudStack services") - service.iptables.start() ; print o.stdout + o.stderr - o = iptables_save() - for p in ports: - r = "INPUT -p tcp -m tcp --dport %s -j ACCEPT" % p - if r in o.stdout: - continue - iptables("-I","INPUT","1","-p","tcp","--dport",p,"-j","ACCEPT") - - o = service.iptables.save() ; print o.stdout + o.stderr - else: - stderr("No need to set up iptables as the service is unconfigured or not set to start up at boot") - except CalledProcessError,e: - print e.stdout+e.stderr - bail(E_FWRECONFIGFAILED,"Firewall rules could not be set") -else: - stderr("Setting up firewall rules to permit traffic to CloudStack services") - try: - for p in ports: ufw.allow(p) - stderr("Rules set") - except CalledProcessError,e: - print e.stdout+e.stderr - bail(E_FWRECONFIGFAILED,"Firewall rules could not be set") - - stderr("We are going to enable ufw now. This may disrupt network connectivity and service availability. See the ufw documentation for information on how to manage ufw firewall policies.") - try: - o = ufw.enable < "y\n" ; print o.stdout + o.stderr - except CalledProcessError,e: - print e.stdout+e.stderr - bail(E_FWRECONFIGFAILED,"Firewall could not be enabled") - -stderr("") -stderr("CloudStack Management Server setup completed successfully") - -try: - if Fedora or CentOS: chkconfig("tomcat6","off") - else: updatercd("tomcat6","disable") - service("tomcat6","status") - stderr("Stopping Tomcat") - m = service("tomcat6","stop") - print m.stdout + m.stderr - stderr("Tomcat stopped") -except CalledProcessError,e: - pass - -try: - if Fedora or CentOS: - chkconfig("--level","35","network","on") -except CalledProcessError,e: - pass - -stderr("Starting the CloudStack Management Server") -try: - m = service("@PACKAGE@-management","start") - print m.stdout + m.stderr -except CalledProcessError,e: - print e.stdout + e.stderr - bail(E_AGENTFAILEDTOSTART,"@PACKAGE@-management failed to start") - - -# FIXMES: 1) nullify networkmanager on ubuntu (asking the user first) and enable the networking service permanently +from cloud.syscfg import sysConfigFactory +from cloud.utilities import initLoging +from cloud.cloudException import CloudRuntimeException, CloudInternalException +from cloud.globalEnv import globalEnv +if __name__ == '__main__': + initLoging("/var/log/cloud/setupManagement.log") + glbEnv = globalEnv() + + glbEnv.mode = "Server" + + print "Starting to configure CloudStack Management Server:" + syscfg = sysConfigFactory.getSysConfigFactory(glbEnv) + try: + syscfg.config() + print "CloudStack Management Server setup is Done!" + except (CloudRuntimeException, CloudInternalException), e: + print e + print "Try to restore your system:" + try: + syscfg.restore() + except: + pass diff --git a/python/lib/cloudutils/__init__.py b/python/lib/cloudutils/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/lib/cloudutils/cloudException.py b/python/lib/cloudutils/cloudException.py new file mode 100644 index 00000000000..883798a4d40 --- /dev/null +++ b/python/lib/cloudutils/cloudException.py @@ -0,0 +1,29 @@ +import sys +import traceback +class CloudRuntimeException(Exception): + def __init__(self, errMsg): + self.errMsg = errMsg + + value = sys.exc_info()[1] + if value is not None: + self.errMsg += ", due to:" + str(value) + + self.details = formatExceptionInfo() + def __str__(self): + return self.errMsg + def getDetails(self): + return self.details + +class CloudInternalException(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 diff --git a/python/lib/cloudutils/configFileOps.py b/python/lib/cloudutils/configFileOps.py new file mode 100644 index 00000000000..46c432b8e43 --- /dev/null +++ b/python/lib/cloudutils/configFileOps.py @@ -0,0 +1,148 @@ +import re +import tempfile +import shutil +from utilities import bash +class configFileOps: + class entry: + def __init__(self, name, value, op, separator): + self.name = name + self.value = value + self.state = "new" + self.op = op + self.separator = separator + def setState(self, state): + self.state = state + def getState(self): + return self.state + + def __init__(self, fileName, cfg=None): + self.fileName = fileName + self.entries = [] + self.backups = [] + + if cfg is not None: + cfg.cfoHandlers.append(self) + + def addEntry(self, name, value, separator="="): + e = self.entry(name, value, "add", separator) + self.entries.append(e) + + def rmEntry(self, name, value, separator="="): + entry = self.entry(name, value, "rm", separator) + self.entries.append(entry) + + def getEntry(self, name, separator="="): + try: + ctx = file(self.fileName).read(-1) + match = re.search("^" + name + ".*", ctx, re.MULTILINE) + if match is None: + return "" + line = match.group(0).split(separator, 1) + return line[1] + except: + return "" + + def save(self): + fp = open(self.fileName, "r") + newLines = [] + for line in fp.readlines(): + matched = False + for entry in self.entries: + if entry.op == "add": + if entry.separator == "=": + matchString = "^\ *" + entry.name + ".*" + elif entry.separator == " ": + matchString = "^\ *" + entry.name + "\ *" + entry.value + else: + if entry.separator == "=": + matchString = "^\ *" + entry.name + "\ *=\ *" + entry.value + else: + matchString = "^\ *" + entry.name + "\ *" + entry.value + + match = re.match(matchString, line) + if match is not None: + if entry.op == "add" and entry.separator == "=": + newline = entry.name + "=" + entry.value + "\n" + entry.setState("set") + newLines.append(newline) + self.backups.append([line, newline]) + matched = True + break + elif entry.op == "rm": + entry.setState("set") + self.backups.append([line, None]) + matched = True + break + + if not matched: + newLines.append(line) + + for entry in self.entries: + if entry.getState() != "set": + if entry.op == "add": + newline = entry.name + entry.separator + entry.value + "\n" + newLines.append(newline) + self.backups.append([None, newline]) + entry.setState("set") + + fp.close() + + file(self.fileName, "w").writelines(newLines) + + def replace_line(self, startswith,stanza,always_add=False): + lines = [ s.strip() for s in file(self.fileName).readlines() ] + newlines = [] + replaced = False + for line in lines: + if re.search(startswith, line): + if stanza is not None: + newlines.append(stanza) + self.backups.append([line, stanza]) + replaced = True + else: newlines.append(line) + if not replaced and always_add: + newlines.append(stanza) + self.backups.append([None, stanza]) + newlines = [ s + '\n' for s in newlines ] + file(self.fileName,"w").writelines(newlines) + + def replace_or_add_line(self, startswith,stanza): + return self.replace_line(startswith,stanza,always_add=True) + + def add_lines(self, lines, addToBackup=True): + fp = file(self.fileName).read(-1) + sh = re.escape(lines) + match = re.search(sh, fp, re.MULTILINE) + if match is not None: + return + + fp += lines + file(self.fileName, "w").write(fp) + self.backups.append([None, lines]) + + def replace_lines(self, src, dst, addToBackup=True): + fp = file(self.fileName).read(-1) + sh = re.escape(src) + if dst is None: + dst = "" + repl,nums = re.subn(sh, dst, fp) + if nums <=0: + return + file(self.fileName, "w").write(repl) + if addToBackup: + self.backups.append([src, dst]) + + def backup(self): + for oldLine, newLine in self.backups: + if newLine is None: + self.add_lines(oldLine, False) + else: + self.replace_lines(newLine, oldLine, False) + +if __name__ == '__main__': + cfo = configFileOps("./sudoers") + #cloud ALL = NOPASSWD : ALL + cfo.addEntry("cloud ALL ", "NOPASSWD : ALL") + cfo.rmEntry("Defaults", "requiretty", " ") + #cfo.addEntry("zone", "test", " ") + cfo.save() diff --git a/python/lib/cloudutils/db.py b/python/lib/cloudutils/db.py new file mode 100644 index 00000000000..03cd133c605 --- /dev/null +++ b/python/lib/cloudutils/db.py @@ -0,0 +1,66 @@ +import MySQLdb +import os +from utilities import bash +from cloudException import CloudRuntimeException +import sys +class Database: + """Database connection""" + def __init__(self, username, password=None, host='localhost', port='3306', db="cloud"): + self.host = host + self.username = username + self.password = password + self.port = port + self.db = db + + def execute(self, statement): + txn = None + try: + if self.password is not None: + txn = MySQLdb.Connect(host=self.host, user=self.username, + passwd=self.password, db=self.db) + else: + txn = MySQLdb.Connect(host=self.host, user=self.username, + db=self.db) + cursor = txn.cursor() + cursor.execute(statement) + cursor.close() + txn.commit() + if txn is not None: + try: + txn.close() + except: + pass + except: + if txn is not None: + try: + txn.close() + except: + pass + raise CloudRuntimeException("Failed to execute:%s"%statement) + + def testConnection(self): + try: + if self.password is not None: + db = MySQLdb.Connect(host=self.host, user=self.username, + passwd=self.password, db=self.db) + else: + db = MySQLdb.Connect(host=self.host, user=self.username, + db=self.db) + return True + except: + raise CloudRuntimeException("Failed to Connect to DB") + + def executeFromFile(self, file): + if not os.path.exists(file): + return False + + cmdLine = "mysql --host=" + self.host + " --port=" + str(self.port) + " --user=" + self.username + if self.password is not None: + cmdLine += " --password=" + self.password + + cmdLine += " < " + file + + try: + bash(cmdLine) + except: + raise CloudRuntimeException("Failed to execute " + cmdLine) diff --git a/python/lib/cloudutils/globalEnv.py b/python/lib/cloudutils/globalEnv.py new file mode 100644 index 00000000000..a0dd1edb344 --- /dev/null +++ b/python/lib/cloudutils/globalEnv.py @@ -0,0 +1,24 @@ +class globalEnv: + def __init__(self): + #Agent/Server/Db + self.mode = None + #server mode: normal/mycloud + self.svrMode = None + #myCloud/Agent/Console + self.agentMode = None + #debug + self.debug = False + #management server IP + self.mgtSvr = None + #zone id or zone name + self.zone = None + #pod id or pod name + self.pod = None + #cluster id or cluster name + self.cluster = None + #nics: 0: private nic, 1: guest nic, 2: public nic used by agent + self.nics = [] + #uuid + self.uuid = None + #default private network + self.privateNet = "cloudbr0" \ No newline at end of file diff --git a/python/lib/cloudutils/networkConfig.py b/python/lib/cloudutils/networkConfig.py new file mode 100644 index 00000000000..8f6189730f0 --- /dev/null +++ b/python/lib/cloudutils/networkConfig.py @@ -0,0 +1,135 @@ +from utilities import bash +from cloudException import CloudRuntimeException, CloudInternalException +import logging +import os +import re + +class networkConfig: + class devInfo: + def __init__(self, macAddr, ipAddr, netmask, gateway, type, name): + self.name = name + self.macAdrr = macAddr + self.ipAddr = ipAddr + self.netmask = netmask + self.gateway = gateway + self.type = type + self.name = name + #dhcp or static + self.method = None + + @staticmethod + def getDefaultNetwork(): + cmd = bash("route -n|awk \'/^0.0.0.0/ {print $2,$8}\'") + if not cmd.isSuccess(): + logging.debug("Failed to get default route") + raise CloudRuntimeException("Failed to get default route") + + result = cmd.getStdout().split(" ") + gateway = result[0] + dev = result[1] + + pdi = networkConfig.getDevInfo(dev) + logging.debug("Found default network device:%s"%pdi.name) + pdi.gateway = gateway + return pdi + + @staticmethod + def createBridge(dev, brName): + if not networkConfig.isBridgeSupported(): + logging.debug("bridge is not supported") + return False + if networkConfig.isBridgeEnslavedWithDevices(brName): + logging.debug("bridge: %s has devices enslaved"%brName) + return False + + cmds = "" + if not networkConfig.isBridge(brName): + cmds = "brctl addbr %s ;"%brName + + cmds += "ifconfig %s up;"%brName + cmds += "brctl addif %s %s"%(brName, dev) + return bash(cmds).isSuccess() + + @staticmethod + def isBridgeEnslavedWithDevices(brName): + if not networkConfig.isBridge(brName): + return False + + if not os.listdir("/sys/class/net/%s/brif"%brName): + return False + + return True + + @staticmethod + def isBridgeSupported(): + if os.path.exists("/proc/sys/net/bridge"): + return True + + return bash("modprobe bridge").isSucess() + + @staticmethod + def isNetworkDev(devName): + return os.path.exists("/sys/class/net/%s"%devName) + + @staticmethod + def isBridgePort(devName): + return os.path.exists("/sys/class/net/%s/brport"%devName) + + @staticmethod + def isBridge(devName): + return os.path.exists("/sys/class/net/%s/bridge"%devName) + + @staticmethod + def getBridge(devName): + bridgeName = None + if os.path.exists("/sys/class/net/%s/brport/bridge"%devName): + realPath = os.path.realpath("/sys/class/net/%s/brport/bridge"%devName) + bridgeName = realPath.split("/")[-1] + return bridgeName + + @staticmethod + def getEnslavedDev(br, brPort): + if not networkConfig.isBridgeEnslavedWithDevices(br): + return None + + for dev in os.listdir("/sys/class/net/%s/brif"%br): + br_port = int(file("/sys/class/net/%s/brif/%s/port_no"%(br,dev)).readline().strip("\n"), 16) + if br_port == brPort: + return dev + + return None + + @staticmethod + def getDevInfo(dev): + if not networkConfig.isNetworkDev(dev): + logging.debug("dev: " + dev + " is not a network device") + raise CloudInternalException("dev: " + dev + " is not a network device") + + netmask = None + ipAddr = None + macAddr = None + + cmd = bash("ifconfig " + dev) + if not cmd.isSuccess(): + logging.debug("Failed to get address from ifconfig") + raise CloudInternalException("Failed to get network info by ifconfig %s"%dev) + + for line in cmd.getLines(): + if line.find("HWaddr") != -1: + macAddr = line.split("HWaddr ")[1].strip(" ") + elif line.find("inet ") != -1: + m = re.search("addr:(.*)\ *Bcast:(.*)\ *Mask:(.*)", line) + if m is not None: + ipAddr = m.group(1).rstrip(" ") + netmask = m.group(3).rstrip(" ") + + if networkConfig.isBridgePort(dev): + type = "brport" + elif networkConfig.isBridge(dev): + type = "bridge" + else: + type = "dev" + + return networkConfig.devInfo(macAddr, ipAddr, netmask, None, type, dev) + + diff --git a/python/lib/cloudutils/serviceConfig.py b/python/lib/cloudutils/serviceConfig.py new file mode 100644 index 00000000000..d4dfdcf919b --- /dev/null +++ b/python/lib/cloudutils/serviceConfig.py @@ -0,0 +1,627 @@ +from utilities import writeProgressBar, bash +from cloudException import CloudRuntimeException, CloudInternalException, formatExceptionInfo +import logging +from networkConfig import networkConfig +import re +from configFileOps import configFileOps +import os +import shutil + +class serviceCfgBase(object): + def __init__(self, syscfg): + self.status = None + self.serviceName = "" + self.cfoHandlers = [] + self.syscfg = syscfg + self.netMgrRunning = False + + def configration(self): + writeProgressBar("Configure " + self.serviceName + " ...", None) + result = False + try: + result = self.config() + self.status = result + writeProgressBar(None, result) + return result + except CloudRuntimeException, e: + self.status = result + writeProgressBar(None, result) + logging.debug(e.getDetails()) + raise e + except CloudInternalException, e: + self.status = result + writeProgressBar(None, result) + raise e + except: + logging.debug(formatExceptionInfo()) + if self.syscfg.env.mode == "Server": + raise CloudRuntimeException("Configure %s failed, Please check the /var/log/cloud/setupManagement.log for detail"%self.serviceName) + else: + raise CloudRuntimeException("Configure %s failed, Please check the /var/log/cloud/setupAgent.log for detail"%self.serviceName) + + def backup(self): + if self.status is None: + return True + + writeProgressBar("Restore " + self.serviceName + " ...", None) + result = False + try: + for cfo in self.cfoHandlers: + cfo.backup() + + result = self.restore() + except (CloudRuntimeException, CloudInternalException), e: + logging.debug(e) + + writeProgressBar(None, result) + + def config(self): + return True + + def restore(self): + return True + +class networkConfigBase: + def __init__(self, syscfg): + self.netcfg = networkConfig() + self.serviceName = "Network" + self.brName = None + self.dev = None + self.syscfg = syscfg + + def isPreConfiged(self): + preCfged = False + for br in self.syscfg.env.nics: + if not self.netcfg.isNetworkDev(br): + raise CloudInternalException("%s is not a network device, is it down?"%br) + if not self.netcfg.isBridge(br): + raise CloudInternalException("%s is not a bridge"%br) + preCfged = True + + return preCfged + + def cfgNetwork(self, dev=None, brName=None): + if dev is None: + device = self.netcfg.getDefaultNetwork() + else: + device = self.netcfg.getDevInfo(dev) + + if device.type == "dev": + if brName is None: + brName = "cloudbr0" + + self.writeToCfgFile(brName, device) + elif device.type == "brport": + brName = self.netcfg.getBridge(dev) + brDevice = self.netcfg.getDevInfo(brName) + self.writeToCfgFile(brDevice.name, device) + elif device.type == "bridge": + #Fixme, assuming the outgoing physcial device is on port 1 + enslavedDev = self.netcfg.getEnslavedDev(device.name, 1) + if enslavedDev is None: + raise CloudInternalException("Failed to get enslaved devices on bridge:%s"%device.name) + + brDevice = device + device = self.netcfg.getDevInfo(enslavedDev) + brName = brDevice.name + self.writeToCfgFile(brName, device) + + self.brName = brName + self.dev = device.name + + def writeToCfgFile(self): + pass + +class networkConfigUbuntu(serviceCfgBase, networkConfigBase): + def __init__(self, syscfg): + super(networkConfigUbuntu, self).__init__(syscfg) + networkConfigBase.__init__(self, syscfg) + self.netCfgFile = "/etc/network/interfaces" + + def getNetworkMethod(self, line): + if line.find("static") != -1: + return "static" + elif line.find("dhcp") != -1: + return "dhcp" + else: + logging.debug("Failed to find the network method from:%s"%line) + raise CloudInternalException("Failed to find the network method from /etc/network/interfaces") + + def addBridge(self, br, dev): + bash("ifdown %s"%dev.name) + for line in file(self.netCfgFile).readlines(): + match = re.match("^ *iface %s.*"%dev.name, line) + if match is not None: + dev.method = self.getNetworkMethod(match.group(0)) + bridgeCfg = "\niface %s inet manual\n \ + auto %s\n \ + iface %s inet %s\n \ + bridge_ports %s\n"%(dev.name, br, br, dev.method, dev.name) + cfo = configFileOps(self.netCfgFile, self) + cfo.replace_line("^ *iface %s.*"%dev.name, bridgeCfg) + + def addDev(self, br, dev): + logging.debug("Haven't implement yet") + + def addBridgeAndDev(self, br, dev): + logging.debug("Haven't implement yet") + + def writeToCfgFile(self, br, dev): + cfg = file(self.netCfgFile).read() + ifaceDev = re.search("^ *iface %s.*"%dev.name, cfg, re.MULTILINE) + ifaceBr = re.search("^ *iface %s.*"%br, cfg, re.MULTILINE) + if ifaceDev is not None and ifaceBr is not None: + logging.debug("%s:%s already configured"%(br, dev.name)) + return True + elif ifaceDev is not None and ifaceBr is None: + #reconfig bridge + self.addBridge(br, dev) + elif ifaceDev is None and ifaceBr is not None: + #reconfig dev + raise CloudInternalException("Missing device configuration, Need to add your network configuration into /etc/network/interfaces at first") + else: + raise CloudInternalException("Missing bridge/device network configuration, need to add your network configuration into /etc/network/interfaces at first") + + def config(self): + try: + if super(networkConfigUbuntu, self).isPreConfiged(): + return True + + self.netMgrRunning = self.syscfg.svo.isServiceRunning("network-manager") + super(networkConfigUbuntu, self).cfgNetwork() + if self.netMgrRunning: + self.syscfg.svo.stopService("network-manager") + self.syscfg.svo.disableService("network-manager") + + if not bash("ifup %s"%self.brName).isSuccess(): + raise CloudInternalException("Can't start network:%s"%self.brName, bash.getErrMsg(self)) + + self.syscfg.env.nics.append(self.brName) + self.syscfg.env.nics.append(self.brName) + self.syscfg.env.nics.append(self.brName) + return True + except: + raise + + def restore(self): + try: + if self.netMgrRunning: + self.syscfg.svo.enableService("network-manager") + self.syscfg.svo.startService("network-manager") + + bash("/etc/init.d/networking stop") + bash("/etc/init.d/networking start") + return True + except: + logging.debug(formatExceptionInfo()) + return False + +class networkConfigRedhat(serviceCfgBase, networkConfigBase): + def __init__(self, syscfg): + super(networkConfigRedhat, self).__init__(syscfg) + networkConfigBase.__init__(self, syscfg) + + def writeToCfgFile(self, brName, dev): + self.devCfgFile = "/etc/sysconfig/network-scripts/ifcfg-%s"%dev.name + self.brCfgFile = "/etc/sysconfig/network-scripts/ifcfg-%s"%brName + + isDevExist = os.path.exists(self.devCfgFile) + isBrExist = os.path.exists(self.brCfgFile) + if isDevExist and isBrExist: + logging.debug("%s:%s already configured"%(brName, dev.name)) + return True + elif isDevExist and not isBrExist: + #reconfig bridge + self.addBridge(brName, dev) + elif not isDevExist and isBrExist: + #reconfig dev + raise CloudInternalException("Missing device configuration, Need to add your network configuration into /etc/sysconfig/network-scripts at first") + else: + raise CloudInternalException("Missing bridge/device network configuration, need to add your network configuration into /etc/sysconfig/network-scripts at first") + + + def addBridge(self, brName, dev): + bash("ifdown %s"%dev.name) + + if not os.path.exists(self.brCfgFile): + shutil.copy(self.devCfgFile, self.brCfgFile) + + #config device file at first: disable nm, set onboot=yes if not + cfo = configFileOps(self.devCfgFile, self) + cfo.addEntry("NM_CONTROLLED", "no") + cfo.addEntry("ONBOOT", "yes") + cfo.addEntry("BRIDGE", brName) + cfo.save() + + cfo = configFileOps(self.brCfgFile, self) + cfo.addEntry("NM_CONTROLLED", "no") + cfo.addEntry("ONBOOT", "yes") + cfo.addEntry("DEVICE", brName) + cfo.addEntry("TYPE", "Bridge") + cfo.save() + + def config(self): + try: + if super(networkConfigRedhat, self).isPreConfiged(): + return True + + super(networkConfigRedhat, self).cfgNetwork() + + self.netMgrRunning = self.syscfg.svo.isServiceRunning("NetworkManager") + if self.netMgrRunning: + self.syscfg.svo.stopService("NetworkManager") + self.syscfg.svo.disableService("NetworkManager") + + + if not bash("service network restart").success(): + raise CloudInternalException("Can't restart network") + + self.syscfg.env.nics.append(self.brName) + self.syscfg.env.nics.append(self.brName) + self.syscfg.env.nics.append(self.brName) + return True + except: + raise + + def restore(self): + try: + if self.netMgrRunning: + self.syscfg.svo.enableService("NetworkManager") + self.syscfg.svo.startService("NetworkManager") + bash("service network restart") + return True + except: + logging.debug(formatExceptionInfo()) + return False + +class cgroupConfig(serviceCfgBase): + def __init__(self, syscfg): + super(cgroupConfig, self).__init__(syscfg) + self.serviceName = "Cgroup" + + def config(self): + try: + cfo = configFileOps("/etc/cgconfig.conf", self) + addConfig = "group virt {\n \ + cpu {\n \ + cpu.shares = 9216;\n \ + }\n \ + }\n" + cfo.add_lines(addConfig) + + self.syscfg.svo.stopService("cgconfig") + self.syscfg.svo.enableService("cgconfig",forcestart=True) + + cfo = configFileOps("/etc/cgrules.conf", self) + cfgline = "root:/usr/sbin/libvirtd cpu virt/\n" + cfo.add_lines(cfgline) + + self.syscfg.svo.stopService("cgred") + if not self.syscfg.svo.enableService("cgred"): + return False + return True + except: + raise + + def restore(self): + try: + self.syscfg.svo.stopService("cgconfig") + self.syscfg.svo.enableService("cgconfig",forcestart=True) + self.syscfg.svo.stopService("cgred") + self.syscfg.svo.enableService("cgred") + return True + except: + logging.debug(formatExceptionInfo()) + return False + +class securityPolicyConfigUbuntu(serviceCfgBase): + def __init__(self, syscfg): + super(securityPolicyConfigUbuntu, self).__init__(syscfg) + self.serviceName = "Apparmor" + + def config(self): + try: + if bash("service apparmor status").getStdout() == "": + self.spRunning = False + return True + + bash("service apparmor stop") + + bash("update-rc.d -f apparmor remove") + return True + except: + raise CloudRuntimeException("Failed to configure apparmor, please see the /var/log/cloud/setupAgent.log for detail, \ + or you can manually disable it before starting myCloud") + + def restore(self): + try: + self.syscfg.svo.enableService("apparmor") + self.syscfg.svo.startService("apparmor") + return True + except: + logging.debug(formatExceptionInfo()) + return False + +class securityPolicyConfigRedhat(serviceCfgBase): + def __init__(self, syscfg): + super(securityPolicyConfigRedhat, self).__init__(syscfg) + self.serviceName = "SElinux" + + def config(self): + selinuxEnabled = True + + if not bash("selinuxenabled").isSuccess(): + selinuxEnabled = False + + if selinuxEnabled: + try: + bash("setenforce 0") + cfo = configFileOps("/etc/selinux/config", self) + cfo.replace_line("SELINUX=", "SELINUX=permissive") + return True + except: + raise CloudRuntimeException("Failed to configure selinux, please see the /var/log/cloud/setupAgent.log for detail, \ + or you can manually disable it before starting myCloud") + + def restore(self): + try: + bash("setenforce 1") + return True + except: + logging.debug(formatExceptionInfo()) + return False + +class libvirtConfigRedhat(serviceCfgBase): + def __init__(self, syscfg): + super(libvirtConfigRedhat, self).__init__(syscfg) + self.serviceName = "Libvirt" + + def config(self): + try: + cfo = configFileOps("/etc/libvirt/libvirtd.conf", self) + cfo.addEntry("listen_tcp", "1") + cfo.addEntry("tcp_port", "\"16509\"") + cfo.addEntry("auth_tcp", "\"none\"") + cfo.addEntry("listen_tls", "0") + cfo.save() + + cfo = configFileOps("/etc/sysconfig/libvirtd", self) + cfo.addEntry("export CGROUP_DAEMON", "'cpu:/virt'") + cfo.addEntry("LIBVIRTD_ARGS", "-l") + cfo.save() + + cfgline = "cgroup_controllers = [ \"cpu\" ]\n" \ + "security_driver = \"none\"\n" + filename = "/etc/libvirt/qemu.conf" + + cfo = configFileOps(filename, self) + cfo.add_lines(cfgline) + + self.syscfg.svo.stopService("libvirtd") + if not self.syscfg.svo.startService("libvirtd"): + return False + + return True + except: + raise + + def restore(self): + pass + +class libvirtConfigUbuntu(serviceCfgBase): + def __init__(self, syscfg): + super(libvirtConfigUbuntu, self).__init__(syscfg) + self.serviceName = "Libvirt" + + def setupLiveMigration(self): + cfo = configFileOps("/etc/libvirt/libvirtd.conf", self) + cfo.addEntry("listen_tcp", "1") + cfo.addEntry("tcp_port", "16509") + cfo.addEntry("auth_tcp", "none") + cfo.addEntry("listen_tls", "0") + cfo.save() + + if os.path.exists("/etc/init/libvirt-bin.conf"): + cfo = configFileOps("/etc/init/libvirt-bin.conf", self) + cfo.replace_line("exec /usr/sbin/libvirtd","exec /usr/sbin/libvirtd -d -l") + else: + cfo = configFileOps("/etc/default/libvirt-bin", self) + cfo.replace_or_add_line("libvirtd_opts=","libvirtd_opts='-l -d'") + + def config(self): + try: + cfgline = "export CGROUP_DAEMON='cpu:/virt'" + libvirtfile = "/etc/default/libvirt-bin" + cfo = configFileOps(libvirtfile, self) + cfo.add_lines(cfgline) + + self.setupLiveMigration() + + cfgline = "cgroup_controllers = [ \"cpu\" ]\n" \ + "security_driver = \"none\"\n" + filename = "/etc/libvirt/qemu.conf" + + cfo = configFileOps(filename, self) + cfo.add_lines(cfgline) + + self.syscfg.svo.stopService("libvirt-bin") + self.syscfg.svo.enableService("libvirt-bin") + return True + except: + raise + + def restore(self): + try: + self.syscfg.svo.stopService("libvirt-bin") + self.syscfg.svo.startService("libvirt-bin") + return True + except: + logging.debug(formatExceptionInfo()) + return False + +class firewallConfigUbuntu(serviceCfgBase): + def __init__(self, syscfg): + super(firewallConfigUbuntu, self).__init__(syscfg) + self.serviceName = "Firewall" + + def config(self): + try: + ports = "22 1798 16509".split() + for p in ports: + bash("ufw allow %s"%p) + bash("ufw allow proto tcp from any to any port 5900:6100") + bash("ufw allow proto tcp from any to any port 49152:49216") + self.syscfg.svo.stopService("ufw") + self.syscfg.svo.startService("ufw") + return True + except: + raise + + def restore(self): + return True + +class firewallConfigBase(serviceCfgBase): + def __init__(self, syscfg): + super(firewallConfigBase, self).__init__(syscfg) + self.serviceName = "Firewall" + + def allowPort(self, port): + status = False + try: + status = bash("iptables-save|grep INPUT|grep -w %s"%port).isSuccess() + except: + pass + + if not status: + bash("iptables -I INPUT -p tcp -m tcp --dport %s -j ACCEPT"%port) + + def config(self): + try: + for port in self.ports: + self.allowPort(port) + + bash("iptables-save > /etc/sysconfig/iptables") + self.syscfg.svo.stopService("iptables") + self.syscfg.svo.startService("iptables") + return True + except: + raise + + def restore(self): + return True + +class firewallConfigAgent(firewallConfigBase): + def __init__(self, syscfg): + super(firewallConfigAgent, self).__init__(syscfg) + self.ports = "22 16509 5900:6100 49152:49216".split() + + + +class cloudAgentConfig(serviceCfgBase): + def __init__(self, syscfg): + super(cloudAgentConfig, self).__init__(syscfg) + if syscfg.env.agentMode == "Agent": + self.serviceName = "cloudAgent" + elif syscfg.env.agentMode == "myCloud": + self.serviceName = "myCloud" + elif syscfg.env.agentMode == "Console": + self.serviceName = "Console Proxy" + + def configMyCloud(self): + try: + cfo = configFileOps("/etc/cloud/agent/agent.properties", self) + cfo.addEntry("host", self.syscfg.env.mgtSvr) + cfo.addEntry("zone", self.syscfg.env.zone) + cfo.addEntry("port", "443") + cfo.addEntry("private.network.device", self.syscfg.env.nics[0]) + cfo.addEntry("public.network.device", self.syscfg.env.nics[1]) + cfo.addEntry("guest.network.device", self.syscfg.env.nics[2]) + cfo.addEntry("guid", str(self.syscfg.env.uuid)) + cfo.addEntry("mount.path", "/mnt") + cfo.addEntry("resource", "com.cloud.storage.resource.LocalSecondaryStorageResource|com.cloud.agent.resource.computing.CloudZonesComputingResource") + cfo.save() + + self.syscfg.svo.stopService("cloud-agent") + self.syscfg.svo.startService("cloud-agent") + return True + except: + raise + + def configAgent(self): + try: + cfo = configFileOps("/etc/cloud/agent/agent.properties", self) + cfo.addEntry("host", self.syscfg.env.mgtSvr) + cfo.addEntry("zone", self.syscfg.env.zone) + cfo.addEntry("pod", self.syscfg.env.pod) + cfo.addEntry("cluster", self.syscfg.env.cluster) + cfo.addEntry("port", "8250") + cfo.addEntry("private.network.device", self.syscfg.env.nics[0]) + cfo.addEntry("public.network.device", self.syscfg.env.nics[1]) + cfo.addEntry("guest.network.device", self.syscfg.env.nics[2]) + cfo.addEntry("guid", str(self.syscfg.env.uuid)) + cfo.addEntry("resource", "com.cloud.agent.resource.computing.LibvirtComputingResource") + cfo.save() + + self.syscfg.svo.stopService("cloud-agent") + self.syscfg.svo.startService("cloud-agent") + return True + except: + raise + + def configConsole(self): + try: + cfo = configFileOps("/etc/cloud/agent/agent.properties", self) + cfo.addEntry("host", self.syscfg.env.mgtSvr) + cfo.addEntry("zone", self.syscfg.env.zone) + cfo.addEntry("pod", self.syscfg.env.pod) + cfo.addEntry("cluster", self.syscfg.env.cluster) + cfo.addEntry("port", "8250") + cfo.addEntry("private.network.device", self.syscfg.env.nics[0]) + cfo.addEntry("public.network.device", self.syscfg.env.nics[1]) + cfo.addEntry("guest.network.device", self.syscfg.env.nics[2]) + cfo.addEntry("guid", str(self.syscfg.env.uuid)) + cfo.addEntry("resource", "com.cloud.agent.resource.computing.consoleProxyResource") + cfo.save() + + self.syscfg.svo.stopService("cloud-agent") + self.syscfg.svo.startService("cloud-agent") + return True + except: + raise + + def config(self): + if self.syscfg.env.agentMode == "Agent": + return self.configAgent() + elif self.syscfg.env.agentMode == "myCloud": + return self.configMyCloud() + elif self.syscfg.env.agentMode == "console": + return self.configConsole() + + def restore(self): + return True + + +class sudoersConfig(serviceCfgBase): + def __init__(self, syscfg): + super(sudoersConfig, self).__init__(syscfg) + self.serviceName = "sudoers" + def config(self): + try: + cfo = configFileOps("/etc/sudoers", self) + cfo.addEntry("cloud ALL ", "NOPASSWD : ALL") + cfo.rmEntry("Defaults", "requiretty", " ") + cfo.save() + return True + except: + raise + + def restore(self): + return True + +class firewallConfigServer(firewallConfigBase): + def __init__(self, syscfg): + super(firewallConfigServer, self).__init__(syscfg) + if self.syscfg.env.svrMode == "myCloud": + self.ports = "443 8080 8096 8250 8443".split() + else: + self.ports = "8080 8096 8250".split() diff --git a/python/lib/cloudutils/serviceConfigServer.py b/python/lib/cloudutils/serviceConfigServer.py new file mode 100644 index 00000000000..781e7e389c0 --- /dev/null +++ b/python/lib/cloudutils/serviceConfigServer.py @@ -0,0 +1,74 @@ +from db import Database +from configFileOps import configFileOps +from serviceConfig import serviceCfgBase +from cloudException import CloudRuntimeException, CloudInternalException +from utilities import bash +import os + +class cloudManagementConfig(serviceCfgBase): + def __init__(self, syscfg): + super(cloudManagementConfig, self).__init__(syscfg) + self.serviceName = "CloudStack Management Server" + + def config(self): + if self.syscfg.env.svrMode == "mycloud": + cfo = configFileOps("/usr/share/cloud/management/conf/environment.properties", self) + cfo.addEntry("cloud-stack-components-specification", "components-cloudzones.xml") + cfo.save() + + cfo = configFileOps("/usr/share/cloud/management/conf/db.properties", self) + dbHost = cfo.getEntry("db.cloud.host") + dbPort = cfo.getEntry("db.cloud.port") + dbUser = cfo.getEntry("db.cloud.username") + dbPass = cfo.getEntry("db.cloud.password") + if dbPass.strip() == "": + dbPass = None + dbName = cfo.getEntry("db.cloud.name") + db = Database(dbUser, dbPass, dbHost, dbPort, dbName) + + try: + db.testConnection() + except CloudRuntimeException, e: + raise e + except: + raise CloudInternalException("Failed to connect to Mysql server") + + try: + statement = """ UPDATE configuration SET value='%s' WHERE name='%s'""" + + db.execute(statement%('true','use.local.storage')) + db.execute(statement%('20','max.template.iso.size')) + + statement = """ UPDATE vm_template SET url='%s',checksum='%s' WHERE id='%s' """ + db.execute(statement%('https://rightscale-cloudstack.s3.amazonaws.com/kvm/RightImage_CentOS_5.4_x64_v5.6.28.qcow2.bz2', '90fcd2fa4d3177e31ff296cecb9933b7', '4')) + + statement="""UPDATE disk_offering set use_local_storage=1""" + db.execute(statement) + except: + raise e + + #add DNAT 443 to 8250 + if not bash("iptables-save |grep PREROUTING | grep 8250").isSuccess(): + bash("iptables -A PREROUTING -t nat -p tcp --dport 443 -j REDIRECT --to-port 8250 ") + + #generate keystore + keyPath = "/var/lib/cloud/management/web.keystore" + if not os.path.exists(keyPath): + cmd = bash("keytool -genkey -keystore %s -storepass \"cloud.com\" -keypass \"cloud.com\" -validity 3650 -dname cn=\"Cloudstack User\",ou=\"mycloud.cloud.com\",o=\"mycloud.cloud.com\",c=\"Unknown\""%keyPath) + + if not cmd.isSuccess(): + raise CloudInternalException(cmd.getErrMsg()) + + cfo = configFileOps("/etc/cloud/management/tomcat6.conf", self) + cfo.add_lines("JAVA_OPTS+=\" -Djavax.net.ssl.trustStore=%s \""%keyPath) + + try: + self.syscfg.svo.disableService("tomcat6") + except: + pass + + if self.syscfg.svo.enableService("cloud-management"): + return True + else: + raise CloudRuntimeException("Failed to configure %s, please see the /var/log/cloud/setupManagement.log for detail"%self.serviceName) + diff --git a/python/lib/cloudutils/syscfg.py b/python/lib/cloudutils/syscfg.py new file mode 100644 index 00000000000..8956e08a3b9 --- /dev/null +++ b/python/lib/cloudutils/syscfg.py @@ -0,0 +1,141 @@ +from utilities import DistributionDetector, serviceOpsRedhat,serviceOpsUbuntu +from serviceConfig import * +class sysConfigFactory: + @staticmethod + def getSysConfigFactory(glbEnv): + if glbEnv.mode == "Agent": + return sysConfigAgentFactory.getAgent(glbEnv) + elif glbEnv.mode == "Server": + return sysConfigServerFactory.getServer(glbEnv) + elif glbEnv.mode == "Db": + return sysConfigDbFactory.getDb(glbEnv) + else: + raise CloudInternalException("Need to specify which mode are u running: Agent/Server/Db") + +class sysConfigAgentFactory: + @staticmethod + def getAgent(glbEnv): + distribution = DistributionDetector().getVersion() + if distribution == "Ubuntu": + return sysConfigAgentUbuntu(glbEnv) + elif distribution == "Fedora": + return sysConfigRedhat6(glbEnv) + elif distribution == "CentOS" or distribution == "RHEL5": + return sysConfigRedhat5(glbEnv) + else: + print "Can't find the distribution version" + return sysConfig() + +class sysConfigServerFactory: + @staticmethod + def getServer(glbEnv): + distribution = DistributionDetector().getVersion() + if distribution == "Ubuntu": + return sysConfigServerUbuntu(glbEnv) + elif distribution != "Unknown": + return sysConfigServerRedhat(glbEnv) + else: + print "Can't find the distribution version" + return sysConfig() + +class sysConfigDbFactory: + @staticmethod + def getDb(glbEnv): + pass + +class sysConfig(object): + def __init__(self, env): + self.env = env + self.services = [] + + def registerService(self, service): + self.services.append(service) + + def config(self): + if not self.check(): + return False + + for service in self.services: + if not service.configration(): + raise CloudInternalException() + + def restore(self): + for service in self.services: + service.backup() + + def check(self): + return True + +class sysConfigAgent(sysConfig): + def check(self): + if self.env.debug: + return True + + if os.geteuid() != 0: + raise CloudInternalException("Need to execute with root permission") + + kvmEnabled = self.svo.isKVMEnabled() + if not kvmEnabled: + raise CloudInternalException("Checking KVM...[Failed]\nPlease enable KVM on this machine\n") + + return True + + +class sysConfigAgentRedhatBase(sysConfigAgent): + def __init__(self, env): + self.svo = serviceOpsRedhat() + super(sysConfigAgentRedhatBase, self).__init__(env) + +class sysConfigAgentUbuntu(sysConfigAgent): + def __init__(self, glbEnv): + super(sysConfigAgentUbuntu, self).__init__(glbEnv) + self.svo = serviceOpsUbuntu() + + self.services = [cgroupConfig(self), + securityPolicyConfigUbuntu(self), + networkConfigUbuntu(self), + libvirtConfigUbuntu(self), + firewallConfigUbuntu(self), + cloudAgentConfig(self)] + +#it covers RHEL6/Fedora13/Fedora14 +class sysConfigRedhat6(sysConfigAgentRedhatBase): + def __init__(self, glbEnv): + super(sysConfigRedhat6, self).__init__(glbEnv) + self.services = [cgroupConfig(self), + securityPolicyConfigRedhat(self), + networkConfigRedhat(self), + libvirtConfigRedhat(self), + firewallConfigAgent(self), + cloudAgentConfig(self)] + +#It covers RHEL5/CentOS5, the mainly difference is that there is no cgroup +class sysConfigRedhat5(sysConfigAgentRedhatBase): + def __init__(self, glbEnv): + super(sysConfigRedhat5, self).__init__(glbEnv) + self.services = [ + securityPolicyConfigRedhat(self), + networkConfigRedhat(self), + libvirtConfigRedhat(self), + firewallConfigAgent(self), + cloudAgentConfig(self)] + +class sysConfigServer(sysConfig): + def check(self): + if os.geteuid() != 0: + raise CloudInternalException("Need to execute with root permission") + return True + +class sysConfigServerRedhat(sysConfigServer): + def __init__(self, glbEnv): + super(sysConfigServerRedhat, self).__init__(glbEnv) + self.svo = serviceOpsRedhat() + self.services = [sudoersConfig(self), + firewallConfigServer(self)] + +class sysConfigServerUbuntu(sysConfigServer): + def __init__(self, glbEnv): + super(sysConfigServerUbuntu, self).__init__(glbEnv) + self.svo = serviceOpsUbuntu() + self.services = [sudoersConfig(self), + firewallConfigServer(self)] diff --git a/python/lib/cloudutils/utilities.py b/python/lib/cloudutils/utilities.py new file mode 100644 index 00000000000..3aca6d5d880 --- /dev/null +++ b/python/lib/cloudutils/utilities.py @@ -0,0 +1,175 @@ +from cloudException import CloudRuntimeException, formatExceptionInfo +import logging +from subprocess import PIPE, Popen +from signal import alarm, signal, SIGALRM, SIGKILL +import sys +import os +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: + logging.debug("Failed to execute:" + self.getErrMsg()) + + 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 getErrMsg(self): + if self.isSuccess(): + return "" + + if self.getStderr() is None or self.getStderr() == "": + return self.getStdout() + else: + return self.getStderr() + +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): + if msg is not None: + output = "%-30s"%msg + elif result is True: + output = "[%-2s]\n"%"OK" + elif result is False: + output = "[%-6s]\n"%"Failed" + sys.stdout.write(output) + sys.stdout.flush() + +class DistributionDetector: + def __init__(self): + self.distro = "Unknown" + + if os.path.exists("/etc/fedora-release"): + self.distro = "Fedora" + elif os.path.exists("/etc/centos-release"): + self.distro = "CentOS" + elif os.path.exists("/etc/redhat-release"): + version = file("/etc/redhat-release").readline() + if version.find("Red Hat Enterprise Linux Server release 6") != -1: + self.distro = "RHEL6" + elif version.find("CentOS release") != -1: + self.distro = "CentOS" + else: + self.distro = "RHEL5" + elif os.path.exists("/etc/legal") and "Ubuntu" in file("/etc/legal").read(-1): + self.distro = "Ubuntu" + else: + self.distro = "Unknown" + + def getVersion(self): + return self.distro + +class serviceOps: + pass +class serviceOpsRedhat(serviceOps): + def isServiceRunning(self, servicename): + try: + o = bash("service " + servicename + " status") + if "start/running" in o.getStdout(): + return True + else: + return False + except: + return False + + def stopService(self, servicename,force=False): + if self.isServiceRunning(servicename) or force: + return bash("service " + servicename +" stop").isSuccess() + + return True + def disableService(self, servicename): + result = self.stopService(servicename) + bash("chkconfig --del " + servicename) + return result + + def startService(self, servicename,force=False): + if not self.isServiceRunning(servicename) or force: + return bash("service " + servicename + " start").isSuccess() + return True + + def enableService(self, servicename,forcestart=False): + bash("chkconfig --level 2345 " + servicename + " on") + return self.startService(servicename,force=forcestart) + + def isKVMEnabled(self): + if os.path.exists("/dev/kvm"): + return True + else: + return False + +class serviceOpsUbuntu(serviceOps): + def isServiceRunning(self, servicename): + try: + o = bash("service " + servicename + " status") + if "start/running" in o.getStdout(): + return True + else: + return False + except: + return False + + def stopService(self, servicename,force=False): + if self.isServiceRunning(servicename) or force: + return bash("service " + servicename +" stop").isSuccess() + + def disableService(self, servicename): + result = self.stopService(servicename) + bash("update-rc.d -f " + servicename + " remove") + return result + + def startService(self, servicename,force=False): + if not self.isServiceRunning(servicename) or force: + return bash("service " + servicename + " start").isSuccess() + + def enableService(self, servicename,forcestart=False): + bash("update-rc.d -f " + servicename + " remove") + bash("update-rc.d -f " + servicename + " start 2 3 4 5 .") + return self.startService(servicename,force=forcestart) + + def isKVMEnabled(self): + return bash("kvm-ok").isSuccess() \ No newline at end of file diff --git a/wscript_build b/wscript_build index 877e32094d9..481a7a599c7 100644 --- a/wscript_build +++ b/wscript_build @@ -343,6 +343,7 @@ def build_xml_api_description (): ) bld.install_files("${PYTHONDIR}/cloudtool", 'cloud-cli/cloudtool/*') + bld.install_files("${PYTHONDIR}/cloudutils", 'python/lib/cloudutils/*') bld.install_as("${PYTHONDIR}/cloudapis.py", 'cloud-cli/cloudapis/cloud.py')