]>
Commit | Line | Data |
---|---|---|
efcfa278 JS |
1 | <!-- |
2 | Copyright 2015 John Snow <[email protected]> and Red Hat, Inc. | |
3 | All rights reserved. | |
4 | ||
5 | This file is licensed via The FreeBSD Documentation License, the full text of | |
6 | which is included at the end of this document. | |
7 | --> | |
8 | ||
9 | # Dirty Bitmaps and Incremental Backup | |
10 | ||
11 | * Dirty Bitmaps are objects that track which data needs to be backed up for the | |
12 | next incremental backup. | |
13 | ||
14 | * Dirty bitmaps can be created at any time and attached to any node | |
15 | (not just complete drives.) | |
16 | ||
17 | ## Dirty Bitmap Names | |
18 | ||
19 | * A dirty bitmap's name is unique to the node, but bitmaps attached to different | |
20 | nodes can share the same name. | |
21 | ||
c4c2daa1 JS |
22 | * Dirty bitmaps created for internal use by QEMU may be anonymous and have no |
23 | name, but any user-created bitmaps may not be. There can be any number of | |
24 | anonymous bitmaps per node. | |
25 | ||
26 | * The name of a user-created bitmap must not be empty (""). | |
27 | ||
efcfa278 JS |
28 | ## Bitmap Modes |
29 | ||
30 | * A Bitmap can be "frozen," which means that it is currently in-use by a backup | |
31 | operation and cannot be deleted, renamed, written to, reset, | |
32 | etc. | |
33 | ||
c4c2daa1 JS |
34 | * The normal operating mode for a bitmap is "active." |
35 | ||
efcfa278 JS |
36 | ## Basic QMP Usage |
37 | ||
38 | ### Supported Commands ### | |
39 | ||
40 | * block-dirty-bitmap-add | |
41 | * block-dirty-bitmap-remove | |
42 | * block-dirty-bitmap-clear | |
43 | ||
44 | ### Creation | |
45 | ||
46 | * To create a new bitmap, enabled, on the drive with id=drive0: | |
47 | ||
48 | ```json | |
49 | { "execute": "block-dirty-bitmap-add", | |
50 | "arguments": { | |
51 | "node": "drive0", | |
52 | "name": "bitmap0" | |
53 | } | |
54 | } | |
55 | ``` | |
56 | ||
57 | * This bitmap will have a default granularity that matches the cluster size of | |
58 | its associated drive, if available, clamped to between [4KiB, 64KiB]. | |
59 | The current default for qcow2 is 64KiB. | |
60 | ||
61 | * To create a new bitmap that tracks changes in 32KiB segments: | |
62 | ||
63 | ```json | |
64 | { "execute": "block-dirty-bitmap-add", | |
65 | "arguments": { | |
66 | "node": "drive0", | |
67 | "name": "bitmap0", | |
68 | "granularity": 32768 | |
69 | } | |
70 | } | |
71 | ``` | |
72 | ||
73 | ### Deletion | |
74 | ||
75 | * Bitmaps that are frozen cannot be deleted. | |
76 | ||
77 | * Deleting the bitmap does not impact any other bitmaps attached to the same | |
78 | node, nor does it affect any backups already created from this node. | |
79 | ||
80 | * Because bitmaps are only unique to the node to which they are attached, | |
81 | you must specify the node/drive name here, too. | |
82 | ||
83 | ```json | |
84 | { "execute": "block-dirty-bitmap-remove", | |
85 | "arguments": { | |
86 | "node": "drive0", | |
87 | "name": "bitmap0" | |
88 | } | |
89 | } | |
90 | ``` | |
91 | ||
92 | ### Resetting | |
93 | ||
94 | * Resetting a bitmap will clear all information it holds. | |
95 | ||
96 | * An incremental backup created from an empty bitmap will copy no data, | |
97 | as if nothing has changed. | |
98 | ||
99 | ```json | |
100 | { "execute": "block-dirty-bitmap-clear", | |
101 | "arguments": { | |
102 | "node": "drive0", | |
103 | "name": "bitmap0" | |
104 | } | |
105 | } | |
106 | ``` | |
107 | ||
df9a681d | 108 | ## Transactions |
efcfa278 JS |
109 | |
110 | ### Justification | |
111 | ||
112 | Bitmaps can be safely modified when the VM is paused or halted by using | |
113 | the basic QMP commands. For instance, you might perform the following actions: | |
114 | ||
115 | 1. Boot the VM in a paused state. | |
116 | 2. Create a full drive backup of drive0. | |
117 | 3. Create a new bitmap attached to drive0. | |
118 | 4. Resume execution of the VM. | |
119 | 5. Incremental backups are ready to be created. | |
120 | ||
121 | At this point, the bitmap and drive backup would be correctly in sync, | |
122 | and incremental backups made from this point forward would be correctly aligned | |
123 | to the full drive backup. | |
124 | ||
125 | This is not particularly useful if we decide we want to start incremental | |
126 | backups after the VM has been running for a while, for which we will need to | |
127 | perform actions such as the following: | |
128 | ||
129 | 1. Boot the VM and begin execution. | |
130 | 2. Using a single transaction, perform the following operations: | |
131 | * Create bitmap0. | |
132 | * Create a full drive backup of drive0. | |
133 | 3. Incremental backups are now ready to be created. | |
134 | ||
135 | ### Supported Bitmap Transactions | |
136 | ||
137 | * block-dirty-bitmap-add | |
138 | * block-dirty-bitmap-clear | |
139 | ||
140 | The usages are identical to their respective QMP commands, but see below | |
141 | for examples. | |
142 | ||
143 | ### Example: New Incremental Backup | |
144 | ||
145 | As outlined in the justification, perhaps we want to create a new incremental | |
146 | backup chain attached to a drive. | |
147 | ||
148 | ```json | |
149 | { "execute": "transaction", | |
150 | "arguments": { | |
151 | "actions": [ | |
152 | {"type": "block-dirty-bitmap-add", | |
153 | "data": {"node": "drive0", "name": "bitmap0"} }, | |
154 | {"type": "drive-backup", | |
155 | "data": {"device": "drive0", "target": "/path/to/full_backup.img", | |
156 | "sync": "full", "format": "qcow2"} } | |
157 | ] | |
158 | } | |
159 | } | |
160 | ``` | |
161 | ||
162 | ### Example: New Incremental Backup Anchor Point | |
163 | ||
164 | Maybe we just want to create a new full backup with an existing bitmap and | |
165 | want to reset the bitmap to track the new chain. | |
166 | ||
167 | ```json | |
168 | { "execute": "transaction", | |
169 | "arguments": { | |
170 | "actions": [ | |
171 | {"type": "block-dirty-bitmap-clear", | |
172 | "data": {"node": "drive0", "name": "bitmap0"} }, | |
173 | {"type": "drive-backup", | |
174 | "data": {"device": "drive0", "target": "/path/to/new_full_backup.img", | |
175 | "sync": "full", "format": "qcow2"} } | |
176 | ] | |
177 | } | |
178 | } | |
179 | ``` | |
180 | ||
181 | ## Incremental Backups | |
182 | ||
183 | The star of the show. | |
184 | ||
185 | **Nota Bene!** Only incremental backups of entire drives are supported for now. | |
186 | So despite the fact that you can attach a bitmap to any arbitrary node, they are | |
187 | only currently useful when attached to the root node. This is because | |
188 | drive-backup only supports drives/devices instead of arbitrary nodes. | |
189 | ||
190 | ### Example: First Incremental Backup | |
191 | ||
192 | 1. Create a full backup and sync it to the dirty bitmap, as in the transactional | |
193 | examples above; or with the VM offline, manually create a full copy and then | |
194 | create a new bitmap before the VM begins execution. | |
195 | ||
196 | * Let's assume the full backup is named 'full_backup.img'. | |
197 | * Let's assume the bitmap you created is 'bitmap0' attached to 'drive0'. | |
198 | ||
199 | 2. Create a destination image for the incremental backup that utilizes the | |
200 | full backup as a backing image. | |
201 | ||
202 | * Let's assume it is named 'incremental.0.img'. | |
203 | ||
204 | ```sh | |
205 | # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2 | |
206 | ``` | |
207 | ||
208 | 3. Issue the incremental backup command: | |
209 | ||
210 | ```json | |
211 | { "execute": "drive-backup", | |
212 | "arguments": { | |
213 | "device": "drive0", | |
214 | "bitmap": "bitmap0", | |
215 | "target": "incremental.0.img", | |
216 | "format": "qcow2", | |
4b80ab2b | 217 | "sync": "incremental", |
efcfa278 JS |
218 | "mode": "existing" |
219 | } | |
220 | } | |
221 | ``` | |
222 | ||
223 | ### Example: Second Incremental Backup | |
224 | ||
225 | 1. Create a new destination image for the incremental backup that points to the | |
226 | previous one, e.g.: 'incremental.1.img' | |
227 | ||
228 | ```sh | |
229 | # qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2 | |
230 | ``` | |
231 | ||
232 | 2. Issue a new incremental backup command. The only difference here is that we | |
233 | have changed the target image below. | |
234 | ||
235 | ```json | |
236 | { "execute": "drive-backup", | |
237 | "arguments": { | |
238 | "device": "drive0", | |
239 | "bitmap": "bitmap0", | |
240 | "target": "incremental.1.img", | |
241 | "format": "qcow2", | |
4b80ab2b | 242 | "sync": "incremental", |
efcfa278 JS |
243 | "mode": "existing" |
244 | } | |
245 | } | |
246 | ``` | |
247 | ||
248 | ## Errors | |
249 | ||
250 | * In the event of an error that occurs after a backup job is successfully | |
251 | launched, either by a direct QMP command or a QMP transaction, the user | |
252 | will receive a BLOCK_JOB_COMPLETE event with a failure message, accompanied | |
253 | by a BLOCK_JOB_ERROR event. | |
254 | ||
255 | * In the case of an event being cancelled, the user will receive a | |
256 | BLOCK_JOB_CANCELLED event instead of a pair of COMPLETE and ERROR events. | |
257 | ||
258 | * In either case, the incremental backup data contained within the bitmap is | |
259 | safely rolled back, and the data within the bitmap is not lost. The image | |
260 | file created for the failed attempt can be safely deleted. | |
261 | ||
262 | * Once the underlying problem is fixed (e.g. more storage space is freed up), | |
263 | you can simply retry the incremental backup command with the same bitmap. | |
264 | ||
265 | ### Example | |
266 | ||
267 | 1. Create a target image: | |
268 | ||
269 | ```sh | |
270 | # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2 | |
271 | ``` | |
272 | ||
273 | 2. Attempt to create an incremental backup via QMP: | |
274 | ||
275 | ```json | |
276 | { "execute": "drive-backup", | |
277 | "arguments": { | |
278 | "device": "drive0", | |
279 | "bitmap": "bitmap0", | |
280 | "target": "incremental.0.img", | |
281 | "format": "qcow2", | |
4b80ab2b | 282 | "sync": "incremental", |
efcfa278 JS |
283 | "mode": "existing" |
284 | } | |
285 | } | |
286 | ``` | |
287 | ||
288 | 3. Receive an event notifying us of failure: | |
289 | ||
290 | ```json | |
291 | { "timestamp": { "seconds": 1424709442, "microseconds": 844524 }, | |
292 | "data": { "speed": 0, "offset": 0, "len": 67108864, | |
293 | "error": "No space left on device", | |
294 | "device": "drive1", "type": "backup" }, | |
295 | "event": "BLOCK_JOB_COMPLETED" } | |
296 | ``` | |
297 | ||
298 | 4. Delete the failed incremental, and re-create the image. | |
299 | ||
300 | ```sh | |
301 | # rm incremental.0.img | |
302 | # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2 | |
303 | ``` | |
304 | ||
305 | 5. Retry the command after fixing the underlying problem, | |
306 | such as freeing up space on the backup volume: | |
307 | ||
308 | ```json | |
309 | { "execute": "drive-backup", | |
310 | "arguments": { | |
311 | "device": "drive0", | |
312 | "bitmap": "bitmap0", | |
313 | "target": "incremental.0.img", | |
314 | "format": "qcow2", | |
4b80ab2b | 315 | "sync": "incremental", |
efcfa278 JS |
316 | "mode": "existing" |
317 | } | |
318 | } | |
319 | ``` | |
320 | ||
321 | 6. Receive confirmation that the job completed successfully: | |
322 | ||
323 | ```json | |
324 | { "timestamp": { "seconds": 1424709668, "microseconds": 526525 }, | |
325 | "data": { "device": "drive1", "type": "backup", | |
326 | "speed": 0, "len": 67108864, "offset": 67108864}, | |
327 | "event": "BLOCK_JOB_COMPLETED" } | |
328 | ``` | |
329 | ||
c4c2daa1 JS |
330 | ### Partial Transactional Failures |
331 | ||
332 | * Sometimes, a transaction will succeed in launching and return success, | |
333 | but then later the backup jobs themselves may fail. It is possible that | |
334 | a management application may have to deal with a partial backup failure | |
335 | after a successful transaction. | |
336 | ||
337 | * If multiple backup jobs are specified in a single transaction, when one of | |
338 | them fails, it will not interact with the other backup jobs in any way. | |
339 | ||
340 | * The job(s) that succeeded will clear the dirty bitmap associated with the | |
341 | operation, but the job(s) that failed will not. It is not "safe" to delete | |
342 | any incremental backups that were created successfully in this scenario, | |
343 | even though others failed. | |
344 | ||
345 | #### Example | |
346 | ||
347 | * QMP example highlighting two backup jobs: | |
348 | ||
349 | ```json | |
350 | { "execute": "transaction", | |
351 | "arguments": { | |
352 | "actions": [ | |
353 | { "type": "drive-backup", | |
354 | "data": { "device": "drive0", "bitmap": "bitmap0", | |
355 | "format": "qcow2", "mode": "existing", | |
356 | "sync": "incremental", "target": "d0-incr-1.qcow2" } }, | |
357 | { "type": "drive-backup", | |
358 | "data": { "device": "drive1", "bitmap": "bitmap1", | |
359 | "format": "qcow2", "mode": "existing", | |
360 | "sync": "incremental", "target": "d1-incr-1.qcow2" } }, | |
361 | ] | |
362 | } | |
363 | } | |
364 | ``` | |
365 | ||
366 | * QMP example response, highlighting one success and one failure: | |
367 | * Acknowledgement that the Transaction was accepted and jobs were launched: | |
368 | ```json | |
369 | { "return": {} } | |
370 | ``` | |
371 | ||
372 | * Later, QEMU sends notice that the first job was completed: | |
373 | ```json | |
374 | { "timestamp": { "seconds": 1447192343, "microseconds": 615698 }, | |
375 | "data": { "device": "drive0", "type": "backup", | |
376 | "speed": 0, "len": 67108864, "offset": 67108864 }, | |
377 | "event": "BLOCK_JOB_COMPLETED" | |
378 | } | |
379 | ``` | |
380 | ||
381 | * Later yet, QEMU sends notice that the second job has failed: | |
382 | ```json | |
383 | { "timestamp": { "seconds": 1447192399, "microseconds": 683015 }, | |
384 | "data": { "device": "drive1", "action": "report", | |
385 | "operation": "read" }, | |
386 | "event": "BLOCK_JOB_ERROR" } | |
387 | ``` | |
388 | ||
389 | ```json | |
390 | { "timestamp": { "seconds": 1447192399, "microseconds": 685853 }, | |
391 | "data": { "speed": 0, "offset": 0, "len": 67108864, | |
392 | "error": "Input/output error", | |
393 | "device": "drive1", "type": "backup" }, | |
394 | "event": "BLOCK_JOB_COMPLETED" } | |
395 | ||
396 | * In the above example, "d0-incr-1.qcow2" is valid and must be kept, | |
397 | but "d1-incr-1.qcow2" is invalid and should be deleted. If a VM-wide | |
398 | incremental backup of all drives at a point-in-time is to be made, | |
399 | new backups for both drives will need to be made, taking into account | |
400 | that a new incremental backup for drive0 needs to be based on top of | |
401 | "d0-incr-1.qcow2." | |
402 | ||
403 | ### Grouped Completion Mode | |
404 | ||
405 | * While jobs launched by transactions normally complete or fail on their own, | |
406 | it is possible to instruct them to complete or fail together as a group. | |
407 | ||
408 | * QMP transactions take an optional properties structure that can affect | |
409 | the semantics of the transaction. | |
410 | ||
411 | * The "completion-mode" transaction property can be either "individual" | |
412 | which is the default, legacy behavior described above, or "grouped," | |
413 | a new behavior detailed below. | |
414 | ||
415 | * Delayed Completion: In grouped completion mode, no jobs will report | |
416 | success until all jobs are ready to report success. | |
417 | ||
418 | * Grouped failure: If any job fails in grouped completion mode, all remaining | |
419 | jobs will be cancelled. Any incremental backups will restore their dirty | |
420 | bitmap objects as if no backup command was ever issued. | |
421 | ||
422 | * Regardless of if QEMU reports a particular incremental backup job as | |
423 | CANCELLED or as an ERROR, the in-memory bitmap will be restored. | |
424 | ||
425 | #### Example | |
426 | ||
427 | * Here's the same example scenario from above with the new property: | |
428 | ||
429 | ```json | |
430 | { "execute": "transaction", | |
431 | "arguments": { | |
432 | "actions": [ | |
433 | { "type": "drive-backup", | |
434 | "data": { "device": "drive0", "bitmap": "bitmap0", | |
435 | "format": "qcow2", "mode": "existing", | |
436 | "sync": "incremental", "target": "d0-incr-1.qcow2" } }, | |
437 | { "type": "drive-backup", | |
438 | "data": { "device": "drive1", "bitmap": "bitmap1", | |
439 | "format": "qcow2", "mode": "existing", | |
440 | "sync": "incremental", "target": "d1-incr-1.qcow2" } }, | |
441 | ], | |
442 | "properties": { | |
443 | "completion-mode": "grouped" | |
444 | } | |
445 | } | |
446 | } | |
447 | ``` | |
448 | ||
449 | * QMP example response, highlighting a failure for drive2: | |
450 | * Acknowledgement that the Transaction was accepted and jobs were launched: | |
451 | ```json | |
452 | { "return": {} } | |
453 | ``` | |
454 | ||
455 | * Later, QEMU sends notice that the second job has errored out, | |
456 | but that the first job was also cancelled: | |
457 | ```json | |
458 | { "timestamp": { "seconds": 1447193702, "microseconds": 632377 }, | |
459 | "data": { "device": "drive1", "action": "report", | |
460 | "operation": "read" }, | |
461 | "event": "BLOCK_JOB_ERROR" } | |
462 | ``` | |
463 | ||
464 | ```json | |
465 | { "timestamp": { "seconds": 1447193702, "microseconds": 640074 }, | |
466 | "data": { "speed": 0, "offset": 0, "len": 67108864, | |
467 | "error": "Input/output error", | |
468 | "device": "drive1", "type": "backup" }, | |
469 | "event": "BLOCK_JOB_COMPLETED" } | |
470 | ``` | |
471 | ||
472 | ```json | |
473 | { "timestamp": { "seconds": 1447193702, "microseconds": 640163 }, | |
474 | "data": { "device": "drive0", "type": "backup", "speed": 0, | |
475 | "len": 67108864, "offset": 16777216 }, | |
476 | "event": "BLOCK_JOB_CANCELLED" } | |
477 | ``` | |
478 | ||
efcfa278 JS |
479 | <!-- |
480 | The FreeBSD Documentation License | |
481 | ||
482 | Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML, | |
483 | PDF, PostScript, RTF and so forth) with or without modification, are permitted | |
484 | provided that the following conditions are met: | |
485 | ||
486 | Redistributions of source code (Markdown) must retain the above copyright | |
487 | notice, this list of conditions and the following disclaimer of this file | |
488 | unmodified. | |
489 | ||
490 | Redistributions in compiled form (transformed to other DTDs, converted to PDF, | |
491 | PostScript, RTF and other formats) must reproduce the above copyright notice, | |
492 | this list of conditions and the following disclaimer in the documentation and/or | |
493 | other materials provided with the distribution. | |
494 | ||
495 | THIS DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
496 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
497 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
498 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
499 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
500 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
501 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
502 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
503 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
504 | THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
505 | --> |