diff mbox

[v2,2/3] block: add block-backup QMP command

Message ID 1367221335-22777-3-git-send-email-stefanha@redhat.com
State New
Headers show

Commit Message

Stefan Hajnoczi April 29, 2013, 7:42 a.m. UTC
@block-backup

Start a point-in-time copy of a block device to a new destination.  The
status of ongoing block backup operations can be checked with
query-block-jobs.  The operation can be stopped before it has completed using
the block-job-cancel command.

@device: the name of the device whose writes should be mirrored.

@target: the target of the new image. If the file exists, or if it
         is a device, the existing file/device will be used as the new
         destination.  If it does not exist, a new file will be created.

@format: #optional the format of the new destination, default is to
         probe if @mode is 'existing', else the format of the source

@mode: #optional whether and how QEMU should create a new image, default is
       'absolute-paths'.

@speed: #optional the maximum speed, in bytes per second

Returns: nothing on success
         If @device is not a valid block device, DeviceNotFound

Since 1.6

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 blockdev.c       | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi-schema.json | 31 +++++++++++++++++++
 qmp-commands.hx  |  6 ++++
 3 files changed, 129 insertions(+)

Comments

Kevin Wolf May 8, 2013, 12:49 p.m. UTC | #1
Am 29.04.2013 um 09:42 hat Stefan Hajnoczi geschrieben:
> @block-backup
> 
> Start a point-in-time copy of a block device to a new destination.  The
> status of ongoing block backup operations can be checked with
> query-block-jobs.  The operation can be stopped before it has completed using
> the block-job-cancel command.
> 
> @device: the name of the device whose writes should be mirrored.
> 
> @target: the target of the new image. If the file exists, or if it
>          is a device, the existing file/device will be used as the new
>          destination.  If it does not exist, a new file will be created.
> 
> @format: #optional the format of the new destination, default is to
>          probe if @mode is 'existing', else the format of the source
> 
> @mode: #optional whether and how QEMU should create a new image, default is
>        'absolute-paths'.
> 
> @speed: #optional the maximum speed, in bytes per second
> 
> Returns: nothing on success
>          If @device is not a valid block device, DeviceNotFound
> 
> Since 1.6
> 
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

drive-backup would probably be a more consistent naming. We would then
still have block-backup for a future low-level command that doesn't
create everything by itself but takes an existing BlockDriverState (e.g.
created by blockdev-add).

We should also make it transactionable from the beginning, as we don't
have schema introspection yet. This way we allow to assume that if the
standalone command exists, the transaction subcommand exists as well.

Kevin
Eric Blake May 11, 2013, 3:34 a.m. UTC | #2
On 05/08/2013 06:49 AM, Kevin Wolf wrote:
> Am 29.04.2013 um 09:42 hat Stefan Hajnoczi geschrieben:
>> @block-backup
>>

> drive-backup would probably be a more consistent naming. We would then
> still have block-backup for a future low-level command that doesn't
> create everything by itself but takes an existing BlockDriverState (e.g.
> created by blockdev-add).

At least it would match why we named a command 'drive-mirror' instead of
'block-mirror'.

Hmm, looking at qapi-schema.json, I wonder if we can rename
'BlockdevAction' to 'TransactionAction' as used in the @transaction
command.  It wouldn't change what is sent over the wire in JSON, and
until we have full introspection, there is no visibility into the type
name used.  Changing the name now would let it be more generic to adding
future transaction items that are not blockdev related.

> 
> We should also make it transactionable from the beginning, as we don't
> have schema introspection yet. This way we allow to assume that if the
> standalone command exists, the transaction subcommand exists as well.

Agreed - existence of a command at the same time the command is made
transactionable serves as a nice substitute for not having full
introspection into the 'BlockdevAction' union type, whereas if we
introduce the command now but not transaction support until 1.7, life
becomes tougher to know when it can be used where (although I HOPE we
have introspection in 1.6).
Eric Blake May 11, 2013, 4:02 a.m. UTC | #3
On 04/29/2013 01:42 AM, Stefan Hajnoczi wrote:
> @block-backup
> 
> +++ b/qapi-schema.json
> @@ -1715,6 +1715,37 @@
>              '*speed': 'int' } }
>  
>  ##
> +# @block-backup
> +#
> +# Start a point-in-time copy of a block device to a new destination.  The
> +# status of ongoing block backup operations can be checked with
> +# query-block-jobs.  The operation can be stopped before it has completed using
> +# the block-job-cancel command.

