#!/usr/bin/env python import os, sys, commands, optparse import time import re import shutil class ProvideInstaller(object): provideVersion = '0.8' dmVersion = '0.8' def __init__(self): self.parseOptions() # Set home path. self.homePath = os.path.abspath(self.options.home) if not self.options.quiet: print "" print "Installation option values:" print " path " + self.homePath print " domainname " + self.options.domainname print " secret " + self.options.secret print " database " + self.options.database print " username " + self.options.username print " password " + self.options.password print "" if self.options.verbose: print "Checking platform...." # Check domainname. if not len(self.options.domainname.split('.')) >= 2: self.exit("The domainname '%s' doesn't look like a top level domain, which means cookies won't work in your applications. :-( You can set a domainname with the '--domainname' option of this program. See --help for more information." % self.options.domainname) # Check filesystem. if self.options.verbose: print " Checking filesystem...." # Check home path. if not os.path.isdir(self.homePath): self.exit('Install directory not found: %s' % self.homePath) # Set release install path. self.baseProvidePath = os.path.join(self.homePath, '.provide') self.providePath = os.path.join(self.baseProvidePath, self.provideVersion) # Check releae install path. if os.path.exists(self.providePath): msg = "This version of Provide is already installed: %s\n" % self.providePath msg += "\n" msg += "Reinstallation will not be attempted. To remove this Provide installation, remove all the provided applications, drop the corresponding database, and remove the following directory.\n" msg += "\n" msg += self.providePath msg += "\n" msg += "\n" msg += "Make sure you really want to do this!!\n" self.exit(msg) if self.options.verbose: print " Filesystem checks out OK." # Check functionality. self.checkProg('cp') self.checkProg('env') self.checkProg('mkdir') self.checkProg('perl') self.checkProg('rm') self.checkProg('sleep') self.checkProg('tar') self.checkProg('wget') self.checkProg('python') self.checkProg('svn', 'subversion') self.checkProg('psql', 'postgresql') self.checkProg('easy_install', 'python-setuptools') self.checkProg('gcc', 'gcc') # Todo: Somehow check correct Python.h header is available. #self.checkProg('Python.h', 'python-dev') self.checkProg('zip', 'zip') self.checkProg('unzip', 'unzip') if self.options.verbose: print " Checking Python...." self.checkPython('PIL', 'python-imaging') self.checkPython('simplejson', 'python-simplejson') self.checkPython('mx.DateTime', 'python-egenix-mxdatetime') # Not sure if this is best set through easy_install or through aptitude. self.checkPython('psycopg', 'python-psycopg') if self.options.verbose: print " Python checks out OK." print if self.options.verbose: print "Platform checks out OK." print if not self.options.quiet: print "Installing to %s" % self.providePath # Create install directories. if not os.path.exists(self.baseProvidePath): os.mkdir(self.baseProvidePath) os.mkdir(self.providePath) os.mkdir(os.path.join(self.providePath, 'lib')) os.mkdir(os.path.join(self.providePath, 'lib', 'python')) if self.options.verbose: print "Augmenting PYTHONPATH environment variable..." PYTHONPATH = os.path.join(self.providePath, 'lib', 'python') if 'PYTHONPATH' in os.environ: PYTHONPATH += ":" + os.environ['PYTHONPATH'] os.environ['PYTHONPATH'] = PYTHONPATH if not self.options.quiet: print "Downloading and installing application files...." # Download and install Django. if self.options.verbose: print "Fetching Django distribution..." djangoTarPath = os.environ.get('ASF_DJANGO_DIST_1_1', '~/src/Django-1.1.tar.gz') if os.path.exists(djangoTarPath): cmd = "cp %s Django-1.1.tar.gz" % djangoTarPath self.system(cmd, "copy locally the django Python package") else: cmd = "wget -O Django-1.1.tar.gz http://www.djangoproject.com/download/1.1/tarball/" self.system(cmd, "download the django Python package") if self.options.verbose: print "Installing django..." cmd = "tar zxvf Django-1.1.tar.gz" self.system(cmd, "extract the django distribution") if os.chdir("Django-1.1"): self.exit("Couldn't change to extracted directory.") cmd = "python setup.py install --home=%s" % self.providePath self.system(cmd, "run django setup.py install") os.chdir('..') cmd = "rm -rf Django-1.1" self.system(cmd, "remove extracted Django distribution") cmd = "rm Django-1.1.tar.gz" self.system(cmd, "remove Django download.") # Download and install domainmodel. if self.options.verbose: print "Fetching domainmodel distribution..." cmd = "wget -O domainmodel-%s.tar.gz http://appropriatesoftware.net/provide/docs/domainmodel-%s.tar.gz" % (self.dmVersion, self.dmVersion) self.system(cmd, "download the domainmodel Python package") if self.options.verbose: print "Installing domainmodel..." cmd = "tar zxvf domainmodel-%s.tar.gz" % self.dmVersion self.system(cmd, "extract the domainmodel distribution") if os.chdir("domainmodel-%s" % self.dmVersion): self.exit("Couldn't change to extracted directory.") cmd = "python setup.py install --home=%s" % self.providePath self.system(cmd, "run domainmodel setup.py install") os.chdir('..') cmd = "rm -rf domainmodel-%s" % self.dmVersion self.system(cmd, "remove extracted domainmodel distribution") cmd = "rm domainmodel-%s.tar.gz" % self.dmVersion self.system(cmd, "remove domainmodel download.") # Download and install provide. if self.options.verbose: print "Fetching provide distribution..." cmd = "wget -O provide-%s.tar.gz http://appropriatesoftware.net/provide/docs/provide-%s.tar.gz" % (self.provideVersion, self.provideVersion) self.system(cmd, "download the provide Python package") if self.options.verbose: print "Installing provide..." cmd = "tar zxvf provide-%s.tar.gz" % self.provideVersion self.system(cmd, "extract the provide distribution") if os.chdir("provide-%s" % self.provideVersion): self.exit("Couldn't change to extracted directory.") cmd = "python setup.py install --home=%s" % self.providePath self.system(cmd, "run provide setup.py install") os.chdir('..') cmd = "rm -rf provide-%s" % self.provideVersion self.system(cmd, "remove extracted provide distribution") cmd = "rm provide-%s.tar.gz" % self.provideVersion self.system(cmd, "remove provide download") # Fixup installation. if os.chdir(self.providePath): self.exit("Couldn't change to installed system directory: %s" % self.providePath) # Fixup config file. if not self.options.quiet: print "Fixing up configuration...." if self.options.verbose: print "Creating new config file..." cmd = "cp etc/provide.conf.new etc/provide.conf" self.system(cmd, "copy provide.conf.new file") configCouples = [ ('/path/to/provide', self.providePath), ('^name = provide\S*', 'name = %s' % self.options.database), ('^user = provide', 'user = %s' % self.options.username), ('^password = pass', 'password = %s' % self.options.password), ('^domain_name = provide\.your\.domain', 'domain_name = %s' % self.options.domainname), ('^secret_key = \S+', 'secret_key = %s' % self.options.secret), ] self.replace('etc/provide.conf', configCouples) os.environ['PYTHONPATH'] = os.path.join(self.providePath, 'lib', 'python') os.environ['PROVIDE_SETTINGS'] = os.path.join(self.providePath, 'etc', 'provide.conf') os.environ['PATH'] = os.path.join(self.providePath, 'bin') + ":" + os.environ['PATH'] self.system('which provide', "find provide on the PATH (%s)" % os.environ['PATH']) if not self.options.quiet: print "Creating database and initialising model...." self.system("provide db create", "create database for provide system") self.system("provide db init", "initialise model for provide system") if not self.options.quiet: msg = "\n".join([ "", "Provide installed OK. Thanks for using Provide.", "", "Please paste these variables in your environment configuration:", "", "export PROVIDE_SETTINGS=%s/etc/provide.conf" % self.providePath, "export PYTHONPATH=%s/lib/python" % self.providePath, "export PATH=%s/bin:$PATH" % self.providePath, "", "Then you can run the provide commands:", "", "provide help", "provide help scripts", "provide scripts scanbooker 0.20", "", "provide-scanbooker-init", "provide-scanbooker-0.20-init", "provide-scanbooker-0.20-accept", "provide-scanbooker-0.20-production", "", "This would initialise a ScanBooker application service provision,", "aquire relase 0.20, and then test and deploy application services.", ]) print msg def parseOptions(self): parser = optparse.OptionParser() parser.add_option('-p', '--path', dest='home', default=os.environ['HOME'], help='path to existing folder (like ~ for example)') parser.add_option('-m', '--domain', dest='domainname', default='provide.%s' % commands.getoutput('hostname'), help='name of domain that will be served by provide') parser.add_option('-s', '--secret', dest='secret', default='this-is-not-a-secret', help='long random string, for security protocols') parser.add_option('-b', '--database', dest='database', default='provide-%s' % self.provideVersion, help='name of database to be created and used by provide') parser.add_option('-u', '--username', dest='username', default=os.environ['USER'], help='name of already existing database user') parser.add_option('-w', '--password', dest='password', default='pass', help='password of already existing database user') parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='run the installation verbosely.') parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='run the installation silently') (self.options, args) = parser.parse_args() def replace(self, filePath, couples): input = open(filePath,'rb') oldlines=input.readlines() input.close() for couple in couples: newlines = [] if self.options.verbose: print " substituting..." print " ", couple[0] print " ", couple[1] for oldline in oldlines: newline = re.sub(couple[0],couple[1], oldline) newlines.append(newline) oldlines = newlines backupPath=filePath+'~re~' output = open(backupPath, 'w') output.writelines(newlines) output.close() os.rename(backupPath, filePath) def system(self, cmd, msg): msg = "Unable to %s" % msg if self.options.verbose: print cmd if os.system(cmd): self.exit(msg + ".") else: (s, o) = commands.getstatusoutput(cmd) if s: if o: msg = "%s: %s" % (msg, o) else: msg = "%s." % (msg) self.exit(msg) def exit(self, msg, code=3): print '\nError: %s' % msg sys.exit(code) def checkProg(self, progName, osPackageName=None): # Todo: Attempt to sudo install indicated packages. cmd = 'which %s' % progName self.system(cmd, "find program '%s'" % progName) def checkPython(self, pyPackageName, osPackageName=None): # Todo: Attempt to sudo install indicated packages. # Todo: Provide alternative for easy_install. cmd = 'python -c "import %s" >/dev/null 2>&1' % pyPackageName msg = "find Python module '%s'.\n" % pyPackageName msg += '\n' msg += "Please install this Python module (in Debian, this package\n" msg += "is called '%s'). Then run this script again.\n\n" % osPackageName self.system(cmd, msg) if __name__ == "__main__": ProvideInstaller()