Blame SOURCES/kvm-blockdev-unify-qmp_drive_backup-and-drive-backup-tra.patch

ddf19c
From 4a03ab2a6cc4974d8d43240d1297b09160818af3 Mon Sep 17 00:00:00 2001
ddf19c
From: Sergio Lopez Pascual <slp@redhat.com>
ddf19c
Date: Fri, 7 Feb 2020 11:27:42 +0000
ddf19c
Subject: [PATCH 09/18] blockdev: unify qmp_drive_backup and drive-backup
ddf19c
 transaction paths
ddf19c
ddf19c
RH-Author: Sergio Lopez Pascual <slp@redhat.com>
ddf19c
Message-id: <20200207112749.25073-3-slp@redhat.com>
ddf19c
Patchwork-id: 93755
ddf19c
O-Subject: [RHEL-AV-8.2.0 qemu-kvm PATCH v2 2/9] blockdev: unify qmp_drive_backup and drive-backup transaction paths
ddf19c
Bugzilla: 1745606 1746217 1773517 1779036 1782111 1782175 1783965
ddf19c
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
ddf19c
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
ddf19c
RH-Acked-by: Max Reitz <mreitz@redhat.com>
ddf19c
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
ddf19c
ddf19c
Issuing a drive-backup from qmp_drive_backup takes a slightly
ddf19c
different path than when it's issued from a transaction. In the code,
ddf19c
this is manifested as some redundancy between do_drive_backup() and
ddf19c
drive_backup_prepare().
ddf19c
ddf19c
This change unifies both paths, merging do_drive_backup() and
ddf19c
drive_backup_prepare(), and changing qmp_drive_backup() to create a
ddf19c
transaction instead of calling do_backup_common() direcly.
ddf19c
ddf19c
As a side-effect, now qmp_drive_backup() is executed inside a drained
ddf19c
section, as it happens when creating a drive-backup transaction. This
ddf19c
change is visible from the user's perspective, as the job gets paused
ddf19c
and immediately resumed before starting the actual work.
ddf19c
ddf19c
Also fix tests 141, 185 and 219 to cope with the extra
ddf19c
JOB_STATUS_CHANGE lines.
ddf19c
ddf19c
Signed-off-by: Sergio Lopez <slp@redhat.com>
ddf19c
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
ddf19c
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
ddf19c
(cherry picked from commit 2288ccfac96281c316db942d10e3f921c1373064)
ddf19c
Signed-off-by: Sergio Lopez <slp@redhat.com>
ddf19c
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
ddf19c
---
ddf19c
 blockdev.c                 | 224 ++++++++++++++++++++-------------------------
ddf19c
 tests/qemu-iotests/141.out |   2 +
ddf19c
 tests/qemu-iotests/185.out |   2 +
ddf19c
 tests/qemu-iotests/219     |   7 +-
ddf19c
 tests/qemu-iotests/219.out |   8 ++
ddf19c
 5 files changed, 117 insertions(+), 126 deletions(-)
ddf19c
ddf19c
diff --git a/blockdev.c b/blockdev.c
ddf19c
index 553e315..5e85fc0 100644
ddf19c
--- a/blockdev.c
ddf19c
+++ b/blockdev.c
ddf19c
@@ -1761,39 +1761,128 @@ typedef struct DriveBackupState {
ddf19c
     BlockJob *job;
ddf19c
 } DriveBackupState;
ddf19c
 
ddf19c
-static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
ddf19c
-                            Error **errp);
ddf19c
+static BlockJob *do_backup_common(BackupCommon *backup,
ddf19c
+                                  BlockDriverState *bs,
ddf19c
+                                  BlockDriverState *target_bs,
ddf19c
+                                  AioContext *aio_context,
ddf19c
+                                  JobTxn *txn, Error **errp);
ddf19c
 
ddf19c
 static void drive_backup_prepare(BlkActionState *common, Error **errp)