Still might be worth mentioning that 'query-block-jobs' will list it as
a job of type 'backup'.

> +#
> +# @device: the name of the device whose writes should be mirrored.
> +#
> +# @target: the target of the new image. If the file exists, or if it
> +#          is a device, the existing file/device will be used as the new
> +#          destination.  If it does not exist, a new file will be created.
> +#
> +# @format: #optional the format of the new destination, default is to
> +#          probe if @mode is 'existing', else the format of the source
> +#
> +# @mode: #optional whether and how QEMU should create a new image, default is
> +#        'absolute-paths'.
> +#
> +# @speed: #optional the maximum speed, in bytes per second
> +#
> +# Returns: nothing on success
> +#          If @device is not a valid block device, DeviceNotFound
> +#
> +# Since 1.6
> +##
> +{ 'command': 'block-backup',
> +  'data': { 'device': 'str', 'target': 'str', '*format': 'str',

Hmm - wondering if we should add an enum type for supported disk formats
instead of using free-form strings.  The wire representation would be
the same, and now's the time to do it before we add introspection (it's
more than just this command impacted).
Paolo Bonzini May 11, 2013, 8:02 a.m. UTC | #4
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Il 11/05/2013 05:34, Eric Blake ha scritto:
> On 05/08/2013 06:49 AM, Kevin Wolf wrote:
>> Am 29.04.2013 um 09:42 hat Stefan Hajnoczi geschrieben:
>>> @block-backup
>>> 
> 
>> drive-backup would probably be a more consistent naming. We would
>> then still have block-backup for a future low-level command that
>> doesn't create everything by itself but takes an existing
>> BlockDriverState (e.g. created by blockdev-add).
> 
> At least it would match why we named a command 'drive-mirror'
> instead of 'block-mirror'.
> 
> Hmm, looking at qapi-schema.json, I wonder if we can rename 
> 'BlockdevAction' to 'TransactionAction' as used in the
> @transaction command.  It wouldn't change what is sent over the
> wire in JSON, and until we have full introspection, there is no
> visibility into the type name used.  Changing the name now would
> let it be more generic to adding future transaction items that are
> not blockdev related.

Right.  For example, "cont" could be made transactionable too (and
executed only if the transaction succeeds).

Paolo

>> 
>> We should also make it transactionable from the beginning, as we
>> don't have schema introspection yet. This way we allow to assume
>> that if the standalone command exists, the transaction subcommand
>> exists as well.
> 
> Agreed - existence of a command at the same time the command is
> made transactionable serves as a nice substitute for not having
> full introspection into the 'BlockdevAction' union type, whereas if
> we introduce the command now but not transaction support until 1.7,
> life becomes tougher to know when it can be used where (although I
> HOPE we have introspection in 1.6).
> 

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.19 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQIcBAEBAgAGBQJRjfslAAoJEBvWZb6bTYbyiVkQAJpqoUcmTmzY9P8Me7pSlR5p
MVogKDdvtFpr+GVWWaiykB4rN79gGjduOGHtMpScuE3Grr42nFSeGnJoKqKP788T
1ZCaEaOt/Il3PeGWJOM7y8RkxnieTOqehPIUODq/qHVKE+mN0+sFlvGI67lhQveI
NWJCV2gzN6z7aCJE291BZxU4dtZzJv1SnkGqPZ2z/sEfQZulpVhmleE44SQRgFE1
oaSdfbniz/TRqmB5x8E3444X7YIZ9I+NTZuGlWr6V8tT9C5tnrB3jhMId9TVbQQg
2tDuYdm735kEC7K7byOtYWxJEKsEca/6dV5LbLh1gyoJWWb6/DM/bZ0je4XbtBFY
sxbKAV2llDbBRa8yWkp7p+N9THARj00skb3u9rE38+UP9p7aQUW6ZXwsn0cawBec
njDjdmnq4JiZQ+ez+wAFfxZmC1kx2Zfxsg/2aws67cf3ySzr14PSe0czgQaW0rgM
BP+7W4pd6pGmrnK+ASEK2r2gWniiF1OyngV4Q3v6d7SIAkKU6fPcu5iY/9INveNv
JRlMw/GE5/POENCuFhA6CEv/Dg48H6j9u9N44fC5i3KpS8mJaTn83av3MoBvAfqv
JAH2ZNHxaK+HoV4oxSm4gbIehlI1gh19Mb08u226EdGYcupTkPvDnOU+GhJFEqoN
0nQLUIysLgHjGJzQtX1/
=ULCr
-----END PGP SIGNATURE-----
Kevin Wolf May 13, 2013, 8:23 a.m. UTC | #5
Am 11.05.2013 um 05:34 hat Eric Blake geschrieben:
> On 05/08/2013 06:49 AM, Kevin Wolf wrote:
> > Am 29.04.2013 um 09:42 hat Stefan Hajnoczi geschrieben:
> >> @block-backup
> >>
> 
> > drive-backup would probably be a more consistent naming. We would then
> > still have block-backup for a future low-level command that doesn't
> > create everything by itself but takes an existing BlockDriverState (e.g.
> > created by blockdev-add).
> 
> At least it would match why we named a command 'drive-mirror' instead of
> 'block-mirror'.
> 
> Hmm, looking at qapi-schema.json, I wonder if we can rename
> 'BlockdevAction' to 'TransactionAction' as used in the @transaction
> command.  It wouldn't change what is sent over the wire in JSON, and
> until we have full introspection, there is no visibility into the type
> name used.  Changing the name now would let it be more generic to adding
> future transaction items that are not blockdev related.

Good point, I never realised that once we have schema introspection,
doing such changes is harder. I'm all for it - it's bad enough that we
have specifically block jobs instead of just background jobs, we
shouldn't repeat the same mistake with transactions.

Kevin
Kevin Wolf May 13, 2013, 8:28 a.m. UTC | #6
Am 11.05.2013 um 06:02 hat Eric Blake geschrieben:
> On 04/29/2013 01:42 AM, Stefan Hajnoczi wrote:
> > @block-backup
> > 
> > +++ b/qapi-schema.json
> > @@ -1715,6 +1715,37 @@
> >              '*speed': 'int' } }
> >  
> >  ##
> > +# @block-backup
> > +#
> > +# Start a point-in-time copy of a block device to a new destination.  The
> > +# status of ongoing block backup operations can be checked with
> > +# query-block-jobs.  The operation can be stopped before it has completed using
> > +# the block-job-cancel command.
> 
> Still might be worth mentioning that 'query-block-jobs' will list it as
> a job of type 'backup'.
> 
> > +#
> > +# @device: the name of the device whose writes should be mirrored.
> > +#
> > +# @target: the target of the new image. If the file exists, or if it
> > +#          is a device, the existing file/device will be used as the new
> > +#          destination.  If it does not exist, a new file will be created.
> > +#
> > +# @format: #optional the format of the new destination, default is to
> > +#          probe if @mode is 'existing', else the format of the source
> > +#
> > +# @mode: #optional whether and how QEMU should create a new image, default is
> > +#        'absolute-paths'.
> > +#
> > +# @speed: #optional the maximum speed, in bytes per second
> > +#
> > +# Returns: nothing on success
> > +#          If @device is not a valid block device, DeviceNotFound
> > +#
> > +# Since 1.6
> > +##
> > +{ 'command': 'block-backup',
> > +  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
> 
> Hmm - wondering if we should add an enum type for supported disk formats
> instead of using free-form strings.  The wire representation would be
> the same, and now's the time to do it before we add introspection (it's
> more than just this command impacted).

