diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f39d0a6c50..2bb3bbc48f 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2048,6 +2048,10 @@ static const vshCmdOptDef opts_block_job[] = { .type = VSH_OT_BOOL, .help = N_("get active job information for the specified disk") }, + {.name = "bytes", + .type = VSH_OT_BOOL, + .help = N_("with --info, get bandwidth in bytes rather than MiB/s") + }, {.name = "raw", .type = VSH_OT_BOOL, .help = N_("implies --info; output details rather than human summary") @@ -2080,8 +2084,9 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd) { virDomainBlockJobInfo info; bool ret = false; - int rc; + int rc = -1; bool raw = vshCommandOptBool(cmd, "raw"); + bool bytes = vshCommandOptBool(cmd, "bytes"); bool abortMode = (vshCommandOptBool(cmd, "abort") || vshCommandOptBool(cmd, "async") || vshCommandOptBool(cmd, "pivot")); @@ -2090,12 +2095,18 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd) virDomainPtr dom = NULL; const char *path; unsigned int flags = 0; + unsigned long long speed; if (abortMode + infoMode + bandwidth > 1) { vshError(ctl, "%s", _("conflict between abort, info, and bandwidth modes")); return false; } + /* XXX also support --bytes with bandwidth mode */ + if (bytes && (abortMode || bandwidth)) { + vshError(ctl, "%s", _("--bytes requires info mode")); + return false; + } if (abortMode) return blockJobImpl(ctl, cmd, VSH_CMD_BLOCK_JOB_ABORT, NULL); @@ -2110,9 +2121,47 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0) goto cleanup; - rc = virDomainGetBlockJobInfo(dom, path, &info, flags); - if (rc < 0) - goto cleanup; + /* If bytes were requested, or if raw mode is not forcing a MiB/s + * query and cache can't prove failure, then query bytes/sec. */ + if (bytes || !(raw || ctl->blockJobNoBytes)) { + flags |= VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES; + rc = virDomainGetBlockJobInfo(dom, path, &info, flags); + if (rc < 0) { + /* Check for particular errors, let all the rest be fatal. */ + switch (last_error->code) { + case VIR_ERR_INVALID_ARG: + ctl->blockJobNoBytes = true; + /* fallthrough */ + case VIR_ERR_OVERFLOW: + if (!bytes && !raw) { + /* try again with MiB/s, unless forcing bytes */ + vshResetLibvirtError(); + break; + } + /* fallthrough */ + default: + goto cleanup; + } + } + speed = info.bandwidth; + } + /* If we don't already have a query result, query for MiB/s */ + if (rc < 0) { + flags &= ~VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES; + if ((rc = virDomainGetBlockJobInfo(dom, path, &info, flags)) < 0) + goto cleanup; + speed = info.bandwidth; + /* Scale to bytes/s unless in raw mode */ + if (!raw) { + speed <<= 20; + if (speed >> 20 != info.bandwidth) { + vshError(ctl, _("overflow in converting %ld MiB/s to bytes\n"), + info.bandwidth); + goto cleanup; + } + } + } + if (rc == 0) { if (!raw) vshPrint(ctl, _("No current block job for %s"), path); @@ -2127,9 +2176,12 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd) } else { vshPrintJobProgress(vshDomainBlockJobToString(info.type), info.end - info.cur, info.end); - if (info.bandwidth != 0) - vshPrint(ctl, _(" Bandwidth limit: %lu MiB/s"), - info.bandwidth); + if (speed) { + const char *unit; + double val = vshPrettyCapacity(speed, &unit); + vshPrint(ctl, _(" Bandwidth limit: %llu bytes/s (%-.3lf %s/s)"), + speed, val, unit); + } vshPrint(ctl, "\n"); } ret = true; diff --git a/tools/virsh.c b/tools/virsh.c index 713c9a5fb6..9706acc88e 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -397,6 +397,7 @@ vshReconnect(vshControl *ctl) disconnected = 0; ctl->useGetInfo = false; ctl->useSnapshotOld = false; + ctl->blockJobNoBytes = false; } @@ -454,6 +455,7 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd) ctl->useGetInfo = false; ctl->useSnapshotOld = false; + ctl->blockJobNoBytes = false; ctl->readonly = ro; ctl->conn = vshConnect(ctl, ctl->name, ctl->readonly); diff --git a/tools/virsh.h b/tools/virsh.h index b4df24bb39..7d5d8a2dc6 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -238,6 +238,8 @@ struct _vshControl { virDomainGetState is not supported */ bool useSnapshotOld; /* cannot use virDomainSnapshotGetParent or virDomainSnapshotNumChildren */ + bool blockJobNoBytes; /* true if _BANDWIDTH_BYTE blockjob flags + are missing */ virThread eventLoop; virMutex lock; bool eventLoopStarted; diff --git a/tools/virsh.pod b/tools/virsh.pod index acde0a1ae0..d95df364d8 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1013,7 +1013,7 @@ exclusive. If no flag is specified, behavior is different depending on hypervisor. =item B I I { [I<--abort>] [I<--async>] [I<--pivot>] | -[I<--info>] [I<--raw>] | [I] } +[I<--info>] [I<--raw>] [I<--bytes>] | [I] } Manage active block operations. There are three mutually-exclusive modes: I<--info>, I, and I<--abort>. I<--async> and I<--pivot> imply @@ -1034,7 +1034,11 @@ commit job be pivoted over to the new image. In I<--info> mode, the active job information on the specified disk will be printed. By default, the output is a single human-readable summary line; this format may change in future versions. Adding -I<--raw> lists each field of the struct, in a stable format. +I<--raw> lists each field of the struct, in a stable format. If the +I<--bytes> flag is set, then the command errors out if the server could +not supply bytes/s resolution; when omitting the flag, raw output is +listed in MiB/s and human-readable output automatically selects the +best resolution supported by the server. I can be used to set bandwidth limit for the active job. Specifying a negative value is interpreted as an unsigned long long