All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ishani Chugh <chugh.ishani@research.iiit.ac.in>
To: qemu-devel@nongnu.org
Cc: stefanha@redhat.com, jsnow@redhat.com,
	Ishani Chugh <chugh.ishani@research.iiit.ac.in>
Subject: [Qemu-devel] [PATCH 2/3] Backup Tool: Support for Incremental Backup
Date: Thu, 31 Aug 2017 00:45:37 +0530	[thread overview]
Message-ID: <1504120538-9309-3-git-send-email-chugh.ishani@research.iiit.ac.in> (raw)
In-Reply-To: <1504120538-9309-1-git-send-email-chugh.ishani@research.iiit.ac.in>

Adds incremental backup functionality.

Signed-off-by: Ishani Chugh <chugh.ishani@research.iiit.ac.in>
---
 contrib/backup/qemu-backup.py | 101 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 99 insertions(+), 2 deletions(-)

diff --git a/contrib/backup/qemu-backup.py b/contrib/backup/qemu-backup.py
index 248ca9f..7a3077a 100644
--- a/contrib/backup/qemu-backup.py
+++ b/contrib/backup/qemu-backup.py
@@ -24,11 +24,13 @@ from __future__ import print_function
 from argparse import ArgumentParser
 import os
 import errno
+from string import Template
 from socket import error as socket_error
 try:
     import configparser
 except ImportError:
     import ConfigParser as configparser
+from configparser import NoOptionError
 import sys
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..',
                              'scripts', 'qmp'))
@@ -41,7 +43,6 @@ class BackupTool(object):
                  '/.config/qemu/qemu-backup-config'):
         if "QEMU_BACKUP_CONFIG" in os.environ:
             self.config_file = os.environ["QEMU_BACKUP_CONFIG"]
-
         else:
             self.config_file = config_file
             try:
@@ -129,6 +130,97 @@ class BackupTool(object):
                         drive_list.remove(event['data']['device'])
         print("Backup Complete")
 
