mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 06:05:27 +00:00
virsh: improve storage unit parsing
Now can now do: virsh vol-resize $vol 10M virsh blockresize $dom $vol 10M to get both interfaces to resize to 10MiB. The remaining wart is that vol-resize defaults to bytes, but blockresize defaults to KiB, but we can't break existing scripts; oh well, it's no worse than the same wart of the underlying virDomainBlockResize. The API for virStorageVolResize states that capacity must always be positive, and that the presence of shrink and delta flags is what implies a negative change. * tools/virsh.c (vshCommandOptScaledInt): New function. (cmdVolResize): Don't pass negative size. (cmdVolSize): Rename... (vshVolSize): ...and use new helper routine. (cmdBlockResize): Use new helper routine, and support new bytes flag. * tools/virsh.pod (NOTES): Document suffixes. (blockresize, vol-create-as, vol-resize): Point to notes.
This commit is contained in:
parent
af3f9aabde
commit
ab95da4058
123
tools/virsh.c
123
tools/virsh.c
@ -313,6 +313,10 @@ static int vshCommandOptLongLong(const vshCmd *cmd, const char *name,
|
|||||||
static int vshCommandOptULongLong(const vshCmd *cmd, const char *name,
|
static int vshCommandOptULongLong(const vshCmd *cmd, const char *name,
|
||||||
unsigned long long *value)
|
unsigned long long *value)
|
||||||
ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
|
ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
|
||||||
|
static int vshCommandOptScaledInt(const vshCmd *cmd, const char *name,
|
||||||
|
unsigned long long *value, int scale,
|
||||||
|
unsigned long long max)
|
||||||
|
ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
|
||||||
static bool vshCommandOptBool(const vshCmd *cmd, const char *name);
|
static bool vshCommandOptBool(const vshCmd *cmd, const char *name);
|
||||||
static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd,
|
static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd,
|
||||||
const vshCmdOpt *opt);
|
const vshCmdOpt *opt);
|
||||||
@ -7625,9 +7629,10 @@ static const vshCmdInfo info_block_resize[] = {
|
|||||||
|
|
||||||
static const vshCmdOptDef opts_block_resize[] = {
|
static const vshCmdOptDef opts_block_resize[] = {
|
||||||
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
|
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
|
||||||
{"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path of block device")},
|
{"path", VSH_OT_DATA, VSH_OFLAG_REQ,
|
||||||
{"size", VSH_OT_INT, VSH_OFLAG_REQ, N_("New size of the block device in kilobytes, "
|
N_("Fully-qualified path of block device")},
|
||||||
"the size must be integer")},
|
{"size", VSH_OT_INT, VSH_OFLAG_REQ,
|
||||||
|
N_("New size of the block device, as scaled integer (default KiB)")},
|
||||||
{NULL, 0, 0, NULL}
|
{NULL, 0, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -7648,15 +7653,16 @@ cmdBlockResize(vshControl *ctl, const vshCmd *cmd)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vshCommandOptULongLong(cmd, "size", &size) < 0) {
|
if (vshCommandOptScaledInt(cmd, "size", &size, 1024, ULLONG_MAX) < 0) {
|
||||||
vshError(ctl, "%s", _("Unable to parse integer"));
|
vshError(ctl, "%s", _("Unable to parse integer"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > ULLONG_MAX / 1024) {
|
/* Prefer the older interface of KiB. */
|
||||||
vshError(ctl, _("Size must be less than %llu"), ULLONG_MAX / 1024);
|
if (size % 1024 == 0)
|
||||||
return false;
|
size /= 1024;
|
||||||
}
|
else
|
||||||
|
flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
|
||||||
|
|
||||||
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
|
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
|
||||||
return false;
|
return false;
|
||||||
@ -11046,43 +11052,26 @@ static const vshCmdInfo info_vol_create_as[] = {
|
|||||||
static const vshCmdOptDef opts_vol_create_as[] = {
|
static const vshCmdOptDef opts_vol_create_as[] = {
|
||||||
{"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
|
{"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
|
||||||
{"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
|
{"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
|
||||||
{"capacity", VSH_OT_DATA, VSH_OFLAG_REQ, N_("size of the vol with optional k,M,G,T suffix")},
|
{"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
|
||||||
{"allocation", VSH_OT_STRING, 0, N_("initial allocation size with optional k,M,G,T suffix")},
|
N_("size of the vol, as scaled integer (default bytes)")},
|
||||||
{"format", VSH_OT_STRING, 0, N_("file format type raw,bochs,qcow,qcow2,vmdk")},
|
{"allocation", VSH_OT_STRING, 0,
|
||||||
{"backing-vol", VSH_OT_STRING, 0, N_("the backing volume if taking a snapshot")},
|
N_("initial allocation size, as scaled integer (default bytes)")},
|
||||||
{"backing-vol-format", VSH_OT_STRING, 0, N_("format of backing volume if taking a snapshot")},
|
{"format", VSH_OT_STRING, 0,
|
||||||
|
N_("file format type raw,bochs,qcow,qcow2,vmdk")},
|
||||||
|
{"backing-vol", VSH_OT_STRING, 0,
|
||||||
|
N_("the backing volume if taking a snapshot")},
|
||||||
|
{"backing-vol-format", VSH_OT_STRING, 0,
|
||||||
|
N_("format of backing volume if taking a snapshot")},
|
||||||
{NULL, 0, 0, NULL}
|
{NULL, 0, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cmdVolSize(const char *data, unsigned long long *val)
|
static int
|
||||||
|
vshVolSize(const char *data, unsigned long long *val)
|
||||||
{
|
{
|
||||||
char *end;
|
char *end;
|
||||||
if (virStrToLong_ull(data, &end, 10, val) < 0)
|
if (virStrToLong_ull(data, &end, 10, val) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
return virScaleInteger(val, end, 1, ULLONG_MAX);
|
||||||
if (end && *end) {
|
|
||||||
/* Deliberate fallthrough cases here :-) */
|
|
||||||
switch (*end) {
|
|
||||||
case 'T':
|
|
||||||
*val *= 1024;
|
|
||||||
/* fallthrough */
|
|
||||||
case 'G':
|
|
||||||
*val *= 1024;
|
|
||||||
/* fallthrough */
|
|
||||||
case 'M':
|
|
||||||
*val *= 1024;
|
|
||||||
/* fallthrough */
|
|
||||||
case 'k':
|
|
||||||
*val *= 1024;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
end++;
|
|
||||||
if (*end)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -11108,11 +11097,11 @@ cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
|
|||||||
|
|
||||||
if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
|
if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
if (cmdVolSize(capacityStr, &capacity) < 0)
|
if (vshVolSize(capacityStr, &capacity) < 0)
|
||||||
vshError(ctl, _("Malformed size %s"), capacityStr);
|
vshError(ctl, _("Malformed size %s"), capacityStr);
|
||||||
|
|
||||||
if ((vshCommandOptString(cmd, "allocation", &allocationStr) > 0) &&
|
if ((vshCommandOptString(cmd, "allocation", &allocationStr) > 0) &&
|
||||||
(cmdVolSize(allocationStr, &allocation) < 0))
|
(vshVolSize(allocationStr, &allocation) < 0))
|
||||||
vshError(ctl, _("Malformed size %s"), allocationStr);
|
vshError(ctl, _("Malformed size %s"), allocationStr);
|
||||||
|
|
||||||
if (vshCommandOptString(cmd, "format", &format) < 0 ||
|
if (vshCommandOptString(cmd, "format", &format) < 0 ||
|
||||||
@ -11908,7 +11897,7 @@ static const vshCmdInfo info_vol_resize[] = {
|
|||||||
static const vshCmdOptDef opts_vol_resize[] = {
|
static const vshCmdOptDef opts_vol_resize[] = {
|
||||||
{"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
|
{"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
|
||||||
{"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
|
{"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
|
||||||
N_("new capacity for the vol with optional k,M,G,T suffix")},
|
N_("new capacity for the vol, as scaled integer (default bytes)")},
|
||||||
{"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
|
{"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
|
||||||
{"allocate", VSH_OT_BOOL, 0,
|
{"allocate", VSH_OT_BOOL, 0,
|
||||||
N_("allocate the new capacity, rather than leaving it sparse")},
|
N_("allocate the new capacity, rather than leaving it sparse")},
|
||||||
@ -11945,18 +11934,22 @@ cmdVolResize(vshControl *ctl, const vshCmd *cmd)
|
|||||||
|
|
||||||
if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
|
if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
if (delta && *capacityStr == '-') {
|
virSkipSpaces(&capacityStr);
|
||||||
if (cmdVolSize(capacityStr + 1, &capacity) < 0) {
|
if (*capacityStr == '-') {
|
||||||
vshError(ctl, _("Malformed size %s"), capacityStr);
|
/* The API always requires a positive value; but we allow a
|
||||||
goto cleanup;
|
* negative value for convenience. */
|
||||||
}
|
if (delta && vshCommandOptBool(cmd, "shrink")){
|
||||||
capacity = -capacity;
|
capacityStr++;
|
||||||
} else {
|
} else {
|
||||||
if (cmdVolSize(capacityStr, &capacity) < 0) {
|
vshError(ctl, "%s",
|
||||||
vshError(ctl, _("Malformed size %s"), capacityStr);
|
_("negative size requires --delta and --shrink"));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (vshVolSize(capacityStr, &capacity) < 0) {
|
||||||
|
vshError(ctl, _("Malformed size %s"), capacityStr);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
if (virStorageVolResize(vol, capacity, flags) == 0) {
|
if (virStorageVolResize(vol, capacity, flags) == 0) {
|
||||||
vshPrint(ctl,
|
vshPrint(ctl,
|
||||||
@ -18005,6 +17998,36 @@ vshCommandOptULongLong(const vshCmd *cmd, const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vshCommandOptScaledInt:
|
||||||
|
* @cmd command reference
|
||||||
|
* @name option name
|
||||||
|
* @value result
|
||||||
|
* @scale default of 1 or 1024, if no suffix is present
|
||||||
|
* @max maximum value permitted
|
||||||
|
*
|
||||||
|
* Returns option as long long, scaled according to suffix
|
||||||
|
* See vshCommandOptInt()
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vshCommandOptScaledInt(const vshCmd *cmd, const char *name,
|
||||||
|
unsigned long long *value, int scale,
|
||||||
|
unsigned long long max)
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
int ret;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
ret = vshCommandOptString(cmd, name, &str);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
if (virStrToLong_ull(str, &end, 10, value) < 0 ||
|
||||||
|
virScaleInteger(value, end, scale, max) < 0)
|
||||||
|
return -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vshCommandOptBool:
|
* vshCommandOptBool:
|
||||||
* @cmd command reference
|
* @cmd command reference
|
||||||
|
@ -121,6 +121,25 @@ version of B<virsh> supported an alternate spelling of a command or
|
|||||||
option (such as I<--tunnelled> instead of I<--tunneled>), then
|
option (such as I<--tunnelled> instead of I<--tunneled>), then
|
||||||
scripts using that older spelling will continue to work.
|
scripts using that older spelling will continue to work.
|
||||||
|
|
||||||
|
Several B<virsh> commands take an optionally scaled integer; if no
|
||||||
|
scale is provided, then the default is listed in the command (for
|
||||||
|
historical reasons, some commands default to bytes, while other
|
||||||
|
commands default to kibibytes). The following case-insensitive
|
||||||
|
suffixes can be used to select a specfic scale:
|
||||||
|
b, byte byte 1
|
||||||
|
KB kilobyte 1,000
|
||||||
|
k, KiB kibibyte 1,024
|
||||||
|
MB megabyte 1,000,000
|
||||||
|
M, MiB mebibyte 1,048,576
|
||||||
|
GB gigabyte 1,000,000,000
|
||||||
|
G, GiB gibibyte 1,073,741,824
|
||||||
|
TB terabyte 1,000,000,000,000
|
||||||
|
T, TiB tebibyte 1,099,511,627,776
|
||||||
|
PB petabyte 1,000,000,000,000,000
|
||||||
|
P, PiB pebibyte 1,125,899,906,842,624
|
||||||
|
EB exabyte 1,000,000,000,000,000,000
|
||||||
|
E, EiB exbibyte 1,152,921,504,606,846,976
|
||||||
|
|
||||||
=head1 GENERIC COMMANDS
|
=head1 GENERIC COMMANDS
|
||||||
|
|
||||||
The following commands are generic i.e. not specific to a domain.
|
The following commands are generic i.e. not specific to a domain.
|
||||||
@ -663,11 +682,14 @@ If I<--info> is specified, the active job information on the specified
|
|||||||
disk will be printed.
|
disk will be printed.
|
||||||
I<bandwidth> can be used to set bandwidth limit for the active job.
|
I<bandwidth> can be used to set bandwidth limit for the active job.
|
||||||
|
|
||||||
=item B<blockresize> I<domain> I<--path> I<--size>
|
=item B<blockresize> I<domain> I<path> I<size>
|
||||||
|
|
||||||
Resize a block device of domain while the domain is running, I<--path>
|
Resize a block device of domain while the domain is running, I<path>
|
||||||
specifies the absolute path of the block device, I<--size> specifies the
|
specifies the absolute path of the block device, I<size> is a scaled
|
||||||
new size in kilobytes
|
integer (see B<NOTES> above) which defaults to KiB (blocks of 1024 bytes)
|
||||||
|
if there is no suffix. You must use a suffix of "B" to get bytes (note
|
||||||
|
that for historical reasons, this differs from B<vol-resize> which
|
||||||
|
defaults to bytes without a suffix).
|
||||||
|
|
||||||
=item B<dominfo> I<domain-id>
|
=item B<dominfo> I<domain-id>
|
||||||
|
|
||||||
@ -2024,10 +2046,10 @@ Create a volume from a set of arguments.
|
|||||||
I<pool-or-uuid> is the name or UUID of the storage pool to create the volume
|
I<pool-or-uuid> is the name or UUID of the storage pool to create the volume
|
||||||
in.
|
in.
|
||||||
I<name> is the name of the new volume.
|
I<name> is the name of the new volume.
|
||||||
I<capacity> is the size of the volume to be created, with optional k, M, G, or
|
I<capacity> is the size of the volume to be created, as a scaled integer
|
||||||
T suffix.
|
(see B<NOTES> above), defaulting to bytes if there is no suffix.
|
||||||
I<--allocation> I<size> is the initial size to be allocated in the volume, with
|
I<--allocation> I<size> is the initial size to be allocated in the volume,
|
||||||
optional k, M, G, or T suffix.
|
also as a scaled integer defaulting to bytes.
|
||||||
I<--format> I<string> is used in file based storage pools to specify the volume
|
I<--format> I<string> is used in file based storage pools to specify the volume
|
||||||
file format to use; raw, bochs, qcow, qcow2, vmdk.
|
file format to use; raw, bochs, qcow, qcow2, vmdk.
|
||||||
I<--backing-vol> I<vol-name-or-key-or-path> is the source backing
|
I<--backing-vol> I<vol-name-or-key-or-path> is the source backing
|
||||||
@ -2163,7 +2185,12 @@ is in. I<vol-name-or-key-or-path> is the name or key or path of the volume
|
|||||||
to resize. The new capacity might be sparse unless I<--allocate> is
|
to resize. The new capacity might be sparse unless I<--allocate> is
|
||||||
specified. Normally, I<capacity> is the new size, but if I<--delta>
|
specified. Normally, I<capacity> is the new size, but if I<--delta>
|
||||||
is present, then it is added to the existing size. Attempts to shrink
|
is present, then it is added to the existing size. Attempts to shrink
|
||||||
the volume will fail unless I<--shrink> is present.
|
the volume will fail unless I<--shrink> is present; I<capacity> cannot
|
||||||
|
be negative unless I<--shrink> is provided, but a negative sign is not
|
||||||
|
necessary. I<capacity> is a scaled integer (see B<NOTES> above), which
|
||||||
|
defaults to bytes if there is no suffix. This command is only safe
|
||||||
|
for storage volumes not in use by an active guest; see also
|
||||||
|
B<blockresize> for live resizing.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user