ddf19c
 {
ddf19c
     DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
ddf19c
-    BlockDriverState *bs;
ddf19c
     DriveBackup *backup;
ddf19c
+    BlockDriverState *bs;
ddf19c
+    BlockDriverState *target_bs;
ddf19c
+    BlockDriverState *source = NULL;
ddf19c
     AioContext *aio_context;
ddf19c
+    QDict *options;
ddf19c
     Error *local_err = NULL;
ddf19c
+    int flags;
ddf19c
+    int64_t size;
ddf19c
+    bool set_backing_hd = false;
ddf19c
 
ddf19c
     assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
ddf19c
     backup = common->action->u.drive_backup.data;
ddf19c
 
ddf19c
+    if (!backup->has_mode) {
ddf19c
+        backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
ddf19c
+    }
ddf19c
+
ddf19c
     bs = bdrv_lookup_bs(backup->device, backup->device, errp);
ddf19c
     if (!bs) {
ddf19c
         return;
ddf19c
     }
ddf19c
 
ddf19c
+    if (!bs->drv) {
ddf19c
+        error_setg(errp, "Device has no medium");
ddf19c
+        return;
ddf19c
+    }
ddf19c
+
ddf19c
     aio_context = bdrv_get_aio_context(bs);
ddf19c
     aio_context_acquire(aio_context);
ddf19c
 
ddf19c
     /* Paired with .clean() */
ddf19c
     bdrv_drained_begin(bs);
ddf19c
 
ddf19c
-    state->bs = bs;
ddf19c
+    if (!backup->has_format) {
ddf19c
+        backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
ddf19c
+                         NULL : (char *) bs->drv->format_name;
ddf19c
+    }
ddf19c
+
ddf19c
+    /* Early check to avoid creating target */
ddf19c
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
ddf19c
+        goto out;
ddf19c
+    }
ddf19c
+
ddf19c
+    flags = bs->open_flags | BDRV_O_RDWR;
ddf19c
+
ddf19c
+    /*
ddf19c
+     * See if we have a backing HD we can use to create our new image
ddf19c
+     * on top of.
ddf19c
+     */
ddf19c
+    if (backup->sync == MIRROR_SYNC_MODE_TOP) {
ddf19c
+        source = backing_bs(bs);
ddf19c
+        if (!source) {
ddf19c
+            backup->sync = MIRROR_SYNC_MODE_FULL;
ddf19c
+        }
ddf19c
+    }
ddf19c
+    if (backup->sync == MIRROR_SYNC_MODE_NONE) {
ddf19c
+        source = bs;
ddf19c
+        flags |= BDRV_O_NO_BACKING;
ddf19c
+        set_backing_hd = true;
ddf19c
+    }
ddf19c
+
ddf19c
+    size = bdrv_getlength(bs);
ddf19c
+    if (size < 0) {
ddf19c
+        error_setg_errno(errp, -size, "bdrv_getlength failed");
ddf19c
+        goto out;
ddf19c
+    }
ddf19c
+
ddf19c
+    if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
ddf19c
+        assert(backup->format);
ddf19c
+        if (source) {
ddf19c
+            bdrv_refresh_filename(source);
ddf19c
+            bdrv_img_create(backup->target, backup->format, source->filename,
ddf19c
+                            source->drv->format_name, NULL,
ddf19c
+                            size, flags, false, &local_err);
ddf19c
+        } else {
ddf19c
+            bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
ddf19c
+                            size, flags, false, &local_err);
ddf19c
+        }
ddf19c
+    }
ddf19c
 
ddf19c
-    state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
ddf19c
     if (local_err) {
ddf19c
         error_propagate(errp, local_err);
ddf19c
         goto out;
ddf19c
     }
ddf19c
 
ddf19c
+    options = qdict_new();
ddf19c
+    qdict_put_str(options, "discard", "unmap");
ddf19c
+    qdict_put_str(options, "detect-zeroes", "unmap");
ddf19c
+    if (backup->format) {
ddf19c
+        qdict_put_str(options, "driver", backup->format);
ddf19c
+    }
ddf19c
+
ddf19c
+    target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
ddf19c
+    if (!target_bs) {
ddf19c
+        goto out;
ddf19c
+    }
ddf19c
+
ddf19c
+    if (set_backing_hd) {
ddf19c
+        bdrv_set_backing_hd(target_bs, source, &local_err);
ddf19c
+        if (local_err) {
ddf19c
+            goto unref;
ddf19c
+        }
ddf19c
+    }
ddf19c
+
ddf19c
+    state->bs = bs;
ddf19c
+
ddf19c
+    state->job = do_backup_common(qapi_DriveBackup_base(backup),
ddf19c
+                                  bs, target_bs, aio_context,
ddf19c
+                                  common->block_job_txn, errp);
ddf19c
+
ddf19c
+unref:
ddf19c
+    bdrv_unref(target_bs);
ddf19c
 out:
ddf19c
     aio_context_release(aio_context);
ddf19c
 }
ddf19c
@@ -3587,126 +3676,13 @@ static BlockJob *do_backup_common(BackupCommon *backup,
ddf19c
     return job;
ddf19c
 }
ddf19c
 
