fixup! Fix recipe when backup-script is not provided.
[slapos.git] / slapos / recipe / generic_mysql / __init__.py
1 ##############################################################################
2 #
3 # Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
4 #
5 # WARNING: This program as such is intended to be used by professional
6 # programmers who take the whole responsibility of assessing all potential
7 # consequences resulting from its eventual inadequacies and bugs
8 # End users who are looking for a ready-to-use solution with commercial
9 # guarantees and support are strongly adviced to contract a Free Software
10 # Service Company
11 #
12 # This program is Free Software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 3
15 # of the License, or (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #
26 ##############################################################################
27 from slapos.recipe.librecipe import GenericBaseRecipe
28 import os
29
30 class Recipe(GenericBaseRecipe):
31
32 def _options(self, options):
33 options['password'] = self.generatePassword()
34 if 'test-database' in options:
35 options['test-password'] = self.generatePassword()
36 options.setdefault('parallel-test-database-amount', '0')
37 for x in xrange(int(options['parallel-test-database-amount'])):
38 options['test-password-%s' % x] = self.generatePassword()
39
40 def install(self):
41 path_list = []
42
43 template_filename = self.getTemplateFilename('my.cnf.in')
44
45 mysql_binary = self.options['mysql-binary']
46 socket = self.options['socket']
47
48 if 'ip' in self.options:
49 networking = 'port = %s\nbind-address = %s' % (
50 self.options['port'],
51 self.options['ip'],
52 )
53 else:
54 networking = 'skip-networking'
55
56 mysql_conf_file = self.createFile(
57 self.options['conf-file'],
58 self.substituteTemplate(template_filename, {
59 'networking': networking,
60 'data_directory': self.options['data-directory'],
61 'pid_file': self.options['pid-file'],
62 'socket': self.options['socket'],
63 'error_log': self.options['error-log'],
64 'slow_query_log': self.options['slow-query-log'],
65 })
66 )
67 path_list.append(mysql_conf_file)
68
69 mysql_script_list = []
70
71 # user defined functions
72 mroonga = self.options.get('mroonga', 'ha_mroonga.so')
73 if mroonga:
74 last_insert_grn_id = "CREATE FUNCTION last_insert_grn_id RETURNS " \
75 "INTEGER SONAME '" + mroonga + "';"
76 else:
77 last_insert_grn_id = ""
78 mysql_script_list.append(self.substituteTemplate(
79 self.getTemplateFilename('mysql-init-function.sql.in'),
80 {
81 'last_insert_grn_id': last_insert_grn_id,
82 }
83 ))
84 # real database
85 mysql_script_list.append(self.substituteTemplate(
86 self.getTemplateFilename('initmysql.sql.in'),
87 {
88 'mysql_database': self.options['database'],
89 'mysql_user': self.options['user'],
90 'mysql_password': self.options['password']
91 }
92 ))
93 # default test database
94 if 'test-database' in self.options:
95 mysql_script_list.append(self.substituteTemplate(
96 self.getTemplateFilename('initmysql.sql.in'),
97 {
98 'mysql_database': self.options['test-database'],
99 'mysql_user': self.options['test-user'],
100 'mysql_password': self.options['test-password']
101 }
102 ))
103 # parallel test databases
104 for x in xrange(int(self.options['parallel-test-database-amount'])):
105 mysql_script_list.append(self.substituteTemplate(
106 self.getTemplateFilename('initmysql.sql.in'),
107 {
108 'mysql_database': self.options['mysql-test-database-base'] + '_%s' % x,
109 'mysql_user': self.options['mysql-test-user-base'] + '_%s' % x,
110 'mysql_password': self.options['test-password-%s' % x]
111 }
112 ))
113 mysql_script_list.append('EXIT')
114 mysql_script = '\n'.join(mysql_script_list)
115
116 mysql_upgrade_binary = self.options['mysql-upgrade-binary']
117 mysql_update = self.createPythonScript(
118 self.options['update-wrapper'],
119 '%s.mysql.updateMysql' % __name__,
120 [dict(
121 mysql_script=mysql_script,
122 mysql_binary=mysql_binary,
123 mysql_upgrade_binary=mysql_upgrade_binary,
124 socket=socket,
125 )]
126 )
127 path_list.append(mysql_update)
128
129 mysqld = self.createPythonScript(
130 self.options['wrapper'],
131 '%s.mysql.runMysql' % __name__,
132 [dict(
133 mysql_base_directory=self.options['mysql-base-directory'],
134 mysql_install_binary=self.options['mysql-install-binary'],
135 mysqld_binary=self.options['mysqld-binary'],
136 data_directory=self.options['data-directory'],
137 mysql_binary=mysql_binary,
138 socket=socket,
139 configuration_file=mysql_conf_file,
140 )]
141 )
142 path_list.append(mysqld)
143 environment = dict(PATH='%s' % self.options['bin-directory'])
144 # TODO: move to a separate recipe (ack'ed by Cedric)
145 if 'backup-script' in self.options:
146 # backup configuration
147 full_backup = self.options['full-backup-directory']
148 incremental_backup = self.options['incremental-backup-directory']
149 innobackupex_argument_list = [self.options['perl-binary'],
150 self.options['innobackupex-binary'],
151 '--defaults-file=%s' % mysql_conf_file,
152 '--socket=%s' % socket.strip(), '--user=root',
153 '--ibbackup=%s'% self.options['xtrabackup-binary']]
154 innobackupex_incremental = self.createPythonScript(self.options['innobackupex-incremental'], 'slapos.recipe.librecipe.execute.executee', [innobackupex_argument_list + ['--incremental'], environment])
155 path_list.append(innobackupex_incremental)
156 innobackupex_full = self.createPythonScript(self.options['innobackupex-full'], 'slapos.recipe.librecipe.execute.executee', [innobackupex_argument_list, environment])
157 path_list.append(innobackupex_full)
158 backup_controller = self.createPythonScript(self.options['backup-script'], __name__ + '.innobackupex.controller', [innobackupex_incremental, innobackupex_full, full_backup, incremental_backup])
159 path_list.append(backup_controller)
160 # TODO: move to a separate recipe (ack'ed by Cedric)
161 # percona toolkit (formerly known as maatkit) installation
162 for pt_script_name in (
163 'pt-align',
164 'pt-archiver',
165 'pt-collect',
166 'pt-config-diff',
167 'pt-deadlock-logger',
168 'pt-diskstats',
169 'pt-duplicate-key-checker',
170 'pt-fifo-split',
171 'pt-find',
172 'pt-fingerprint',
173 'pt-fk-error-logger',
174 'pt-heartbeat',
175 'pt-index-usage',
176 'pt-ioprofile',
177 'pt-kill',
178 'pt-log-player',
179 'pt-mext',
180 'pt-mysql-summary',
181 'pt-online-schema-change',
182 'pt-pmp',
183 'pt-query-advisor',
184 'pt-query-digest',
185 'pt-show-grants',
186 'pt-sift',
187 'pt-slave-delay',
188 'pt-slave-find',
189 'pt-slave-restart',
190 'pt-stalk',
191 'pt-summary',
192 'pt-table-checksum',
193 'pt-table-sync',
194 'pt-table-usage',
195 'pt-tcp-model',
196 'pt-trend',
197 'pt-upgrade',
198 'pt-variable-advisor',
199 'pt-visual-explain',
200 ):
201 option_name = pt_script_name + '-binary'
202 if option_name not in self.options:
203 continue
204 pt_argument_list = [self.options['perl-binary'],
205 self.options[option_name],
206 '--defaults-file=%s' % mysql_conf_file,
207 '--socket=%s' % socket.strip(), '--user=root',
208 ]
209 pt_exe = self.createPythonScript(os.path.join(self.options['bin-directory'], pt_script_name), 'slapos.recipe.librecipe.execute.executee', [pt_argument_list, environment])
210 path_list.append(pt_exe)
211
212 return path_list
213
214 class WrapUpdateMySQL(GenericBaseRecipe):
215 def install(self):
216 return [
217 self.createPythonScript(
218 self.options['output'],
219 __name__ + '.mysql.updateMysql',
220 [{
221 'mysql_upgrade_binary': self.options['binary'],
222 'mysql_binary': self.options['mysql'],
223 'socket': self.options['socket'],
224 'mysql_script_file': self.options['init-script'],
225 }]
226 ),
227 ]
228
229 class WrapMySQLd(GenericBaseRecipe):
230 def install(self):
231 return [
232 self.createPythonScript(
233 self.options['output'],
234 __name__ + '.mysql.runMysql',
235 [{
236 'mysqld_binary': self.options['binary'],
237 'configuration_file': self.options['configuration-file'],
238 'data_directory': self.options['data-directory'],
239 'mysql_install_binary': self.options['mysql-install-binary'],
240 'mysql_base_directory': self.options['mysql-base-directory'],
241 }]
242 ),
243 ]