And ideally we shouldn't make it a static list that contains every
format for which qemu has some code, but only those that are actually
compiled in. (Hm, and probably not protocols?)

Luiz, any idea how to do something like this, a QAPI enum with values
that are determined at runtime? Especially with respect to the coming
schema introspection?

Kevin
Eric Blake May 13, 2013, 12:56 p.m. UTC | #7
On 05/13/2013 02:28 AM, Kevin Wolf wrote:

>>> +{ 'command': 'block-backup',
>>> +  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
>>
>> Hmm - wondering if we should add an enum type for supported disk formats
>> instead of using free-form strings.  The wire representation would be
>> the same, and now's the time to do it before we add introspection (it's
>> more than just this command impacted).
> 
> And ideally we shouldn't make it a static list that contains every
> format for which qemu has some code, but only those that are actually
> compiled in. (Hm, and probably not protocols?)
> 
> Luiz, any idea how to do something like this, a QAPI enum with values
> that are determined at runtime? Especially with respect to the coming
> schema introspection?

Or maybe we make the 'enum' list ALL possible types, but then add a
query-* command that returns an array of only those enum values that are
supported.  Introspection would see all types, but the query command
would be the useful variant that is runtime-dependent.
Kevin Wolf May 13, 2013, 1:09 p.m. UTC | #8
Am 13.05.2013 um 14:56 hat Eric Blake geschrieben:
> On 05/13/2013 02:28 AM, Kevin Wolf wrote:
> 
> >>> +{ 'command': 'block-backup',
> >>> +  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
> >>
> >> Hmm - wondering if we should add an enum type for supported disk formats
> >> instead of using free-form strings.  The wire representation would be
> >> the same, and now's the time to do it before we add introspection (it's
> >> more than just this command impacted).
> > 
> > And ideally we shouldn't make it a static list that contains every
> > format for which qemu has some code, but only those that are actually
> > compiled in. (Hm, and probably not protocols?)
> > 
> > Luiz, any idea how to do something like this, a QAPI enum with values
> > that are determined at runtime? Especially with respect to the coming
> > schema introspection?
> 
> Or maybe we make the 'enum' list ALL possible types, but then add a
> query-* command that returns an array of only those enum values that are
> supported.  Introspection would see all types, but the query command
> would be the useful variant that is runtime-dependent.

