On Fri, Sep 17, 2021 at 03:34:56PM +0200, Peter Krempa wrote: > Issuing simple QMP commands is pain as they need to be wrapped by the > JSON wrapper: > > { "execute": "COMMAND" } > > and optionally also: > > { "execute": "COMMAND", "arguments":...} > > For simple commands without arguments we can add syntax sugar to virsh > which allows simple usage of QMP and additionally prepares also for > passing through of the 'arguments' section: > > virsh qemu-monitor-command $VM query-status > > is equivalent to > > virsh qemu-monitor-command $VM '{"execute":"query-status"}' > > and > > virsh qemu-monitor-command $VM query-named-block-nodes '{"flat":true}' > or > virsh qemu-monitor-command $VM query-named-block-nodes '"flat":true' > > is equivalent to > > virsh qemu-monitor-command $VM '{"execute":"query-named-block-nodes", "arguments":{"flat":true}}' Very cool; thanks for this. > Signed-off-by: Peter Krempa <pkrempa@xxxxxxxxxx> FWIW: Very-much-appreciated-by: Kashyap Chamarthy <kchamart@xxxxxxxxxx> > --- > > v2: > - dropped the '--qmpwrap' option and do wrapping if we don't get a > JSON object instead. Similarly for arguments. > > docs/manpages/virsh.rst | 16 ++++++- > tools/virsh-domain.c | 98 ++++++++++++++++++++++++++++++++++++----- > 2 files changed, 103 insertions(+), 11 deletions(-) > > diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst > index 350ded2026..b4d31bf884 100644 > --- a/docs/manpages/virsh.rst > +++ b/docs/manpages/virsh.rst > @@ -7772,7 +7772,21 @@ If more than one argument is provided for *command*, they are concatenated with > a space in between before passing the single command to the monitor. > > Note that libvirt uses the QMP to talk to qemu so *command* must be valid JSON > -in QMP format to work properly. > +in QMP format to work properly. If *command* is not a JSON object libvirt tries > +to wrap it as a JSON object to provide convenient interface such as the groups > +of commands with identical handling: > + > +:: > + > + # simple command > + $ virsh qemu-monitor-command VM commandname > + $ virsh qemu-monitor-command VM '{"execute":"commandname"}' Do you want to give a proper example here? E.g. `query-kvm` or `query-status`, or `query-block`? > + # with arguments > + $ virsh qemu-monitor-command VM commandname '"arg1":123' '"arg2":"test"' > + $ virsh qemu-monitor-command VM commandname '{"arg1":123,"arg2":"test"}' > + $ virsh qemu-monitor-command VM '{"execute":"commandname", "arguments":{"arg1":123,"arg2":"test"}}' > + > > If *--pretty* is given the QMP reply is pretty-printed. > > diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c > index 70aa4167c2..659aa1a242 100644 > --- a/tools/virsh-domain.c > +++ b/tools/virsh-domain.c > @@ -9445,6 +9445,84 @@ static const vshCmdOptDef opts_qemu_monitor_command[] = { > {.name = NULL} > }; > > + > +static char * > +cmdQemuMonitorCommandConcatCmd(vshControl *ctl, > + const vshCmd *cmd, > + const vshCmdOpt *opt) > +{ > + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; > + > + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) > + virBufferAsprintf(&buf, "%s ", opt->data); > + > + virBufferTrim(&buf, " "); > + > + return virBufferContentAndReset(&buf); > +} > + > + > +static char * > +cmdQemuMonitorCommandQMPWrap(vshControl *ctl, > + const vshCmd *cmd) > +{ > + g_autofree char *fullcmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL); > + g_autoptr(virJSONValue) fullcmdjson = virJSONValueFromString(fullcmd); > + g_autofree char *fullargs = NULL; > + g_autoptr(virJSONValue) fullargsjson = NULL; > + const vshCmdOpt *opt = NULL; > + const char *commandname = NULL; > + g_autoptr(virJSONValue) command = NULL; > + g_autoptr(virJSONValue) arguments = NULL; > + > + /* if we've got a JSON object, pass it through */ > + if (virJSONValueIsObject(fullcmdjson)) > + return g_steal_pointer(&fullcmd); > + > + /* we try to wrap the command and possible arguments into a JSON object, if > + * we as fall back we pass through what we've got from the user */ > + > + if ((opt = vshCommandOptArgv(ctl, cmd, opt))) > + commandname = opt->data; > + > + /* now we process arguments similarly to how we've dealt with the full command */ > + if ((fullargs = cmdQemuMonitorCommandConcatCmd(ctl, cmd, opt))) > + fullargsjson = virJSONValueFromString(fullargs); > + > + /* for empty args or a valid JSON object we just use that */ > + if (!fullargs || virJSONValueIsObject(fullargsjson)) { > + arguments = g_steal_pointer(&fullargsjson); > + } else { > + /* for a non-object we try to concatenate individual _ARGV bits into a > + * JSON object wrapper and try using that */ > + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; > + > + virBufferAddLit(&buf, "{"); > + /* opt points to the _ARGV option bit containing the command so we'll > + * iterate through the arguments now */ > + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) > + virBufferAsprintf(&buf, "%s,", opt->data); > + > + virBufferTrim(&buf, ","); > + virBufferAddLit(&buf, "}"); > + > + if (!(arguments = virJSONValueFromString(virBufferCurrentContent(&buf)))) { > + vshError(ctl, _("failed to wrap arguments '%s' into a QMP command wrapper"), > + fullargs); > + return NULL; > + } > + } > + > + if (virJSONValueObjectCreate(&command, > + "s:execute", commandname, > + "A:arguments", &arguments, > + NULL) < 0) > + return NULL; > + > + return virJSONValueToString(command, false); > +} > + > + > static bool > cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) > { > @@ -9453,8 +9531,6 @@ cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) > g_autofree char *result = NULL; > g_autoptr(virJSONValue) resultjson = NULL; > unsigned int flags = 0; > - const vshCmdOpt *opt = NULL; > - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; > bool pretty = vshCommandOptBool(cmd, "pretty"); > bool returnval = vshCommandOptBool(cmd, "return-value"); > virJSONValue *formatjson; > @@ -9466,15 +9542,17 @@ cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) > if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) > return false; > > - while ((opt = vshCommandOptArgv(ctl, cmd, opt))) > - virBufferAsprintf(&buf, "%s ", opt->data); > - > - virBufferTrim(&buf, " "); > - > - monitor_cmd = virBufferContentAndReset(&buf); > - > - if (vshCommandOptBool(cmd, "hmp")) > + if (vshCommandOptBool(cmd, "hmp")) { > flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP; > + monitor_cmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL); > + } else { > + monitor_cmd = cmdQemuMonitorCommandQMPWrap(ctl, cmd); > + } > + > + if (!monitor_cmd) { > + vshSaveLibvirtError(); > + return NULL; > + } > > if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0) > return false; > -- > 2.31.1 > -- /kashyap