ddf19c
-static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
ddf19c
-                                 Error **errp)
ddf19c
-{
ddf19c
-    BlockDriverState *bs;
ddf19c
-    BlockDriverState *target_bs;
ddf19c
-    BlockDriverState *source = NULL;
ddf19c
-    BlockJob *job = NULL;
ddf19c
-    AioContext *aio_context;
ddf19c
-    QDict *options;
ddf19c
-    Error *local_err = NULL;
ddf19c
-    int flags;
ddf19c
-    int64_t size;
ddf19c
-    bool set_backing_hd = false;
ddf19c
-
ddf19c
-    if (!backup->has_mode) {
ddf19c
-        backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
ddf19c
-    }
ddf19c
-
ddf19c
-    bs = bdrv_lookup_bs(backup->device, backup->device, errp);
ddf19c
-    if (!bs) {
ddf19c
-        return NULL;
ddf19c
-    }
ddf19c
-
ddf19c
-    if (!bs->drv) {
ddf19c
-        error_setg(errp, "Device has no medium");
ddf19c
-        return NULL;
ddf19c
-    }
ddf19c
-
ddf19c
-    aio_context = bdrv_get_aio_context(bs);
ddf19c
-    aio_context_acquire(aio_context);
ddf19c
-
ddf19c
-    if (!backup->has_format) {
ddf19c
-        backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
ddf19c
-                         NULL : (char *) bs->drv->format_name;
ddf19c
-    }
ddf19c
-
ddf19c
-    /* Early check to avoid creating target */
ddf19c
-    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
ddf19c
-        goto out;
ddf19c
-    }
ddf19c
-
ddf19c
-    flags = bs->open_flags | BDRV_O_RDWR;
ddf19c
-
ddf19c
-    /*
ddf19c
-     * See if we have a backing HD we can use to create our new image
ddf19c
-     * on top of.
ddf19c
-     */
ddf19c
-    if (backup->sync == MIRROR_SYNC_MODE_TOP) {
ddf19c
-        source = backing_bs(bs);
ddf19c
-        if (!source) {
ddf19c
-            backup->sync = MIRROR_SYNC_MODE_FULL;
ddf19c
-        }
ddf19c
-    }
ddf19c
-    if (backup->sync == MIRROR_SYNC_MODE_NONE) {
ddf19c
-        source = bs;
ddf19c
-        flags |= BDRV_O_NO_BACKING;
ddf19c
-        set_backing_hd = true;
ddf19c
-    }
ddf19c
-
ddf19c
-    size = bdrv_getlength(bs);
ddf19c
-    if (size < 0) {
ddf19c
-        error_setg_errno(errp, -size, "bdrv_getlength failed");
ddf19c
-        goto out;
ddf19c
-    }
ddf19c
-
ddf19c
-    if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
ddf19c
-        assert(backup->format);
ddf19c
-        if (source) {
ddf19c
-            bdrv_refresh_filename(source);
ddf19c
-            bdrv_img_create(backup->target, backup->format, source->filename,
ddf19c
-                            source->drv->format_name, NULL,
ddf19c
-                            size, flags, false, &local_err);
ddf19c
-        } else {
ddf19c
-            bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
ddf19c
-                            size, flags, false, &local_err);
ddf19c
-        }
ddf19c
-    }
ddf19c
-
ddf19c
-    if (local_err) {
ddf19c
-        error_propagate(errp, local_err);
ddf19c
-        goto out;
ddf19c
-    }
ddf19c
-
ddf19c
-    options = qdict_new();
ddf19c
-    qdict_put_str(options, "discard", "unmap");
ddf19c
-    qdict_put_str(options, "detect-zeroes", "unmap");
ddf19c
-    if (backup->format) {
ddf19c
-        qdict_put_str(options, "driver", backup->format);
ddf19c
-    }
ddf19c
-
ddf19c
-    target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
ddf19c
-    if (!target_bs) {
ddf19c
-        goto out;
ddf19c
-    }
ddf19c
-
ddf19c
-    if (set_backing_hd) {
ddf19c
-        bdrv_set_backing_hd(target_bs, source, &local_err);
ddf19c
-        if (local_err) {
ddf19c
-            goto unref;
ddf19c
-        }
ddf19c
-    }
ddf19c
-
ddf19c
-    job = do_backup_common(qapi_DriveBackup_base(backup),
ddf19c
-                           bs, target_bs, aio_context, txn, errp);
ddf19c
-
ddf19c
-unref:
ddf19c
-    bdrv_unref(target_bs);
ddf19c
-out:
ddf19c
-    aio_context_release(aio_context);
ddf19c
-    return job;
ddf19c
-}
ddf19c
-
ddf19c
-void qmp_drive_backup(DriveBackup *arg, Error **errp)
ddf19c
+void qmp_drive_backup(DriveBackup *backup, Error **errp)
ddf19c
 {
ddf19c
-
ddf19c
-    BlockJob *job;
ddf19c
-    job = do_drive_backup(arg, NULL, errp);
ddf19c
-    if (job) {
ddf19c
-        job_start(&job->job);
ddf19c
-    }
ddf19c
+    TransactionAction action = {
ddf19c
+        .type = TRANSACTION_ACTION_KIND_DRIVE_BACKUP,
ddf19c
+        .u.drive_backup.data = backup,
ddf19c
+    };
ddf19c
+    blockdev_do_action(&action, errp);
ddf19c
 }
