From patchwork Sat Mar 9 22:22:28 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 226401 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DDC922C033B for ; Sun, 10 Mar 2013 09:28:02 +1100 (EST) Received: from localhost ([::1]:38350 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UESFF-0003Ok-3f for incoming@patchwork.ozlabs.org; Sat, 09 Mar 2013 17:28:01 -0500 Received: from eggs.gnu.org ([208.118.235.92]:43557) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UESBC-0005JA-2H for qemu-devel@nongnu.org; Sat, 09 Mar 2013 17:23:53 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UESB8-0008RM-SA for qemu-devel@nongnu.org; Sat, 09 Mar 2013 17:23:50 -0500 Received: from mx1.redhat.com ([209.132.183.28]:21632) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UESB8-0008RE-KZ for qemu-devel@nongnu.org; Sat, 09 Mar 2013 17:23:46 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r29MNkMZ027584 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Sat, 9 Mar 2013 17:23:46 -0500 Received: from localhost (ovpn-112-17.ams2.redhat.com [10.36.112.17]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r29MNinx024238; Sat, 9 Mar 2013 17:23:45 -0500 From: Stefan Hajnoczi To: Date: Sat, 9 Mar 2013 23:22:28 +0100 Message-Id: <1362867748-30528-9-git-send-email-stefanha@redhat.com> In-Reply-To: <1362867748-30528-1-git-send-email-stefanha@redhat.com> References: <1362867748-30528-1-git-send-email-stefanha@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: Kevin Wolf , dietmar@proxmox.com, Stefan Hajnoczi , Markus Armbruster Subject: [Qemu-devel] [RFC 8/8] Add backup.py tool X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- backup.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 backup.py 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 +# +# 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)