From 4633020bdba2c6620eee5036862e1d3511985411 Mon Sep 17 00:00:00 2001 From: Fred Date: Sat, 4 Aug 2012 02:28:44 +0800 Subject: [PATCH] Moodle install has been split in two methods init and install --- lib/moodle.py | 74 ++++++++++++++++++++++++--- lib/workplace.py | 107 +++++++++++++++++++++++++++++++++++++-- moodle-init.py | 78 ++++++++++++++++++++++++++++ moodle-install.py | 148 +++++++----------------------------------------------- 4 files changed, 264 insertions(+), 143 deletions(-) create mode 100755 moodle-init.py diff --git a/lib/moodle.py b/lib/moodle.py index a345c4c..0d87e2a 100644 --- a/lib/moodle.py +++ b/lib/moodle.py @@ -29,7 +29,9 @@ class Moodle(): self._load() def addConfig(self, name, value): - if not self.get('installed'): + """Add a parameter to the config file""" + configFile = os.path.join(self.path, 'config.php') + if not os.path.isfile(configFile): return None if type(value) != 'int': @@ -37,16 +39,18 @@ class Moodle(): value = str(value) try: - f = open(os.path.join(self.path, 'config.php'), 'a+') + f = open(configFile, 'a') f.write('\n$CFG->%s = %s;' % (name, value)) f.close() except: raise Exception('Error while writing to config file') def branch(self): + """Returns the current branch on the git repository""" return self._git().currentBranch() def cli(self, cli, args = '', **kwargs): + """Executes a command line tool script""" cli = os.path.join(self.get('path'), cli.lstrip('/')) if not os.path.isfile(cli): raise Exception('Could not find script to call') @@ -56,6 +60,7 @@ class Moodle(): return process(cmd, cwd=self.get('path'), **kwargs) def dbo(self): + """Returns a Database object""" if self._dbo == None: engine = self.get('dbtype') db = self.get('dbname') @@ -67,6 +72,7 @@ class Moodle(): return self._dbo def get(self, param, default = None): + """Returns a property of this instance""" info = self.info() try: return info[param] @@ -74,11 +80,13 @@ class Moodle(): return default def git(self): + """Returns a Git object""" if self._git == None: self._git = Git(self.path, C('git')) return self._git def initPHPUnit(self): + """Initialise the PHP Unit environment""" result = (None, None, None) exception = False try: @@ -95,19 +103,69 @@ class Moodle(): raise Exception('Error while calling PHP Unit init script') def info(self): + """Returns a dictionary of information about this instance""" self._load() - info = {} - info['identifier'] = self.identifier - info['path'] = self.path - info['installed'] = self.installed + info = { + 'path': self.path, + 'installed': self.installed, + 'identifier': self.identifier + } for (k, v) in self.config.items(): info[k] = v for (k, v) in self.version.items(): info[k] = v return info + def install(self, dbname = None, engine = None, dataDir = None, fullname = None, dropDb = False): + """Launch the install script of an Instance""" + + if self.installed: + raise Exception('Instance already installed!') + + if dataDir == None or not os.path.isdir(dataDir): + raise Exception('Cannot install instance without knowing where the data directory is') + if dbname == None: + dbname = self.identifier + if engine == None: + engine = C('defaultEngine') + if fullname == None: + fullname = self.identifier.replace('-', ' ').replace('_', ' ').title() + + debug('Creating database...') + if dbname == None: + dbname = re.sub(r'[^a-zA-Z0-9]', '', self.identifier).lower()[:28] + db = DB(engine, C('db.%s' % engine)) + if db.dbexists(dbname): + if dropDb: + db.dropdb(dbname) + db.createdb(dbname) + else: + raise Exception('Cannot install an instance on an existing database (%s)' % dbname) + else: + db.createdb(dbname) + db.selectdb(dbname) + + debug('Installing %s...' % self.identifier) + cli = 'admin/cli/install.php' + params = (C('host'), self.identifier, dataDir, engine, dbname, C('db.%s.user' % engine), C('db.%s.passwd' % engine), C('db.%s.host' % engine), fullname, self.identifier, C('login'), C('passwd')) + args = '--wwwroot="http://%s/%s/" --dataroot="%s" --dbtype="%s" --dbname="%s" --dbuser="%s" --dbpass="%s" --dbhost="%s" --fullname="%s" --shortname="%s" --adminuser="%s" --adminpass="%s" --allow-unstable --agree-license --non-interactive' % params + result = self.cli(cli, args, stdout=None, stderr=None) + if result[0] != 0: + raise Exception('Error while running the install, please manually fix the problem.') + + configFile = os.path.join(self.path, 'config.php') + os.chmod(configFile, 0666) + try: + self.addConfig('sessioncookiepath', '/%s/' % self.identifier) + except Exception as e: + print e + debug('Could not append $CFG->sessioncookiepath to config.php') + + self.reload() + @staticmethod def isInstance(path): + """Check whether the path is a Moodle web directory""" version = os.path.join(path, 'version.php') try: f = open(version, 'r') @@ -126,7 +184,7 @@ class Moodle(): return True def _load(self): - + """Loads the information""" if not self.isInstance(self.path): return False @@ -196,6 +254,6 @@ class Moodle(): return True def reload(self): + """Reloads the information""" self._loaded = False return self._load() - diff --git a/lib/workplace.py b/lib/workplace.py index b0be048..2117870 100644 --- a/lib/workplace.py +++ b/lib/workplace.py @@ -4,7 +4,7 @@ import os import shutil -from tools import debug +from tools import debug, process import config import db import moodle @@ -24,15 +24,96 @@ class Workplace(): if not os.path.isdir(path): raise Exception('Directory %s not found' % path) + # Directory paths self.path = os.path.abspath(os.path.realpath(path)) + self.cache = os.path.abspath(os.path.realpath(C('dirs.cache'))) + self.www = os.path.abspath(os.path.realpath(C('dirs.www'))) + + # Directory names self.wwwDir = wwwDir self.dataDir = dataDir + def checkCachedClones(self, stable = True, integration = True): + """Clone the official repository in a local cache""" + cacheStable = os.path.join(self.cache, 'moodle.git') + cacheIntegration = os.path.join(self.cache, 'integration.git') + if not os.path.isdir(cacheStable) and stable: + debug('Cloning stable repository into cache...') + result = process('%s clone %s %s' % (C('git'), C('remotes.stable'), cacheStable)) + result = process('%s fetch -a' % C('git'), cacheStable) + if not os.path.isdir(cacheIntegration) and integration: + debug('Cloning integration repository into cache...') + result = process('%s clone %s %s' % (C('git'), C('remotes.integration'), cacheIntegration)) + result = process('%s fetch -a' % C('git'), cacheIntegration) + + def create(self, name = None, version = 'master', integration = False, useCacheAsRemote = False): + """Creates a new instance of Moodle""" + if name == None: + if integration: + name = C('wording.prefixIntegration') + prefixVersion + else: + name = C('wording.prefixStable') + prefixVersion + + installDir = os.path.join(self.path, name) + wwwDir = os.path.join(installDir, self.wwwDir) + dataDir = os.path.join(installDir, self.dataDir) + linkDir = os.path.join(self.www, name) + + if self.isMoodle(name): + raise Exception('This Moodle instance %s already exists' % name) + elif os.path.isdir(installDir): + raise Exception('Installation path exists: %s' % installDir) + + self.checkCachedClones(not integration, integration) + os.mkdir(installDir, 0755) + os.mkdir(wwwDir, 0755) + os.mkdir(dataDir, 0777) + + if integration: + repository = os.path.join(C('dirs.cache'), 'integration.git') + else: + repository = os.path.join(C('dirs.cache'), 'moodle.git') + + # Clone the instances + debug('Cloning repository...') + if useCacheAsRemote: + result = process('%s clone %s %s' % (C('git'), repository, wwwDir)) + else: + shutil.copytree(repository, wwwDir) + + # Symbolic link + if os.path.islink(linkDir): + os.remove(linkDir) + if os.path.isfile(linkDir) or os.path.isdir(linkDir): # No elif! + debug('Could not create symbolic link') + debug('Please manually create: ln -s %s %s' (wwwDir, linkDir)) + else: + os.symlink(wwwDir, linkDir) + + # Creating, fetch, pulling branches + debug('Checking out branch...') + M = self.get(name) + git = M.git() + result = git.fetch('origin') + if version == 'master': + git.checkout('master') + else: + track = 'origin/MOODLE_%s_STABLE' % version + branch = 'MOODLE_%s_STABLE' % version + if not git.createBranch(branch, track): + debug('Could not create branch %s tracking %s' % (branch, track)) + else: + git.checkout(branch) + git.pull() + git.addRemote('mine', C('remotes.mine')) + + return M + def delete(self, name): + """Completely remove an instance, database included""" + # Instantiating the object also checks if it exists M = self.get(name) - DB = M.dbo() - dbname = M.get('dbname') # Deleting the whole thing shutil.rmtree(os.path.join(self.path, name)) @@ -46,11 +127,13 @@ class Workplace(): pass # Delete db + DB = M.dbo() + dbname = M.get('dbname') if DB and dbname: DB.dropdb(dbname) def get(self, name): - + """Returns an instance defined by its name, or by path""" # Extracts name from path if os.sep in name: path = os.path.abspath(os.path.realpath(name)) @@ -62,7 +145,18 @@ class Workplace(): raise Exception('Could not find Moodle instance %s' % name) return moodle.Moodle(os.path.join(self.path, name, self.wwwDir), identifier = name) + def getPath(self, name, mode = None): + """Returns the path of an instance base on its name""" + base = os.path.join(self.path, name) + if mode == 'www': + return os.path.join(base, self.wwwDir) + elif mode == 'data': + return os.path.join(base, self.dataDir) + else: + return base + def isMoodle(self, name): + """Checks whether a Moodle instance exist under this name""" d = os.path.join(self.path, name) if not os.path.isdir(d): return False @@ -78,6 +172,7 @@ class Workplace(): return True def list(self): + """Return the list of Moodle instances""" dirs = os.listdir(self.path) names = [] for d in dirs: @@ -87,3 +182,7 @@ class Workplace(): names.append(d) return names + + def updateCachedClones(self, integration = True, stable = True): + """Update the cached clone of the repositories""" + pass diff --git a/moodle-init.py b/moodle-init.py new file mode 100755 index 0000000..184fbbe --- /dev/null +++ b/moodle-init.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os +import argparse + +from lib import config, db, moodle, workplace +from lib.tools import debug, yesOrNo + +DB = db.DB +C = config.Conf().get +Wp = workplace.Workplace() + +# Arguments +parser = argparse.ArgumentParser(description='Creates a new instance of Moodle') +parser.add_argument('-t', '--integration', action='store_true', help='create an instance from integration') +parser.add_argument('-i', '--install', action='store_true', help='launch the installation script after creating the instance', dest='install') +parser.add_argument('-v', '--version', action='store', choices=[ str(x) for x in range(13, C('masterBranch')) ] + ['master'], default='master', help='version of Moodle', metavar='version') +parser.add_argument('-s', '--suffix', action='store', help='suffix for the instance name', metavar='suffix') +parser.add_argument('-e', '--engine', action='store', choices=['mysqli', 'pgsql'], default=C('defaultEngine'), help='database engine to use', metavar='engine') +args = parser.parse_args() + +engine = args.engine +version = args.version + +# Wording version +prefixVersion = version +versionNice = version +if version == 'master': + prefixVersion = C('wording.prefixMaster') + versionNice = C('wording.master') + +# Generating names +if args.integration: + name = C('wording.prefixIntegration') + prefixVersion + fullname = C('wording.integration') + ' ' + versionNice + ' ' + C('wording.%s' % engine) +else: + name = C('wording.prefixStable') + prefixVersion + fullname = C('wording.stable') + ' ' + versionNice + ' ' + C('wording.%s' % engine) + +# Append the suffix +if args.suffix: + name += C('wording.suffixSeparator') + args.suffix + fullname += ' ' + args.suffix.replace('-', ' ').replace('_', ' ').title() + +# Create the instance +debug('Creating instance %s...' % name) +kwargs = { + 'name': name, + 'version': version, + 'integration': args.integration, + 'useCacheAsRemote': C('useCacheAsRemote') +} +M = Wp.create(**kwargs) + +# Run the install script +if args.install: + + # Checking database + dbname = re.sub(r'[^a-zA-Z0-9]', '', name).lower()[:28] + db = DB(engine, C('db.%s' % engine)) + dropDb = False + if db.dbexists(dbname): + debug('Database already exists (%s)' % dbname) + dropDb = yesOrNo('Do you want to remove it?') + + # Install + kwargs = { + 'engine': engine, + 'dbname': dbname, + 'dropDb': dropDb, + 'fullname': fullname, + 'dataDir': Wp.getPath(name, 'data') + } + M.install(**kwargs) + +debug('Process complete!') diff --git a/moodle-install.py b/moodle-install.py index c4ada0c..a50a15f 100755 --- a/moodle-install.py +++ b/moodle-install.py @@ -3,12 +3,10 @@ import sys import os -import shutil import argparse -import re from lib import config, db, moodle, workplace -from lib.tools import debug, process, yesOrNo +from lib.tools import debug DB = db.DB C = config.Conf().get @@ -16,140 +14,28 @@ Wp = workplace.Workplace() # Arguments parser = argparse.ArgumentParser(description='Install a Moodle instance') -parser.add_argument('-i', '--integration', action='store_true', help='create an instance from integration') -parser.add_argument('-v', '--version', action='store', choices=[ str(x) for x in range(13, C('masterBranch')) ] + ['master'], default='master', help='version of Moodle', metavar='version') parser.add_argument('-e', '--engine', action='store', choices=['mysqli', 'pgsql'], default=C('defaultEngine'), help='database engine to use', metavar='engine') -parser.add_argument('-s', '--suffix', action='store', help='suffix for the instance name', metavar='suffix') -parser.add_argument('--interactive', action='store_true', help='interactive mode') -parser.add_argument('--no-install', action='store_true', help='disable the installation', dest='noinstall') +parser.add_argument('-y', '--non-interactive', action='store_true', help='non-interactive mode', dest='noninteractive') +parser.add_argument('-f', '--fullname', action='store', help='full name of the instance') +parser.add_argument('name', metavar='name', help='name of the instance') args = parser.parse_args() +name = args.name engine = args.engine -version = args.version +fullname = args.fullname -cacheStable = os.path.join(C('dirs.cache'), 'moodle.git') -cacheIntegration = os.path.join(C('dirs.cache'), 'integration.git') +if not Wp.isMoodle(name): + debug('Instance %s does not exist. Exiting...' % name) + sys.exit(1) -# Cloning/caching repositories if necessary -if not os.path.isdir(cacheStable): - result = process('%s clone %s %s' % (C('git'), C('remotes.stable'), cacheStable)) - result = process('%s fetch -a' % C('git'), cacheStable) -if not os.path.isdir(cacheIntegration): - result = process('%s clone %s %s' % (C('git'), C('remotes.integration'), cacheIntegration)) - result = process('%s fetch -a' % C('git'), cacheIntegration) - -# Wording version -prefixVersion = version -versionNice = version -if version == 'master': - prefixVersion = C('wording.prefixMaster') - versionNice = C('wording.master') - -# Generating parameters -if args.integration: - name = C('wording.prefixIntegration') + prefixVersion - fullname = C('wording.integration') + ' ' + versionNice + ' ' + C('wording.%s' % engine) - repository = cacheIntegration -else: - name = C('wording.prefixStable') + prefixVersion - fullname = C('wording.stable') + ' ' + versionNice + ' ' + C('wording.%s' % engine) - repository = cacheStable - -# Append the suffix -if args.suffix: - name += C('wording.suffixSeparator') + args.suffix - fullname += ' ' + args.suffix.replace('-', ' ').replace('_', ' ').title() - -installDir = os.path.join(C('dirs.store'), name) -wwwDir = os.path.join(installDir, C('wwwDir')) -dataDir = os.path.join(installDir, C('dataDir')) -linkDir = os.path.join(C('dirs.www'), name) - -# Cloning the repository -skipClone = False -debug('Preparing instance directories...') -if os.path.isdir(installDir): - debug('Installation directory exists (%s)' % installDir) - if Wp.isMoodle(name): - if not args.interactive or yesOrNo('This is a Moodle instance, do you want to completely remove it?'): - debug('Deleting instance %s' % name) - Wp.delete(name) - else: - skipClone = True - elif not args.interactive or yesOrNo('Do you want to remove it?'): - debug('Removing directory %s' % installDir) - shutil.rmtree(installDir) - else: - debug('We cannot install an instance if the directory exists. Exiting...') - sys.exit(1) - -if not skipClone: - os.mkdir(installDir, 0755) +dataDir = Wp.getPath(name, 'data') +if not os.path.isdir(dataDir): os.mkdir(dataDir, 0777) - if C('useCacheAsRemote'): - result = process('%s clone %s %s' % (C('git'), repository, wwwDir)) - else: - shutil.copytree(repository, wwwDir) -# Checking database -debug('Preparing database...') -dbname = re.sub(r'[^a-zA-Z0-9]', '', name).lower()[:28] -db = DB(engine, C('db.%s' % engine)) -if db.dbexists(dbname): - debug('Database already exists (%s)' % dbname) - if not args.interactive or yesOrNo('Do you want to remove it?'): - db.dropdb(dbname) - debug('Creating database %s' % dbname) - db.createdb(dbname) - else: - debug('We cannot install an instance on an existing database. Exiting...') - sys.exit(1) -else: - debug('Creating database %s' % dbname) - db.createdb(dbname) -db.selectdb(dbname) - -# Installing -if os.path.islink(linkDir): - os.remove(linkDir) -if os.path.isfile(linkDir) or os.path.isdir(linkDir): # No elif! - debug('Could not create symbolic link') - debug('Please manually create: ln -s %s %s' (wwwDir, linkDir)) -else: - os.symlink(wwwDir, linkDir) - -# Creating, fetch, pulling branches -debug('Setting up repository...') M = Wp.get(name) -git = M.git() -result = git.fetch('origin') -if version == 'master': - git.checkout('master') -else: - track = 'origin/MOODLE_%s_STABLE' % version - branch = 'MOODLE_%s_STABLE' % version - if not git.createBranch(branch, track): - raise Exception('Could not create branch %s tracking %s' % (branch, track)) - git.checkout(branch) -git.pull() -git.addRemote('mine', C('remotes.mine')) - -# Launching installation process -if not args.noinstall: - - debug('Installing %s...' % name) - cli = 'admin/cli/install.php' - params = (C('host'), name, dataDir, engine, dbname, C('db.%s.user' % engine), C('db.%s.passwd' % engine), C('db.%s.host' % engine), fullname, name, C('login'), C('passwd')) - args = '--wwwroot="http://%s/%s/" --dataroot="%s" --dbtype="%s" --dbname="%s" --dbuser="%s" --dbpass="%s" --dbhost="%s" --fullname="%s" --shortname="%s" --adminuser="%s" --adminpass="%s" --allow-unstable --agree-license --non-interactive' % params - result = M.cli(cli, args, stdout=None, stderr=None) - if result[0] != 0: - raise Exception('Error while running the install, please manually fix the problem.') - - configFile = os.path.join(wwwDir, 'config.php') - os.chmod(configFile, 0666) - try: - M.addConfig('sessioncookiepath', '/%s/' % name) - except Exception: - debug('Could not append $CFG->sessioncookiepath to config.php') - -debug('Process complete!') +kwargs = { + 'engine': engine, + 'fullname': fullname, + 'dataDir': dataDir +} +M.install(**kwargs) -- 2.11.0