Then is there any advantage in making it an enum in the first place? Can
libvirt really make use of the information "this qemu version could have
been compiled with VHDX support, but it hasn't"?

Kevin
Luiz Capitulino May 13, 2013, 1:18 p.m. UTC | #9
On Mon, 13 May 2013 15:09:31 +0200
Kevin Wolf <kwolf@redhat.com> wrote:

> Am 13.05.2013 um 14:56 hat Eric Blake geschrieben:
> > On 05/13/2013 02:28 AM, Kevin Wolf wrote:
> > 
> > >>> +{ 'command': 'block-backup',
> > >>> +  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
> > >>
> > >> Hmm - wondering if we should add an enum type for supported disk formats
> > >> instead of using free-form strings.  The wire representation would be
> > >> the same, and now's the time to do it before we add introspection (it's
> > >> more than just this command impacted).
> > > 
> > > And ideally we shouldn't make it a static list that contains every
> > > format for which qemu has some code, but only those that are actually
> > > compiled in. (Hm, and probably not protocols?)
> > > 
> > > Luiz, any idea how to do something like this, a QAPI enum with values
> > > that are determined at runtime? Especially with respect to the coming
> > > schema introspection?
> > 
> > Or maybe we make the 'enum' list ALL possible types, but then add a
> > query-* command that returns an array of only those enum values that are
> > supported.  Introspection would see all types, but the query command
> > would be the useful variant that is runtime-dependent.

Agreed. This is a capability, and we're adding query- commands to query
capabilities.

> Then is there any advantage in making it an enum in the first place?

Eric is in a better position to answer this, but the fact that this can
be queried isn't a strong pro for having it?
Eric Blake May 13, 2013, 2:14 p.m. UTC | #10
On 05/13/2013 07:18 AM, Luiz Capitulino wrote:
>>>> Luiz, any idea how to do something like this, a QAPI enum with values
>>>> that are determined at runtime? Especially with respect to the coming
>>>> schema introspection?
>>>
>>> Or maybe we make the 'enum' list ALL possible types, but then add a
>>> query-* command that returns an array of only those enum values that are
>>> supported.  Introspection would see all types, but the query command
>>> would be the useful variant that is runtime-dependent.
> 
> Agreed. This is a capability, and we're adding query- commands to query
> capabilities.
> 
>> Then is there any advantage in making it an enum in the first place?
> 
> Eric is in a better position to answer this, but the fact that this can
> be queried isn't a strong pro for having it?

