mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 03:12:22 +00:00
Add ability to register callback for virCommand dry run
To allow for fault injection of the virCommand dry run, add the ability to register a callback. The callback will be passed the argv, env and stdin buffer and is expected to return the exit status and optionally fill stdout and stderr buffers. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
df3a681c03
commit
7b3f1f8c30
@ -135,6 +135,9 @@ struct _virCommand {
|
|||||||
|
|
||||||
/* See virCommandSetDryRun for description for this variable */
|
/* See virCommandSetDryRun for description for this variable */
|
||||||
static virBufferPtr dryRunBuffer;
|
static virBufferPtr dryRunBuffer;
|
||||||
|
static virCommandDryRunCallback dryRunCallback;
|
||||||
|
static void *dryRunOpaque;
|
||||||
|
static int dryRunStatus;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* virCommandFDIsSet:
|
* virCommandFDIsSet:
|
||||||
@ -1860,6 +1863,11 @@ virCommandProcessIO(virCommandPtr cmd)
|
|||||||
size_t inoff = 0;
|
size_t inoff = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dryRunBuffer || dryRunCallback) {
|
||||||
|
VIR_DEBUG("Dry run requested, skipping I/O processing");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* With an input buffer, feed data to child
|
/* With an input buffer, feed data to child
|
||||||
* via pipe */
|
* via pipe */
|
||||||
if (cmd->inbuf)
|
if (cmd->inbuf)
|
||||||
@ -2267,16 +2275,25 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
str = virCommandToString(cmd);
|
str = virCommandToString(cmd);
|
||||||
if (dryRunBuffer) {
|
if (dryRunBuffer || dryRunCallback) {
|
||||||
|
dryRunStatus = 0;
|
||||||
if (!str) {
|
if (!str) {
|
||||||
/* error already reported by virCommandToString */
|
/* error already reported by virCommandToString */
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
VIR_DEBUG("Dry run requested, appending stringified "
|
if (dryRunBuffer) {
|
||||||
"command to dryRunBuffer=%p", dryRunBuffer);
|
VIR_DEBUG("Dry run requested, appending stringified "
|
||||||
virBufferAdd(dryRunBuffer, str, -1);
|
"command to dryRunBuffer=%p", dryRunBuffer);
|
||||||
virBufferAddChar(dryRunBuffer, '\n');
|
virBufferAdd(dryRunBuffer, str, -1);
|
||||||
|
virBufferAddChar(dryRunBuffer, '\n');
|
||||||
|
}
|
||||||
|
if (dryRunCallback) {
|
||||||
|
dryRunCallback((const char *const*)cmd->args,
|
||||||
|
(const char *const*)cmd->env,
|
||||||
|
cmd->inbuf, cmd->outbuf, cmd->errbuf,
|
||||||
|
&dryRunStatus, dryRunOpaque);
|
||||||
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -2356,10 +2373,11 @@ virCommandWait(virCommandPtr cmd, int *exitstatus)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dryRunBuffer) {
|
if (dryRunBuffer || dryRunCallback) {
|
||||||
VIR_DEBUG("Dry run requested, claiming success");
|
VIR_DEBUG("Dry run requested, returning status %d",
|
||||||
|
dryRunStatus);
|
||||||
if (exitstatus)
|
if (exitstatus)
|
||||||
*exitstatus = 0;
|
*exitstatus = dryRunStatus;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2704,6 +2722,7 @@ virCommandDoAsyncIO(virCommandPtr cmd)
|
|||||||
/**
|
/**
|
||||||
* virCommandSetDryRun:
|
* virCommandSetDryRun:
|
||||||
* @buf: buffer to store stringified commands
|
* @buf: buffer to store stringified commands
|
||||||
|
* @callback: callback to process input/output/args
|
||||||
*
|
*
|
||||||
* Sometimes it's desired to not actually run given command, but
|
* Sometimes it's desired to not actually run given command, but
|
||||||
* see its string representation without having to change the
|
* see its string representation without having to change the
|
||||||
@ -2712,8 +2731,13 @@ virCommandDoAsyncIO(virCommandPtr cmd)
|
|||||||
* virCommandRun* API. The virCommandSetDryRun allows you to
|
* virCommandRun* API. The virCommandSetDryRun allows you to
|
||||||
* modify this behavior: once called, every call to
|
* modify this behavior: once called, every call to
|
||||||
* virCommandRun* results in command string representation being
|
* virCommandRun* results in command string representation being
|
||||||
* appended to @buf instead of being executed. the strings are
|
* appended to @buf instead of being executed. If @callback is
|
||||||
* escaped for a shell and separated by a newline. For example:
|
* provided, then it is invoked with the argv, env and stdin
|
||||||
|
* data string for the command. It is expected to fill the stdout
|
||||||
|
* and stderr data strings and exit status variables.
|
||||||
|
*
|
||||||
|
* The strings stored in @buf are escaped for a shell and
|
||||||
|
* separated by a newline. For example:
|
||||||
*
|
*
|
||||||
* virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
* virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
||||||
* virCommandSetDryRun(&buffer);
|
* virCommandSetDryRun(&buffer);
|
||||||
@ -2725,10 +2749,14 @@ virCommandDoAsyncIO(virCommandPtr cmd)
|
|||||||
*
|
*
|
||||||
* /bin/echo 'Hello world'\n
|
* /bin/echo 'Hello world'\n
|
||||||
*
|
*
|
||||||
* To cancel this effect pass NULL.
|
* To cancel this effect pass NULL for @buf and @callback.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
virCommandSetDryRun(virBufferPtr buf)
|
virCommandSetDryRun(virBufferPtr buf,
|
||||||
|
virCommandDryRunCallback cb,
|
||||||
|
void *opaque)
|
||||||
{
|
{
|
||||||
dryRunBuffer = buf;
|
dryRunBuffer = buf;
|
||||||
|
dryRunCallback = cb;
|
||||||
|
dryRunOpaque = opaque;
|
||||||
}
|
}
|
||||||
|
@ -186,4 +186,5 @@ void virCommandAbort(virCommandPtr cmd);
|
|||||||
void virCommandFree(virCommandPtr cmd);
|
void virCommandFree(virCommandPtr cmd);
|
||||||
|
|
||||||
void virCommandDoAsyncIO(virCommandPtr cmd);
|
void virCommandDoAsyncIO(virCommandPtr cmd);
|
||||||
|
|
||||||
#endif /* __VIR_COMMAND_H__ */
|
#endif /* __VIR_COMMAND_H__ */
|
||||||
|
@ -28,5 +28,16 @@
|
|||||||
|
|
||||||
# include "vircommand.h"
|
# include "vircommand.h"
|
||||||
|
|
||||||
void virCommandSetDryRun(virBufferPtr buf);
|
typedef void (*virCommandDryRunCallback)(const char *const*args,
|
||||||
|
const char *const*env,
|
||||||
|
const char *input,
|
||||||
|
char **output,
|
||||||
|
char **error,
|
||||||
|
int *status,
|
||||||
|
void *opaque);
|
||||||
|
|
||||||
|
void virCommandSetDryRun(virBufferPtr buf,
|
||||||
|
virCommandDryRunCallback cb,
|
||||||
|
void *opaque);
|
||||||
|
|
||||||
#endif /* __VIR_COMMAND_PRIV_H__ */
|
#endif /* __VIR_COMMAND_PRIV_H__ */
|
||||||
|
@ -96,7 +96,7 @@ testKModLoad(const void *args)
|
|||||||
bool useBlacklist = info->useBlacklist;
|
bool useBlacklist = info->useBlacklist;
|
||||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
|
||||||
virCommandSetDryRun(&buf);
|
virCommandSetDryRun(&buf, NULL, NULL);
|
||||||
|
|
||||||
errbuf = virKModLoad(module, useBlacklist);
|
errbuf = virKModLoad(module, useBlacklist);
|
||||||
if (errbuf) {
|
if (errbuf) {
|
||||||
@ -110,7 +110,7 @@ testKModLoad(const void *args)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
virCommandSetDryRun(NULL);
|
virCommandSetDryRun(NULL, NULL, NULL);
|
||||||
VIR_FREE(errbuf);
|
VIR_FREE(errbuf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ testKModUnload(const void *args)
|
|||||||
const char *module = info->module;
|
const char *module = info->module;
|
||||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
|
||||||
virCommandSetDryRun(&buf);
|
virCommandSetDryRun(&buf, NULL, NULL);
|
||||||
|
|
||||||
errbuf = virKModUnload(module);
|
errbuf = virKModUnload(module);
|
||||||
if (errbuf) {
|
if (errbuf) {
|
||||||
@ -139,7 +139,7 @@ testKModUnload(const void *args)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
virCommandSetDryRun(NULL);
|
virCommandSetDryRun(NULL, NULL, NULL);
|
||||||
VIR_FREE(errbuf);
|
VIR_FREE(errbuf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ testVirNetDevBandwidthSet(const void *data)
|
|||||||
if (!iface)
|
if (!iface)
|
||||||
iface = "eth0";
|
iface = "eth0";
|
||||||
|
|
||||||
virCommandSetDryRun(&buf);
|
virCommandSetDryRun(&buf, NULL, NULL);
|
||||||
|
|
||||||
if (virNetDevBandwidthSet(iface, band, info->hierarchical_class) < 0)
|
if (virNetDevBandwidthSet(iface, band, info->hierarchical_class) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -101,6 +101,7 @@ testVirNetDevBandwidthSet(const void *data)
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
cleanup:
|
cleanup:
|
||||||
|
virCommandSetDryRun(NULL, NULL, NULL);
|
||||||
virNetDevBandwidthFree(band);
|
virNetDevBandwidthFree(band);
|
||||||
virBufferFreeAndReset(&buf);
|
virBufferFreeAndReset(&buf);
|
||||||
VIR_FREE(actual_cmd);
|
VIR_FREE(actual_cmd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user