f89ccdae18339abe3910039011970a0946390274
[mdk.git] / mdk / commands / create.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Moodle Development Kit
6
7 Copyright (c) 2013 Frédéric Massart - FMCorz.net
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22 http://github.com/FMCorz/mdk
23 """
24
25 import re
26 import logging
27
28 from ..db import DB
29 from ..command import Command
30 from ..tools import yesOrNo
31 from ..exceptions import CreateException, InstallException
32
33
34 class CreateCommand(Command):
35
36 _description = 'Creates new instances of Moodle'
37
38 def __init__(self, *args, **kwargs):
39 super(CreateCommand, self).__init__(*args, **kwargs)
40 self._arguments = [
41 (
42 ['-i', '--install'],
43 {
44 'action': 'store_true',
45 'dest': 'install',
46 'help': 'launch the installation script after creating the instance'
47 }
48 ),
49 (
50 ['-e', '--engine'],
51 {
52 'action': 'store',
53 'choices': ['mariadb', 'mysqli', 'pgsql'],
54 'default': self.C.get('defaultEngine'),
55 'help': 'database engine to install the instance on, use with --install',
56 'metavar': 'engine'
57 }
58 ),
59 (
60 ['-t', '--integration'],
61 {
62 'action': 'store_true',
63 'help': 'create an instance from integration'
64 }
65 ),
66 (
67 ['-r', '--run'],
68 {
69 'action': 'store',
70 'help': 'scripts to run after installation',
71 'metavar': 'run',
72 'nargs': '*'
73 }
74 ),
75 (
76 ['-n', '--identifier'],
77 {
78 'action': 'store',
79 'default': None,
80 'help': 'use this identifier instead of generating one. The flag --suffix will be used. ' +
81 'Do not use when creating multiple versions at once',
82 'metavar': 'name',
83 }
84 ),
85 (
86 ['-s', '--suffix'],
87 {
88 'action': 'store',
89 'default': [None],
90 'help': 'suffixes for the instance name',
91 'metavar': 'suffix',
92 'nargs': '*'
93 }
94 ),
95 (
96 ['-v', '--version'],
97 {
98 'choices': [str(x) for x in range(13, int(self.C.get('masterBranch')))] + ['master'],
99 'default': ['master'],
100 'help': 'version of Moodle',
101 'metavar': 'version',
102 'nargs': '*'
103 }
104 ),
105 ]
106
107 def run(self, args):
108
109 engine = args.engine
110 versions = args.version
111 suffixes = args.suffix
112 install = args.install
113
114 # Throw an error when --engine is used without --install. The code is currently commented out
115 # because as --engine has a default value, it will always be set, and so it becomes impossible
116 # to create an instance without installing it. I cannot think about a clean fix yet. Removing
117 # the default value will cause --help not to output the default as it should... Let's put more
118 # thoughts into this and perhaps use argument groups.
119 # if engine and not install:
120 # self.argumentError('--engine can only be used with --install.')
121
122 for version in versions:
123 for suffix in suffixes:
124 arguments = {
125 'version': version,
126 'suffix': suffix,
127 'engine': engine,
128 'integration': args.integration,
129 'identifier': args.identifier,
130 'install': install,
131 'run': args.run
132 }
133 self.do(arguments)
134 logging.info('')
135
136 logging.info('Process complete!')
137
138 def do(self, args):
139 """Proceeds to the creation of an instance"""
140
141 # TODO Remove these ugly lines, but I'm lazy to rewrite the variables in this method...
142 class Bunch:
143 __init__ = lambda self, **kw: setattr(self, '__dict__', kw)
144 args = Bunch(**args)
145
146 engine = args.engine
147 version = args.version
148 name = self.Wp.generateInstanceName(version, integration=args.integration, suffix=args.suffix, identifier=args.identifier)
149
150 # Wording version
151 versionNice = version
152 if version == 'master':
153 versionNice = self.C.get('wording.master')
154
155 # Generating names
156 if args.integration:
157 fullname = self.C.get('wording.integration') + ' ' + versionNice + ' ' + self.C.get('wording.%s' % engine)
158 else:
159 fullname = self.C.get('wording.stable') + ' ' + versionNice + ' ' + self.C.get('wording.%s' % engine)
160
161 # Append the suffix
162 if args.suffix:
163 fullname += ' ' + args.suffix.replace('-', ' ').replace('_', ' ').title()
164
165 # Create the instance
166 logging.info('Creating instance %s...' % name)
167 kwargs = {
168 'name': name,
169 'version': version,
170 'integration': args.integration
171 }
172 try:
173 M = self.Wp.create(**kwargs)
174 except CreateException as e:
175 logging.error('Error creating %s:\n %s' % (name, e))
176 return False
177 except Exception as e:
178 logging.exception('Error creating %s:\n %s' % (name, e))
179 return False
180
181 # Run the install script
182 if args.install:
183
184 # Checking database
185 dbname = re.sub(r'[^a-zA-Z0-9]', '', name).lower()
186 prefixDbname = self.C.get('db.namePrefix')
187 if prefixDbname:
188 dbname = prefixDbname + dbname
189 dbname = dbname[:28]
190 db = DB(engine, self.C.get('db.%s' % engine))
191 dropDb = False
192 if db.dbexists(dbname):
193 logging.info('Database already exists (%s)' % dbname)
194 dropDb = yesOrNo('Do you want to remove it?')
195
196 # Install
197 kwargs = {
198 'engine': engine,
199 'dbname': dbname,
200 'dropDb': dropDb,
201 'fullname': fullname,
202 'dataDir': self.Wp.getPath(name, 'data'),
203 'wwwroot': self.Wp.getUrl(name)
204 }
205 try:
206 M.install(**kwargs)
207 except InstallException as e:
208 logging.warning('Error while installing %s:\n %s' % (name, e))
209 return False
210 except Exception as e:
211 logging.exception('Error while installing %s:\n %s' % (name, e))
212 return False
213
214 # Running scripts
215 if M.isInstalled() and type(args.run) == list:
216 for script in args.run:
217 logging.info('Running script \'%s\'' % (script))
218 try:
219 M.runScript(script)
220 except Exception as e:
221 logging.warning('Error while running the script \'%s\':\ %s' % (script, e))