Hmm, you raise an interesting point - if we have a query-block-formats
command that returns an array of strings, then keep 'str' everywhere
else a format is required, that is no different for what is sent over
the wire compared to a query-block-formats that returns an array of
'BlockFormat' enum values, with the enum showing all possible formats
(even if support wasn't compiled in), and with 'BlockFormat' everywhere
else.  Introspection-wise, you'd have to know that you call
query-block-formats instead of introspecting on the type of the format
argument, but information-wise, there's no loss of details if the query-
command provides the runtime list, and the remaining commands stick with
'str'.  I still think an enum is a bit nicer from the type-safety
aspect, but I'm finding it hard to envision any scenario where libvirt
would have to resort to introspection if we have a query-block-formats
in place, and thus am not opposed to your idea of avoiding the enum.
Kevin Wolf May 13, 2013, 2:27 p.m. UTC | #11
Am 13.05.2013 um 16:14 hat Eric Blake geschrieben:
> On 05/13/2013 07:18 AM, Luiz Capitulino wrote:
> >>>> Luiz, any idea how to do something like this, a QAPI enum with values
> >>>> that are determined at runtime? Especially with respect to the coming
> >>>> schema introspection?
> >>>
> >>> Or maybe we make the 'enum' list ALL possible types, but then add a
> >>> query-* command that returns an array of only those enum values that are
> >>> supported.  Introspection would see all types, but the query command
> >>> would be the useful variant that is runtime-dependent.
> > 
> > Agreed. This is a capability, and we're adding query- commands to query
> > capabilities.
> > 
> >> Then is there any advantage in making it an enum in the first place?
> > 
> > Eric is in a better position to answer this, but the fact that this can
> > be queried isn't a strong pro for having it?
> 
> Hmm, you raise an interesting point - if we have a query-block-formats
> command that returns an array of strings, then keep 'str' everywhere
> else a format is required, that is no different for what is sent over
> the wire compared to a query-block-formats that returns an array of
> 'BlockFormat' enum values, with the enum showing all possible formats
> (even if support wasn't compiled in), and with 'BlockFormat' everywhere
> else.  Introspection-wise, you'd have to know that you call
> query-block-formats instead of introspecting on the type of the format
> argument, but information-wise, there's no loss of details if the query-
> command provides the runtime list, and the remaining commands stick with
> 'str'.  I still think an enum is a bit nicer from the type-safety
> aspect, but I'm finding it hard to envision any scenario where libvirt
> would have to resort to introspection if we have a query-block-formats
> in place, and thus am not opposed to your idea of avoiding the enum.

I think long term we'll need a dynamic schema anyway. As we go forward
with modularisation and putting things into shared libraries, we'll have
modules that add support for commands, enum values, etc.

Providing the full list of theoretically available elements (i.e. what
would be there, if everything was compiled and all modules were loaded)
would probably implement the spec for the introspection interfaces by the
letter, but just give useless information. Callers want to know what's
really there.

If we're going to have a query-* command for everything, then we don't
need introspection at all. I would however prefer having the uniform
schema introspection and building the schema at runtime instead of many
separate query-* commands.

Kevin
Eric Blake May 13, 2013, 2:50 p.m. UTC | #12
On 05/13/2013 08:27 AM, Kevin Wolf wrote:
> I think long term we'll need a dynamic schema anyway. As we go forward
> with modularisation and putting things into shared libraries, we'll have
> modules that add support for commands, enum values, etc.

In other words, qapi-schema.json should have a way to declare a
dynamic-enum, where introspection on that enum will see what is made
available at runtime, rather than manually listing the enum contents
directly in the .json file.

> 
> Providing the full list of theoretically available elements (i.e. what
> would be there, if everything was compiled and all modules were loaded)
> would probably implement the spec for the introspection interfaces by the
> letter, but just give useless information. Callers want to know what's
> really there.
> 
> If we're going to have a query-* command for everything, then we don't
> need introspection at all. I would however prefer having the uniform
> schema introspection and building the schema at runtime instead of many
> separate query-* commands.

Indeed, having introspection of a dynamic enum results in fewer commands
overall, by making a reusable command have more power.  And maybe it's
possible to have introspection do both - with an optional boolean
parameter that distinguishes between full vs. runtime querying of any
enum type.
Wayne Xia May 14, 2013, 2:18 a.m. UTC | #13
于 2013-5-13 22:50, Eric Blake 写道:
> On 05/13/2013 08:27 AM, Kevin Wolf wrote:
>> I think long term we'll need a dynamic schema anyway. As we go forward
>> with modularisation and putting things into shared libraries, we'll have
>> modules that add support for commands, enum values, etc.
>
> In other words, qapi-schema.json should have a way to declare a
> dynamic-enum, where introspection on that enum will see what is made
> available at runtime, rather than manually listing the enum contents
> directly in the .json file.
>
>>
>> Providing the full list of theoretically available elements (i.e. what
>> would be there, if everything was compiled and all modules were loaded)
>> would probably implement the spec for the introspection interfaces by the
>> letter, but just give useless information. Callers want to know what's
>> really there.
>>
>> If we're going to have a query-* command for everything, then we don't
>> need introspection at all. I would however prefer having the uniform
>> schema introspection and building the schema at runtime instead of many
>> separate query-* commands.
>
> Indeed, having introspection of a dynamic enum results in fewer commands
> overall, by making a reusable command have more power.  And maybe it's
> possible to have introspection do both - with an optional boolean
> parameter that distinguishes between full vs. runtime querying of any
> enum type.
>
   "string <-> enum mapping" issue happens when I try coding libqblock,
what I found is that, since lower level implement code still depends
string, so there will be another translating back to string inside
later. For format string, it is probably OK since two functions will be
enough, but when more enum is wanted, things become complex.
    So I feel it is right to try use enum in the API now, but maybe a
plan to reorganize block layer will be also needed, which may be
a bit complex needing serous plan.
Stefan Hajnoczi May 14, 2013, 8:44 a.m. UTC | #14
On Wed, May 08, 2013 at 02:49:00PM +0200, Kevin Wolf wrote:
> Am 29.04.2013 um 09:42 hat Stefan Hajnoczi geschrieben:
> > @block-backup
> > 
> > Start a point-in-time copy of a block device to a new destination.  The
> > status of ongoing block backup operations can be checked with
> > query-block-jobs.  The operation can be stopped before it has completed using
> > the block-job-cancel command.
> > 
> > @device: the name of the device whose writes should be mirrored.
> > 
> > @target: the target of the new image. If the file exists, or if it
> >          is a device, the existing file/device will be used as the new
> >          destination.  If it does not exist, a new file will be created.
> > 
> > @format: #optional the format of the new destination, default is to
> >          probe if @mode is 'existing', else the format of the source
> > 
> > @mode: #optional whether and how QEMU should create a new image, default is
> >        'absolute-paths'.
> > 
> > @speed: #optional the maximum speed, in bytes per second
> > 
> > Returns: nothing on success
> >          If @device is not a valid block device, DeviceNotFound
> > 
> > Since 1.6
> > 
> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> 
> drive-backup would probably be a more consistent naming. We would then
> still have block-backup for a future low-level command that doesn't
> create everything by itself but takes an existing BlockDriverState (e.g.
> created by blockdev-add).
> 
> We should also make it transactionable from the beginning, as we don't
> have schema introspection yet. This way we allow to assume that if the
> standalone command exists, the transaction subcommand exists as well.

Sounds good.  I'll make both changes.

Stefan
Stefan Hajnoczi May 14, 2013, 8:48 a.m. UTC | #15
On Fri, May 10, 2013 at 10:02:14PM -0600, Eric Blake wrote:
> On 04/29/2013 01:42 AM, Stefan Hajnoczi wrote:
> > @block-backup
> > 
> > +++ b/qapi-schema.json
> > @@ -1715,6 +1715,37 @@
> >              '*speed': 'int' } }
> >  
> >  ##
> > +# @block-backup
> > +#
> > +# Start a point-in-time copy of a block device to a new destination.  The
> > +# status of ongoing block backup operations can be checked with
> > +# query-block-jobs.  The operation can be stopped before it has completed using
> > +# the block-job-cancel command.
> 
> Still might be worth mentioning that 'query-block-jobs' will list it as
> a job of type 'backup'.

