Merge lp:~longbow/kewpie/xb_manager into lp:kewpie
- xb_manager
- Merge into trunk
Proposed by
Valentine Gostev
Status: | Merged |
---|---|
Merged at revision: | 126 |
Proposed branch: | lp:~longbow/kewpie/xb_manager |
Merge into: | lp:kewpie |
Diff against target: |
335 lines (+272/-2) 5 files modified
kewpie.py (+4/-1) lib/test_mgmt/execution_management.py (+2/-1) lib/test_mgmt/test_execution.py (+1/-0) lib/util/xb_manager.py (+203/-0) percona_tests/xbm/xb_incremental_test.py (+62/-0) |
To merge this branch: | bzr merge lp:~longbow/kewpie/xb_manager |
Related bugs: | |
Related blueprints: |
Xtrabackup manager for kewpie
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Valentine Gostev | Approve | ||
Review via email: mp+109727@code.launchpad.net |
Commit message
Description of the change
Added xtrabackup management library and a sample test in 'xmb' suite
To post a comment you must log in.
Revision history for this message
Valentine Gostev (longbow) : | # |
review:
Approve
Revision history for this message
Patrick Crews (patrick-crews) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'kewpie.py' | |||
2 | --- kewpie.py 2012-02-20 20:49:50 +0000 | |||
3 | +++ kewpie.py 2012-06-11 20:30:31 +0000 | |||
4 | @@ -44,6 +44,7 @@ | |||
5 | 44 | from lib.sys_mgmt.system_management import systemManager | 44 | from lib.sys_mgmt.system_management import systemManager |
6 | 45 | from lib.test_mgmt.execution_management import executionManager | 45 | from lib.test_mgmt.execution_management import executionManager |
7 | 46 | from lib.opts.matrix_manager import matrixManager | 46 | from lib.opts.matrix_manager import matrixManager |
8 | 47 | from lib.util.xb_manager import xtrabackupManager | ||
9 | 47 | 48 | ||
10 | 48 | # functions | 49 | # functions |
11 | 49 | def handle_sys_config(input_args, defaults): | 50 | def handle_sys_config(input_args, defaults): |
12 | @@ -93,6 +94,8 @@ | |||
13 | 93 | # Create our server_manager | 94 | # Create our server_manager |
14 | 94 | server_manager = serverManager(system_manager, variables) | 95 | server_manager = serverManager(system_manager, variables) |
15 | 95 | 96 | ||
16 | 97 | # Create XtraBackup manager | ||
17 | 98 | xtrabackup_manager = xtrabackupManager(system_manager, server_manager, variables) | ||
18 | 96 | # Get our mode-specific test_manager and test_executor | 99 | # Get our mode-specific test_manager and test_executor |
19 | 97 | (test_manager,test_executor) = handle_mode(variables, system_manager) | 100 | (test_manager,test_executor) = handle_mode(variables, system_manager) |
20 | 98 | 101 | ||
21 | @@ -102,7 +105,7 @@ | |||
22 | 102 | # Initialize test execution manager | 105 | # Initialize test execution manager |
23 | 103 | execution_manager = executionManager(server_manager, system_manager | 106 | execution_manager = executionManager(server_manager, system_manager |
24 | 104 | , test_manager, test_executor | 107 | , test_manager, test_executor |
26 | 105 | , variables, matrix_manager) | 108 | , variables, matrix_manager, xtrabackup_manager) |
27 | 106 | 109 | ||
28 | 107 | # Execute our tests! | 110 | # Execute our tests! |
29 | 108 | execution_manager.execute_tests() | 111 | execution_manager.execute_tests() |
30 | 109 | 112 | ||
31 | === modified file 'lib/test_mgmt/execution_management.py' | |||
32 | --- lib/test_mgmt/execution_management.py 2012-02-20 20:49:50 +0000 | |||
33 | +++ lib/test_mgmt/execution_management.py 2012-06-11 20:30:31 +0000 | |||
34 | @@ -39,11 +39,12 @@ | |||
35 | 39 | """ | 39 | """ |
36 | 40 | 40 | ||
37 | 41 | def __init__(self, server_manager, system_manager, test_manager | 41 | def __init__(self, server_manager, system_manager, test_manager |
39 | 42 | , executor_type, variables, matrix_manager): | 42 | , executor_type, variables, matrix_manager, xtrabackup_manager): |
40 | 43 | 43 | ||
41 | 44 | self.server_manager = server_manager | 44 | self.server_manager = server_manager |
42 | 45 | self.system_manager = system_manager | 45 | self.system_manager = system_manager |
43 | 46 | self.matrix_manager = matrix_manager | 46 | self.matrix_manager = matrix_manager |
44 | 47 | self.xtrabackup_manager = xtrabackup_manager | ||
45 | 47 | self.logging = system_manager.logging | 48 | self.logging = system_manager.logging |
46 | 48 | self.test_manager = test_manager | 49 | self.test_manager = test_manager |
47 | 49 | if variables['verbose']: | 50 | if variables['verbose']: |
48 | 50 | 51 | ||
49 | === modified file 'lib/test_mgmt/test_execution.py' | |||
50 | --- lib/test_mgmt/test_execution.py 2012-02-20 20:49:50 +0000 | |||
51 | +++ lib/test_mgmt/test_execution.py 2012-06-11 20:30:31 +0000 | |||
52 | @@ -58,6 +58,7 @@ | |||
53 | 58 | self.server_manager = self.execution_manager.server_manager | 58 | self.server_manager = self.execution_manager.server_manager |
54 | 59 | self.time_manager = self.system_manager.time_manager | 59 | self.time_manager = self.system_manager.time_manager |
55 | 60 | self.matrix_manager = self.execution_manager.matrix_manager | 60 | self.matrix_manager = self.execution_manager.matrix_manager |
56 | 61 | self.xtrabackup_manager = self.execution_manager.xtrabackup_manager | ||
57 | 61 | self.name = name | 62 | self.name = name |
58 | 62 | self.working_environment = {} # we pass env dict to define what we need | 63 | self.working_environment = {} # we pass env dict to define what we need |
59 | 63 | self.dirset = { self.name : { 'log': None } } | 64 | self.dirset = { self.name : { 'log': None } } |
60 | 64 | 65 | ||
61 | === added file 'lib/util/xb_manager.py' | |||
62 | --- lib/util/xb_manager.py 1970-01-01 00:00:00 +0000 | |||
63 | +++ lib/util/xb_manager.py 2012-06-11 20:30:31 +0000 | |||
64 | @@ -0,0 +1,203 @@ | |||
65 | 1 | #! /usr/bin/env python | ||
66 | 2 | # -*- mode: python; indent-tabs-mode: nil; -*- | ||
67 | 3 | # vim:expandtab:shiftwidth=2:tabstop=2:smarttab: | ||
68 | 4 | # | ||
69 | 5 | # Copyright (C) 2011-2012 Patrick Crews, Valentine Gostev | ||
70 | 6 | # | ||
71 | 7 | # | ||
72 | 8 | # This program is free software; you can redistribute it and/or modify | ||
73 | 9 | # it under the terms of the GNU General Public License as published by | ||
74 | 10 | # the Free Software Foundation; either version 2 of the License, or | ||
75 | 11 | # (at your option) any later version. | ||
76 | 12 | # | ||
77 | 13 | # This program is distributed in the hope that it will be useful, | ||
78 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
79 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
80 | 16 | # GNU General Public License for more details. | ||
81 | 17 | # | ||
82 | 18 | # You should have received a copy of the GNU General Public License | ||
83 | 19 | # along with this program; if not, write to the Free Software | ||
84 | 20 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
85 | 21 | |||
86 | 22 | import os | ||
87 | 23 | import shutil | ||
88 | 24 | import re | ||
89 | 25 | import subprocess | ||
90 | 26 | |||
91 | 27 | """ Xtrabackup manager class handles all mysql backup operations | ||
92 | 28 | it creates a backup object from server objects | ||
93 | 29 | """ | ||
94 | 30 | |||
95 | 31 | class xtrabackupManager: | ||
96 | 32 | def __init__(self, server_manager, system_manager, variables): | ||
97 | 33 | self.system_manager = system_manager | ||
98 | 34 | self.code_manager = system_manager.code_manager | ||
99 | 35 | self.server_manager = server_manager | ||
100 | 36 | self.env_manager = system_manager.env_manager | ||
101 | 37 | self.logging = system_manager.logging | ||
102 | 38 | self.xb_bin_path = variables['xtrabackuppath'] | ||
103 | 39 | self.ib_bin_path = variables['innobackupexpath'] | ||
104 | 40 | self.backup_dir = os.path.join(variables['workdir'],'backups') | ||
105 | 41 | if not os.path.isdir(self.backup_dir): | ||
106 | 42 | os.makedirs(self.backup_dir) | ||
107 | 43 | |||
108 | 44 | """ clean_dir method is used to wipe out data from server object's | ||
109 | 45 | data directory in order to restore a prepared backup | ||
110 | 46 | Usage clean_dir() | ||
111 | 47 | """ | ||
112 | 48 | def clean_dir(self,path): | ||
113 | 49 | for top,dirs,files in os.walk(path): | ||
114 | 50 | for file in files: | ||
115 | 51 | os.unlink(os.path.join(top,file)) | ||
116 | 52 | for dir in dirs: | ||
117 | 53 | shutil.rmtree(os.path.join(top,dir)) | ||
118 | 54 | |||
119 | 55 | """ alloc_dir returns a directory name for next backup | ||
120 | 56 | """ | ||
121 | 57 | def alloc_dir(self, topdir, dir_pattern='backup'): | ||
122 | 58 | dir_pattern_obj = '%s(\\d+)' %dir_pattern | ||
123 | 59 | dir_pattern_obj = re.compile(dir_pattern_obj) | ||
124 | 60 | dir_list = [list(dirs)[1] for dirs in os.walk(topdir) if list(dirs)[0] == topdir][0] | ||
125 | 61 | dir_list = [b for b in dir_list if dir_pattern_obj.match(b)] | ||
126 | 62 | if not dir_list: | ||
127 | 63 | dir_suffix = '0' | ||
128 | 64 | else: | ||
129 | 65 | dir_suffix = str(max([int(re.split(dir_pattern,b)[1]) for b in dir_list])+1) | ||
130 | 66 | |||
131 | 67 | return '%s%s' %(dir_pattern,dir_suffix) | ||
132 | 68 | |||
133 | 69 | """ execute_cmd executes backup program to create backup object | ||
134 | 70 | """ | ||
135 | 71 | def execute_cmd(self, cmd, exec_path, outfile_path): | ||
136 | 72 | outfile = open(outfile_path,'w') | ||
137 | 73 | cmd_subproc = subprocess.Popen( cmd | ||
138 | 74 | , cwd = exec_path | ||
139 | 75 | , shell=True | ||
140 | 76 | , stdout = outfile | ||
141 | 77 | , stderr = subprocess.STDOUT | ||
142 | 78 | ) | ||
143 | 79 | cmd_subproc.wait() | ||
144 | 80 | retcode = cmd_subproc.returncode | ||
145 | 81 | outfile.close | ||
146 | 82 | in_file = open(outfile_path,'r') | ||
147 | 83 | output = ''.join(in_file.readlines()) | ||
148 | 84 | return retcode,output | ||
149 | 85 | |||
150 | 86 | """ method creates full backup from server object | ||
151 | 87 | new_backup_object = backup_full(server_object) | ||
152 | 88 | """ | ||
153 | 89 | def backup_full(self,server_object): | ||
154 | 90 | self.datadir = server_object.datadir | ||
155 | 91 | self.ib_bin = self.ib_bin_path | ||
156 | 92 | self.xb_bin = self.xb_bin_path | ||
157 | 93 | self.b_root_dir = self.backup_dir | ||
158 | 94 | allocated_dir = self.alloc_dir(self.b_root_dir) | ||
159 | 95 | self.b_path = os.path.join(self.b_root_dir, allocated_dir) | ||
160 | 96 | temp_log = os.path.join(self.b_root_dir, '%s.log' %allocated_dir) | ||
161 | 97 | self.xb_log = os.path.join(self.b_path, '%s.log' %allocated_dir) | ||
162 | 98 | cmd = [self.ib_bin | ||
163 | 99 | , "--defaults-file=%s" %server_object.cnf_file | ||
164 | 100 | , "--no-timestamp" | ||
165 | 101 | , "--user=root" | ||
166 | 102 | , "--port=%d" %server_object.master_port | ||
167 | 103 | , "--host=127.0.0.1" | ||
168 | 104 | , "--ibbackup=%s" %self.xb_bin | ||
169 | 105 | , self.b_path | ||
170 | 106 | ] | ||
171 | 107 | cmd = " ".join(cmd) | ||
172 | 108 | self.retcode, self.output = self.execute_cmd(cmd, self.b_root_dir, temp_log) | ||
173 | 109 | shutil.move(temp_log, self.xb_log) | ||
174 | 110 | self.status = 'full-backup' | ||
175 | 111 | return self | ||
176 | 112 | |||
177 | 113 | """ Create incremental backup from full backup and server objects | ||
178 | 114 | Usage: incremental_backup_object = backup_inc(server_obj,previous_backup_object) | ||
179 | 115 | """ | ||
180 | 116 | def backup_inc(self,server_object,backup_object): | ||
181 | 117 | self.ib_bin = self.ib_bin_path | ||
182 | 118 | self.xb_bin = self.xb_bin_path | ||
183 | 119 | self.b_root_dir = self.backup_dir | ||
184 | 120 | allocated_dir = self.alloc_dir(self.b_root_dir) | ||
185 | 121 | self.b_path = os.path.join(self.b_root_dir, allocated_dir) | ||
186 | 122 | temp_log = os.path.join(self.b_root_dir, '%s.log' %allocated_dir) | ||
187 | 123 | self.xb_log = os.path.join(self.b_path, '%s.log' %allocated_dir) | ||
188 | 124 | cmd = [self.ib_bin | ||
189 | 125 | , '--defualts-file=%s' %server_object.cnf_file | ||
190 | 126 | , '--no-timestamp' | ||
191 | 127 | , '--user=root' | ||
192 | 128 | , '--port=%d' %server_object.master_port | ||
193 | 129 | , '--host=127.0.0.1' | ||
194 | 130 | , '--ibbackup=%s' %self.xb_bin | ||
195 | 131 | , '--incremental' | ||
196 | 132 | , '--incremental-basedir=%s' %backup_object.b_path | ||
197 | 133 | , self.b_path | ||
198 | 134 | ] | ||
199 | 135 | cmd = " ".join(cmd) | ||
200 | 136 | self.retcode, self.output = self.execute_cmd(cmd, self.b_root_dir, temp_log) | ||
201 | 137 | shutil.move(temp_log, self.xb_log) | ||
202 | 138 | self.status = 'inc-backup' | ||
203 | 139 | return self | ||
204 | 140 | |||
205 | 141 | """ Method to prepare a backup | ||
206 | 142 | Usage prepare(unprepared_backup_object) | ||
207 | 143 | """ | ||
208 | 144 | def prepare(self,backup_object,rollback=True): | ||
209 | 145 | if rollback: | ||
210 | 146 | rollback = '' | ||
211 | 147 | else: | ||
212 | 148 | rollback = '--redo-only' | ||
213 | 149 | |||
214 | 150 | cmd = [backup_object.ib_bin | ||
215 | 151 | , "--apply-log" | ||
216 | 152 | , " %s" %rollback | ||
217 | 153 | , "--ibbackup=%s" %backup_object.xb_bin | ||
218 | 154 | , backup_object.b_path | ||
219 | 155 | ] | ||
220 | 156 | cmd = " ".join(cmd) | ||
221 | 157 | temp_log = os.path.join(backup_object.b_path, self.alloc_dir(backup_object.b_path,dir_pattern='prepare')) | ||
222 | 158 | retcode, output = self.execute_cmd(cmd, backup_object.b_path, temp_log) | ||
223 | 159 | if rollback and retcode==0: | ||
224 | 160 | backup_object.status = 'prepared-redo-only' | ||
225 | 161 | elif not rollback and retcode==0: | ||
226 | 162 | backup_object.status = 'prepared' | ||
227 | 163 | else: | ||
228 | 164 | backup_object.status = 'prepare-failed' | ||
229 | 165 | |||
230 | 166 | return retcode, output | ||
231 | 167 | |||
232 | 168 | """ Method restores server_object's data from backup | ||
233 | 169 | If backup is not ready for restore an appropriate error | ||
234 | 170 | message will be returned. | ||
235 | 171 | Usage: restore(prepared_backup_object,server_object) | ||
236 | 172 | """ | ||
237 | 173 | def restore(self,backup_object,server_object): | ||
238 | 174 | if backup_object.status=='prepared-redo-only' or backup_object.status=='prepared': | ||
239 | 175 | pass | ||
240 | 176 | elif backup_object.status=='full-backup': | ||
241 | 177 | retcode = 1 | ||
242 | 178 | output = 'Backup has to be prepared before restore!\n' | ||
243 | 179 | return retcode, output | ||
244 | 180 | elif backup_object.status=='inc-backup': | ||
245 | 181 | retcode = 1 | ||
246 | 182 | output = 'You have to apply incrementals to full backup to restore it!\n' | ||
247 | 183 | elif backup_object.status=='prepare-failed': | ||
248 | 184 | retcode = 1 | ||
249 | 185 | output = 'Backup failed to prepare, see prepare log for details.\n' | ||
250 | 186 | else: | ||
251 | 187 | retcode = 1 | ||
252 | 188 | output = 'CRITICAL ERROR: Unknown backup status!\n' | ||
253 | 189 | |||
254 | 190 | cmd = [backup_object.ib_bin | ||
255 | 191 | , "--defaults-file=%s" %server_object.cnf_file | ||
256 | 192 | , "--copy-back" | ||
257 | 193 | , "--ibbackup=%s" %backup_object.xb_bin | ||
258 | 194 | , backup_object.b_path | ||
259 | 195 | ] | ||
260 | 196 | cmd = " ".join(cmd) | ||
261 | 197 | server_object.stop() | ||
262 | 198 | self.clean_dir(server_object.datadir) | ||
263 | 199 | temp_log = os.path.join(backup_object.b_path, self.alloc_dir(backup_object.b_path,dir_pattern='restore')) | ||
264 | 200 | retcode, output = self.execute_cmd(cmd, backup_object.b_path, temp_log) | ||
265 | 201 | server_object.start() | ||
266 | 202 | return retcode, output | ||
267 | 203 | |||
268 | 0 | 204 | ||
269 | === added directory 'percona_tests/xbm' | |||
270 | === added file 'percona_tests/xbm/xb_incremental_test.py' | |||
271 | --- percona_tests/xbm/xb_incremental_test.py 1970-01-01 00:00:00 +0000 | |||
272 | +++ percona_tests/xbm/xb_incremental_test.py 2012-06-11 20:30:31 +0000 | |||
273 | @@ -0,0 +1,62 @@ | |||
274 | 1 | #! /usr/bin/env python | ||
275 | 2 | # -*- mode: python; indent-tabs-mode: nil; -*- | ||
276 | 3 | # vim:expandtab:shiftwidth=2:tabstop=2:smarttab: | ||
277 | 4 | # | ||
278 | 5 | # Copyright (C) 2011 Patrick Crews | ||
279 | 6 | # | ||
280 | 7 | # | ||
281 | 8 | # This program is free software; you can redistribute it and/or modify | ||
282 | 9 | # it under the terms of the GNU General Public License as published by | ||
283 | 10 | # the Free Software Foundation; either version 2 of the License, or | ||
284 | 11 | # (at your option) any later version. | ||
285 | 12 | # | ||
286 | 13 | # This program is distributed in the hope that it will be useful, | ||
287 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
288 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
289 | 16 | # GNU General Public License for more details. | ||
290 | 17 | # | ||
291 | 18 | # You should have received a copy of the GNU General Public License | ||
292 | 19 | # along with this program; if not, write to the Free Software | ||
293 | 20 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
294 | 21 | |||
295 | 22 | import os | ||
296 | 23 | import shutil | ||
297 | 24 | |||
298 | 25 | from lib.util.mysqlBaseTestCase import mysqlBaseTestCase | ||
299 | 26 | |||
300 | 27 | server_requirements = [['--innodb-file-per-table'],['--innodb-file-per-table']] | ||
301 | 28 | servers = [] | ||
302 | 29 | server_manager = None | ||
303 | 30 | test_executor = None | ||
304 | 31 | # we explicitly use the --no-timestamp option | ||
305 | 32 | # here. We will be using a generic / vanilla backup dir | ||
306 | 33 | backup_path = None | ||
307 | 34 | |||
308 | 35 | class basicTest(mysqlBaseTestCase): | ||
309 | 36 | |||
310 | 37 | |||
311 | 38 | def test_ib_incremental(self): | ||
312 | 39 | master_server = servers[0] | ||
313 | 40 | slave_server = servers[1] | ||
314 | 41 | self.servers = servers | ||
315 | 42 | logging = test_executor.logging | ||
316 | 43 | xb_manager = test_executor.xtrabackup_manager | ||
317 | 44 | # backup0 = xb_manager.backup_full(master_server) | ||
318 | 45 | # xb_manager.prepare(backup0) | ||
319 | 46 | # xb_manager.restore(backup0,master_server) | ||
320 | 47 | randgen_manager = test_executor.randgen_manager | ||
321 | 48 | randgen_manager.create_test_bed(master_server, optional_parameters) | ||
322 | 49 | backup0 = xb_manager.backup_full(master_server) | ||
323 | 50 | load0 = randgen_manager.create_load(master_server, runtime_options) | ||
324 | 51 | load0.start() | ||
325 | 52 | backup1 = xb_manager.backup_inc(master_server, backup0) | ||
326 | 53 | xb_manager.prepare(backup0,rollback=False) | ||
327 | 54 | xb_manager.prepare_inc(backup0,backup1) | ||
328 | 55 | xb_manager.restore(slave_server) | ||
329 | 56 | slave_server.start_repl(master_server) | ||
330 | 57 | load0.wait() | ||
331 | 58 | compare_checksums(master_server,slave_server,db1="test",db2="test2") | ||
332 | 59 | |||
333 | 60 | |||
334 | 61 | |||
335 | 62 |
You rock! Glad to see you are still finding use for the tools : )
I'm currently working on some updates myself...just been locked in a basement for a few weeks ; )