ddf19c
 
ddf19c
 BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
ddf19c
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
ddf19c
index 3645675..263b680 100644
ddf19c
--- a/tests/qemu-iotests/141.out
ddf19c
+++ b/tests/qemu-iotests/141.out
ddf19c
@@ -13,6 +13,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.
ddf19c
 Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
ddf19c
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
ddf19c
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
ddf19c
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "job0"}}
ddf19c
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
ddf19c
 {'execute': 'blockdev-del', 'arguments': {'node-name': 'drv0'}}
ddf19c
 {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
ddf19c
 {'execute': 'block-job-cancel', 'arguments': {'device': 'job0'}}
ddf19c
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
ddf19c
index 8379ac5..9a3b657 100644
ddf19c
--- a/tests/qemu-iotests/185.out
ddf19c
+++ b/tests/qemu-iotests/185.out
ddf19c
@@ -65,6 +65,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l
ddf19c
 Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16
ddf19c
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
ddf19c
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
ddf19c
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
ddf19c
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
ddf19c
 {"return": {}}
ddf19c
 { 'execute': 'quit' }
ddf19c
 {"return": {}}
ddf19c
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
ddf19c
index e0c5166..655f54d 100755
ddf19c
--- a/tests/qemu-iotests/219
ddf19c
+++ b/tests/qemu-iotests/219
ddf19c
@@ -63,7 +63,7 @@ def test_pause_resume(vm):
ddf19c
                 # logged immediately
ddf19c
                 iotests.log(vm.qmp('query-jobs'))
ddf19c
 
ddf19c
-def test_job_lifecycle(vm, job, job_args, has_ready=False):
ddf19c
+def test_job_lifecycle(vm, job, job_args, has_ready=False, is_mirror=False):
ddf19c
     global img_size
ddf19c
 
ddf19c
     iotests.log('')
ddf19c
@@ -135,6 +135,9 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False):
ddf19c
     iotests.log('Waiting for PENDING state...')
ddf19c
     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
ddf19c
     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
ddf19c
+    if is_mirror:
ddf19c
+        iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
ddf19c
+        iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
ddf19c
 
ddf19c
     if not job_args.get('auto-finalize', True):
ddf19c
         # PENDING state:
ddf19c
@@ -218,7 +221,7 @@ with iotests.FilePath('disk.img') as disk_path, \
ddf19c
 
ddf19c
     for auto_finalize in [True, False]:
ddf19c
         for auto_dismiss in [True, False]:
ddf19c
-            test_job_lifecycle(vm, 'drive-backup', job_args={
ddf19c
+            test_job_lifecycle(vm, 'drive-backup', is_mirror=True, job_args={
ddf19c
                 'device': 'drive0-node',
ddf19c
                 'target': copy_path,
ddf19c
                 'sync': 'full',
ddf19c
diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out
ddf19c
index 8ebd3fe..0ea5d0b 100644
ddf19c
--- a/tests/qemu-iotests/219.out
ddf19c
+++ b/tests/qemu-iotests/219.out
ddf19c
@@ -135,6 +135,8 @@ Pause/resume in RUNNING
ddf19c
 {"return": {}}
ddf19c
 
ddf19c
 Waiting for PENDING state...
ddf19c
+{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
+{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
@@ -186,6 +188,8 @@ Pause/resume in RUNNING
ddf19c
 {"return": {}}
ddf19c
 
ddf19c
 Waiting for PENDING state...
ddf19c
+{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
+{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
@@ -245,6 +249,8 @@ Pause/resume in RUNNING
ddf19c
 {"return": {}}
ddf19c
 
ddf19c
 Waiting for PENDING state...
ddf19c
+{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
+{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]}
ddf19c
@@ -304,6 +310,8 @@ Pause/resume in RUNNING
ddf19c
 {"return": {}}
ddf19c
 
ddf19c
 Waiting for PENDING state...
ddf19c
+{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
+{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
ddf19c
 {"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]}
ddf19c
-- 
ddf19c
1.8.3.1
ddf19c