Patchwork [RFC,8/8] Add backup.py tool

login
register
mail settings
Submitter Stefan Hajnoczi
Date March 9, 2013, 10:22 p.m.
Message ID <1362867748-30528-9-git-send-email-stefanha@redhat.com>
Download mbox | patch
Permalink /patch/226401/
State New
Headers show

Comments

Stefan Hajnoczi - March 9, 2013, 10:22 p.m.
The backup.py tool connects to a running guest's QMP monitor and
orchestrates a live backup.

To invoke a backup:

  qemu -qmp unix:/tmp/monitor.sock,server,nowait ...
  backup.py /tmp/monitor.sock backup-20130301.vma

The script uses vma-writer.py to bundle vmstate and disk backups into a
single backup archive file.  vma-writer.py is launched as a separate
process.

First a 'migrate' command is used to send the vmstate to the writer
process and pause the guest.

Then the 'block-backup' command is used to send a point-in-time copy of
all disks to the writer process before resuming the guest.

The guest continues running while disks are being backed up over NBD.

Finally, the backup completes when the 'block-backup' block jobs finish.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 backup.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 backup.py

Patch

diff --git a/backup.py b/backup.py
new file mode 100644
index 0000000..2e0b179
--- /dev/null
+++ b/backup.py
@@ -0,0 +1,84 @@ 
+#!/usr/bin/env python
+#
+# Live backup tool for QEMU
+#
+# Copyright 2013 Red Hat, Inc. and/or its affiliates
+#
+# Authors:
+#   Stefan Hajnoczi <stefanha@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+import sys; sys.path.append('QMP')
+import qmp
+import subprocess
+
+MIGRATE_PATH = '/tmp/migrate.sock'
+NBD_PATH = '/tmp/nbd.sock'
+
+def find_backup_drives(mon):
+    '''Return list of devices that should be backed up'''
+    result = []
+    for info in mon.command('query-block'):
+        # Skip empty drives
+        if 'inserted' not in info:
+            continue
+
+        # Skip read-only drives
+        if info['inserted']['ro']:
+            continue
+
+        result.append((info['device'], info['inserted']['virtual_size']))
+    return result
+
+def spawn_writer_process(filename, drives):
+    '''Return Popen instance for vma-writer.py process'''
+    args = ['python', 'vma-writer.py',
+            '--output', filename,
+            '--incoming', MIGRATE_PATH,
+            '--nbd', NBD_PATH]
+    for name, size in drives:
+        args.extend(('--drive', 'name=%s,size=%s' % (name, size)))
+    writer = subprocess.Popen(args, stdout=subprocess.PIPE)
+    writer.stdout.readline() # Wait for "Ready"
+    return writer
+
+def main(args):
+    mon = qmp.QEMUMonitorProtocol(args[1])
+    mon.connect()
+
+    drives = find_backup_drives(mon)
+    writer = spawn_writer_process(args[2], drives)
+
+    sys.stderr.write('Running migration...\n')
+    mon.command('migrate', uri='unix:' + MIGRATE_PATH)
+    while True:
+        evt = mon.pull_event(wait=True)
+        if evt['event'] == 'STOP':
+            break
+
+    sys.stderr.write('Running block-backup...\n')
+    for name, _ in drives:
+        mon.command('block-backup',
+                    device=name,
+                    target='nbd+unix:///%s?socket=%s' % (name, NBD_PATH),
+                    format='raw',
+                    mode='existing')
+    mon.command('cont')
+    pending_drives = set(name for name, _ in drives)
+    while pending_drives:
+        evt = mon.pull_event(wait=True)
+        if evt['event'] == 'BLOCK_JOB_COMPLETED':
+            name = evt['data']['device']
+            sys.stderr.write('Finished device "%s"\n' % name)
+            pending_drives.discard(name)
+    sys.stderr.write('Backup complete, terminating writer process\n')
+
+    # Wait for writer process to terminate and print its output
+    sys.stdout.write(writer.communicate()[0])
+
+    mon.close()
+
+if __name__ == '__main__':
+    main(sys.argv)