Will fix in v3.

> > +#
> > +# @device: the name of the device whose writes should be mirrored.
> > +#
> > +# @target: the target of the new image. If the file exists, or if it
> > +#          is a device, the existing file/device will be used as the new
> > +#          destination.  If it does not exist, a new file will be created.
> > +#
> > +# @format: #optional the format of the new destination, default is to
> > +#          probe if @mode is 'existing', else the format of the source
> > +#
> > +# @mode: #optional whether and how QEMU should create a new image, default is
> > +#        'absolute-paths'.
> > +#
> > +# @speed: #optional the maximum speed, in bytes per second
> > +#
> > +# Returns: nothing on success
> > +#          If @device is not a valid block device, DeviceNotFound
> > +#
> > +# Since 1.6
> > +##
> > +{ 'command': 'block-backup',
> > +  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
> 
> Hmm - wondering if we should add an enum type for supported disk formats
> instead of using free-form strings.  The wire representation would be
> the same, and now's the time to do it before we add introspection (it's
> more than just this command impacted).

Interesting discussion about dynamic schema.  Since the wire format is
the same, I don't want to attempt solving the problem in the
block-backup series.
diff mbox

Patch

diff --git a/blockdev.c b/blockdev.c
index 6e293e9..afb9b4a 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1279,6 +1279,98 @@  void qmp_block_commit(const char *device,
     drive_get_ref(drive_get_by_blockdev(bs));
 }
 