+    def _inc_backup(self, guest_name):
+        """
+        Performs Incremental backup
+        """
+        if guest_name not in self.config.sections():
+            print ("Cannot find specified guest", file=sys.stderr)
+            exit(1)
+
+        self.verify_guest_running(guest_name)
+        connection = QEMUMonitorProtocol(
+                                         self.get_socket_address(
+                                             self.config[guest_name]['qmp']))
+        connection.connect()
+        backup_cmd = {"execute": "transaction",
+                      "arguments": {"actions": [], "properties":
+                                    {"completion-mode": "grouped"}}}
+        bitmap_cmd = {"execute": "transaction", "arguments": {"actions": []}}
+        for key in self.config[guest_name]:
+            if key.startswith("drive_"):
+                drive = key[len('drive_'):]
+                target = self.config.get(guest_name, key).rsplit('/', 1)[0]
+                inc_backup_pattern = Template('${drive}_inc_${N}')
+                bitmap = 'qemu_backup_'+guest_name
+                try:
+                    query_block_cmd = {'execute': 'query-block'}
+                    returned_json = connection.cmd_obj(query_block_cmd)
+                    device_present = False
+                    for device in returned_json['return']:
+                        if device['device'] == drive:
+                            device_present = True
+                            bitmap_present = False
+                            for bitmaps in device['dirty-bitmaps']:
+                                if bitmap == bitmaps['name']:
+                                    bitmap_present = True
+                                    if os.path.isfile(self.config.get(
+                                                      guest_name,
+                                                      'inc_'+drive)) is False:
+                                        print("Initial Backup does not exist")
+                                        bitmap_remove = {"execute":
+                                                         "block-dirty" +
+                                                         "-bitmap-remove",
+                                                         "arguments":
+                                                         {"node": drive,
+                                                          "name":
+                                                          "qemu_backup_" +
+                                                          guest_name}}
+                                        connection.cmd_obj(bitmap_remove)
+                                        bitmap_present = False
+                            if bitmap_present is False:
+                                raise NoOptionError(guest_name, 'inc_'+drive)
+                            break
+
+                    if not device_present:
+                        print("No such drive in guest", file=sys.stderr)
+                        sys.exit(1)
+                    N = int(self.config.get(guest_name, drive+'_N'))+1
+                    target = self.config.get(guest_name, key).rsplit(
+                                                                    '/', 1)[0]\
+                        + '/' + inc_backup_pattern.substitute(drive=drive, N=N)
+                    os.system("qemu-img create -f qcow2 " + target + " -b " +
+                              self.config.get(guest_name, 'inc_' +
+                                              drive) + " -F qcow2")
+                    sub_cmd = {"type": "drive-backup",
+                               "data": {"device": drive, "bitmap": bitmap,
+                                        "mode": "existing",
+                                        "sync": "incremental",
+                                        "target": target}}
+                    backup_cmd['arguments']['actions'].append(sub_cmd)
+                    self.config.set(guest_name, drive+'_N',
+                                    str(int(self.config.get(guest_name,
+                                                            drive+'_N'))+1))
+                    self.config.set(guest_name, 'inc_'+drive, target)
+                except (NoOptionError, KeyError) as e:
+                    target = self.config.get(guest_name, key).rsplit(
+                                                                    '/', 1)[0]\
+                        + '/' + inc_backup_pattern.substitute(drive=drive, N=0)
+                    sub_cmd_1 = {"type": "block-dirty-bitmap-add",
+                                 "data": {"node": drive, "name": bitmap,
+                                          "persistent": True,
+                                          "autoload": True}}
+                    sub_cmd_2 = {"type": "drive-backup",
+                                 "data": {"device": drive, "target": target,
+                                          "sync": "full", "format": "qcow2"}}
+                    self.config.set(guest_name, drive+'_N', '0')
+                    self.config.set(guest_name, 'inc_'+drive, target)
+                    bitmap_cmd['arguments']['actions'].append(sub_cmd_1)
+                    bitmap_cmd['arguments']['actions'].append(sub_cmd_2)
+        connection.cmd_obj(bitmap_cmd)
+        connection.cmd_obj(backup_cmd)
+        self.write_config()
+
     def _drive_add(self, drive_id, guest_name, target=None):
         """
         Adds drive for backup
@@ -275,7 +367,10 @@ class BackupTool(object):
         """
         Wrapper for _full_backup method
         """
-        self._full_backup(args.guest)
+        if args.inc is False:
+            self._full_backup(args.guest)
+        else:
+            self._inc_backup(args.guest)
 
     def restore_wrapper(self, args):
         """
@@ -329,6 +424,8 @@ def main():
     backup_parser = subparsers.add_parser('backup', help='Creates backup')
     backup_parser.add_argument('--guest', action='store',
                                type=str, help='Name of the guest')
+    backup_parser.add_argument('--inc', nargs='?',
+                               default=False, help='Destination path')
     backup_parser.set_defaults(func=backup_tool.fullbackup_wrapper)
 
     backup_parser = subparsers.add_parser('restore', help='Restores drives')
-- 
2.7.4

  parent reply	other threads:[~2017-08-30 19:16 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-08-30 19:15 [Qemu-devel] [PATCH 0/3] Backup Tool: Incremental backup Ishani Chugh
2017-08-30 19:15 ` [Qemu-devel] [PATCH 1/3] Backup Tool: Manpage for Incremental Backup Ishani Chugh
2017-08-31  1:57   ` Fam Zheng
2017-08-30 19:15 ` Ishani Chugh [this message]
2017-08-31  1:56   ` [Qemu-devel] [PATCH 2/3] Backup Tool: Support " Fam Zheng
2017-08-30 19:15 ` [Qemu-devel] [PATCH 3/3] Backup Tool: Test " Ishani Chugh
2017-08-31  2:02   ` Fam Zheng
2017-09-01 22:53     ` John Snow
2017-09-07  4:03       ` Ishani

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1504120538-9309-3-git-send-email-chugh.ishani@research.iiit.ac.in \
    --to=chugh.ishani@research.iiit.ac.in \
    --cc=jsnow@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.