From nobody Fri Oct 24 09:53:12 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1519431300325767.8528584200802; Fri, 23 Feb 2018 16:15:00 -0800 (PST) Received: from localhost ([::1]:48003 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1epNUH-0007Cj-3o for importer@patchew.org; Fri, 23 Feb 2018 19:14:49 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55536) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1epN8V-00062S-IN for qemu-devel@nongnu.org; Fri, 23 Feb 2018 18:52:22 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1epN8S-0006Aj-AU for qemu-devel@nongnu.org; Fri, 23 Feb 2018 18:52:19 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:33884 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1epN8F-0005jS-1D; Fri, 23 Feb 2018 18:52:03 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6C6F24040856; Fri, 23 Feb 2018 23:51:52 +0000 (UTC) Received: from probe.bos.redhat.com (dhcp-17-231.bos.redhat.com [10.18.17.231]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3BEBB10A9DC0; Fri, 23 Feb 2018 23:51:52 +0000 (UTC) From: John Snow To: qemu-block@nongnu.org Date: Fri, 23 Feb 2018 18:51:25 -0500 Message-Id: <20180223235142.21501-5-jsnow@redhat.com> In-Reply-To: <20180223235142.21501-1-jsnow@redhat.com> References: <20180223235142.21501-1-jsnow@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 23 Feb 2018 23:51:52 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 23 Feb 2018 23:51:52 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'jsnow@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v4 04/21] blockjobs: add status enum X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, John Snow , pkrempa@redhat.com, jtc@redhat.com, qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" We're about to add several new states, and booleans are becoming unwieldly and difficult to reason about. It would help to have a more explicit bookkeeping of the state of blockjobs. To this end, add a new "status" field and add our existing states in a redundant manner alongside the bools they are replacing: UNDEFINED: Placeholder, default state. Not currently visible to QMP unless changes occur in the future to allow creating jobs without starting them via QMP. CREATED: replaces !!job->co && paused && !busy RUNNING: replaces effectively (!paused && busy) PAUSED: Nearly redundant with info->paused, which shows pause_count. This reports the actual status of the job, which almost always matches the paused request status. It differs in that it is strictly only true when the job has actually gone dormant. READY: replaces job->ready. STANDBY: Paused, but job->ready is true. New state additions in coming commits will not be quite so redundant: WAITING: Waiting on transaction. This job has finished all the work it can until the transaction converges, fails, or is canceled. PENDING: Pending authorization from user. This job has finished all the work it can until the job or transaction is finalized via block_job_finalize. This implies the transaction has converged and left the WAITING phase. ABORTING: Job has encountered an error condition and is in the process of aborting. CONCLUDED: Job has ceased all operations and has a return code available for query and may be dismissed via block_job_dismiss. NULL: Job has been dismissed and (should) be destroyed. Should never be visible to QMP. Some of these states appear somewhat superfluous, but it helps define the expected flow of a job; so some of the states wind up being synchronous empty transitions. Importantly, jobs can be in only one of these states at any given time, which helps code and external users alike reason about the current condition of a job unambiguously. Signed-off-by: John Snow Reviewed-by: Kevin Wolf --- blockjob.c | 9 +++++++++ include/block/blockjob.h | 7 +++++-- qapi/block-core.json | 31 ++++++++++++++++++++++++++++++- tests/qemu-iotests/109.out | 24 ++++++++++++------------ 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/blockjob.c b/blockjob.c index 47468331ec..1be9c20cff 100644 --- a/blockjob.c +++ b/blockjob.c @@ -320,6 +320,7 @@ void block_job_start(BlockJob *job) job->pause_count--; job->busy =3D true; job->paused =3D false; + job->status =3D BLOCK_JOB_STATUS_RUNNING; bdrv_coroutine_enter(blk_bs(job->blk), job->co); } =20 @@ -598,6 +599,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **er= rp) info->speed =3D job->speed; info->io_status =3D job->iostatus; info->ready =3D job->ready; + info->status =3D job->status; return info; } =20 @@ -701,6 +703,7 @@ void *block_job_create(const char *job_id, const BlockJ= obDriver *driver, job->pause_count =3D 1; job->refcnt =3D 1; job->manual =3D (flags & BLOCK_JOB_MANUAL); + job->status =3D BLOCK_JOB_STATUS_CREATED; aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, QEMU_CLOCK_REALTIME, SCALE_NS, block_job_sleep_timer_cb, job); @@ -814,9 +817,14 @@ void coroutine_fn block_job_pause_point(BlockJob *job) } =20 if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { + BlockJobStatus status =3D job->status; + job->status =3D status =3D=3D BLOCK_JOB_STATUS_READY ? \ + BLOCK_JOB_STATUS_STANDBY : \ + BLOCK_JOB_STATUS_PAUSED; job->paused =3D true; block_job_do_yield(job, -1); job->paused =3D false; + job->status =3D status; } =20 if (job->driver->resume) { @@ -922,6 +930,7 @@ void block_job_iostatus_reset(BlockJob *job) =20 void block_job_event_ready(BlockJob *job) { + job->status =3D BLOCK_JOB_STATUS_READY; job->ready =3D true; =20 if (block_job_is_internal(job)) { diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 8ffabdcbc4..e254359d6b 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -143,10 +143,13 @@ typedef struct BlockJob { =20 /** * Set to true when the management API has requested manual job - * management semantics. + * management semantics. See @BlockJobStatus for details. */ bool manual; =20 + /** Current state; See @BlockJobStatus for details. */ + BlockJobStatus status; + /** Non-NULL if this job is part of a transaction */ BlockJobTxn *txn; QLIST_ENTRY(BlockJob) txn_list; @@ -157,7 +160,7 @@ typedef enum BlockJobCreateFlags { BLOCK_JOB_DEFAULT =3D 0x00, /* BlockJob is not QMP-created and should not send QMP events */ BLOCK_JOB_INTERNAL =3D 0x01, - /* BlockJob requests manual job management steps. */ + /* BlockJob requests manual job management steps. See @BlockJobStatus.= */ BLOCK_JOB_MANUAL =3D 0x02, } BlockJobCreateFlags; =20 diff --git a/qapi/block-core.json b/qapi/block-core.json index 5c5921bfb7..deddee616a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -955,6 +955,32 @@ { 'enum': 'BlockJobType', 'data': ['commit', 'stream', 'mirror', 'backup'] } =20 +## +# @BlockJobStatus: +# +# Indicates the present state of a given blockjob in its lifetime. +# +# @undefined: Erroneous, default state. Should not ever be visible. +# +# @created: The job has been created, but not yet started. +# +# @running: The job is currently running. +# +# @paused: The job is running, but paused. The pause may be requested by +# either the QMP user or by internal processes. +# +# @ready: The job is running, but is ready for the user to signal completi= on. +# This is used for long-running jobs like mirror that are designed= to +# run indefinitely. +# +# @standby: The job is ready, but paused. This is nearly identical to @pau= sed. +# The job may return to @ready or otherwise be canceled. +# +# Since: 2.12 +## +{ 'enum': 'BlockJobStatus', + 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby'= ] } + ## # @BlockJobInfo: # @@ -981,12 +1007,15 @@ # # @ready: true if the job may be completed (since 2.2) # +# @status: Current job state/status (since 2.12) +# # Since: 1.1 ## { 'struct': 'BlockJobInfo', 'data': {'type': 'str', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'in= t', - 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} } + 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', + 'status': 'BlockJobStatus' } } =20 ## # @query-block-jobs: diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index c189e2833d..d288f2eef6 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -19,7 +19,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024,= "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 102= 4, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mir= ror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 102= 4, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready":= true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1= 024, "speed": 0, "type": "mirror"}} @@ -45,7 +45,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197= 120, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197= 120, "offset": 197120, "paused": false, "speed": 0, "ready": true, "type": = "mirror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197= 120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "rea= dy": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset":= 197120, "speed": 0, "type": "mirror"}} @@ -71,7 +71,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327= 680, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327= 680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": = "mirror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327= 680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "rea= dy": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset":= 327680, "speed": 0, "type": "mirror"}} @@ -97,7 +97,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024,= "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 102= 4, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mir= ror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 102= 4, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready":= true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1= 024, "speed": 0, "type": "mirror"}} @@ -123,7 +123,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 6553= 6, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 655= 36, "offset": 65536, "paused": false, "speed": 0, "ready": true, "type": "m= irror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 655= 36, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready= ": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": = 65536, "speed": 0, "type": "mirror"}} @@ -149,7 +149,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560,= "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 256= 0, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mir= ror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 256= 0, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready":= true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2= 560, "speed": 0, "type": "mirror"}} @@ -174,7 +174,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560,= "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 256= 0, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mir= ror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 256= 0, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready":= true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2= 560, "speed": 0, "type": "mirror"}} @@ -199,7 +199,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 3= 1457280, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 314= 57280, "offset": 31457280, "paused": false, "speed": 0, "ready": true, "typ= e": "mirror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 314= 57280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, = "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset= ": 31457280, "speed": 0, "type": "mirror"}} @@ -224,7 +224,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327= 680, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327= 680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": = "mirror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327= 680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "rea= dy": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset":= 327680, "speed": 0, "type": "mirror"}} @@ -249,7 +249,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048,= "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 204= 8, "offset": 2048, "paused": false, "speed": 0, "ready": true, "type": "mir= ror"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 204= 8, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready":= true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2= 048, "speed": 0, "type": "mirror"}} @@ -265,7 +265,7 @@ Automatically detecting the format is dangerous for raw= images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "= speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512= , "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirro= r"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512= , "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": t= rue, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 51= 2, "speed": 0, "type": "mirror"}} @@ -274,7 +274,7 @@ Images are identical. {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "= speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512= , "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirro= r"}]} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512= , "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": t= rue, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event"= : "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 51= 2, "speed": 0, "type": "mirror"}} --=20 2.14.3