+void qmp_block_backup(const char *device, const char *target,
+                      bool has_format, const char *format,
+                      bool has_mode, enum NewImageMode mode,
+                      bool has_speed, int64_t speed,
+                      Error **errp)
+{
+    BlockDriverState *bs;
+    BlockDriverState *target_bs;
+    BlockDriver *proto_drv;
+    BlockDriver *drv = NULL;
+    Error *local_err = NULL;
+    int flags;
+    uint64_t size;
+    int ret;
+
+    if (!has_speed) {
+        speed = 0;
+    }
+    if (!has_mode) {
+        mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+    }
+
+    bs = bdrv_find(device);
+    if (!bs) {
+        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+        return;
+    }
+
+    if (!bdrv_is_inserted(bs)) {
+        error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+        return;
+    }
+
+    if (!has_format) {
+        format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
+    }
+    if (format) {
+        drv = bdrv_find_format(format);
+        if (!drv) {
+            error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+            return;
+        }
+    }
+
+    if (bdrv_in_use(bs)) {
+        error_set(errp, QERR_DEVICE_IN_USE, device);
+        return;
+    }
+
+    flags = bs->open_flags | BDRV_O_RDWR;
+
+    proto_drv = bdrv_find_protocol(target);
+    if (!proto_drv) {
+        error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+        return;
+    }
+
+    bdrv_get_geometry(bs, &size);
+    size *= 512;
+    if (mode != NEW_IMAGE_MODE_EXISTING) {
+        assert(format && drv);
+        bdrv_img_create(target, format,
+                        NULL, NULL, NULL, size, flags, &local_err, false);
+    }
+
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    target_bs = bdrv_new("");
+    ret = bdrv_open(target_bs, target, NULL, flags, drv);
+
+    if (ret < 0) {
+        bdrv_delete(target_bs);
+        error_set(errp, QERR_OPEN_FILE_FAILED, target);
+        return;
+    }
+
+    backup_start(bs, target_bs, speed, block_job_cb, bs, &local_err);
+    if (local_err != NULL) {
+        bdrv_delete(target_bs);
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    /* Grab a reference so hotplug does not delete the BlockDriverState from
+     * underneath us.
+     */
+    drive_get_ref(drive_get_by_blockdev(bs));
+}
+
 #define DEFAULT_MIRROR_BUF_SIZE   (10 << 20)
 
 void qmp_drive_mirror(const char *device, const char *target,
diff --git a/qapi-schema.json b/qapi-schema.json
index 5b0fb3b..550ccac 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1715,6 +1715,37 @@ 
             '*speed': 'int' } }
 
 ##
+# @block-backup
+#
+# Start a point-in-time copy of a block device to a new destination.  The
+# status of ongoing block backup operations can be checked with
+# query-block-jobs.  The operation can be stopped before it has completed using
+# the block-job-cancel command.
+#
+# @device: the name of the device whose writes should be mirrored.
+#
+# @target: the target of the new image. If the file exists, or if it
+#          is a device, the existing file/device will be used as the new
+#          destination.  If it does not exist, a new file will be created.
+#
+# @format: #optional the format of the new destination, default is to
+#          probe if @mode is 'existing', else the format of the source
+#
+# @mode: #optional whether and how QEMU should create a new image, default is
+#        'absolute-paths'.
+#
+# @speed: #optional the maximum speed, in bytes per second
+#
+# Returns: nothing on success
+#          If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.6
+##
+{ 'command': 'block-backup',
+  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
+            '*mode': 'NewImageMode', '*speed': 'int' } }
+
+##
 # @drive-mirror
 #
 # Start mirroring a block device's writes to a new destination.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 0e89132..f9fb926 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -889,6 +889,12 @@  EQMP
     },
 
     {
+        .name       = "block-backup",
+        .args_type  = "device:B,target:s,speed:i?,mode:s?,format:s?",
+        .mhandler.cmd_new = qmp_marshal_input_block_backup,
+    },
+
+    {
         .name       = "block-job-set-speed",
         .args_type  = "device:B,speed:o",
         .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,