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:
Daniel P. Berrange 2014-03-07 11:39:48 +00:00
parent df3a681c03
commit 7b3f1f8c30
5 changed files with 59 additions and 18 deletions

View File

@ -135,6 +135,9 @@ struct _virCommand {
/* See virCommandSetDryRun for description for this variable */
static virBufferPtr dryRunBuffer;
static virCommandDryRunCallback dryRunCallback;
static void *dryRunOpaque;
static int dryRunStatus;
/*
* virCommandFDIsSet:
@ -1860,6 +1863,11 @@ virCommandProcessIO(virCommandPtr cmd)
size_t inoff = 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
* via pipe */
if (cmd->inbuf)
@ -2267,16 +2275,25 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
}
str = virCommandToString(cmd);
if (dryRunBuffer) {
if (dryRunBuffer || dryRunCallback) {
dryRunStatus = 0;
if (!str) {
/* error already reported by virCommandToString */
goto cleanup;
}
VIR_DEBUG("Dry run requested, appending stringified "
"command to dryRunBuffer=%p", dryRunBuffer);
virBufferAdd(dryRunBuffer, str, -1);
virBufferAddChar(dryRunBuffer, '\n');
if (dryRunBuffer) {
VIR_DEBUG("Dry run requested, appending stringified "
"command to dryRunBuffer=%p", dryRunBuffer);
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;
goto cleanup;
}
@ -2356,10 +2373,11 @@ virCommandWait(virCommandPtr cmd, int *exitstatus)
return -1;
}
if (dryRunBuffer) {
VIR_DEBUG("Dry run requested, claiming success");
if (dryRunBuffer || dryRunCallback) {
VIR_DEBUG("Dry run requested, returning status %d",
dryRunStatus);
if (exitstatus)
*exitstatus = 0;
*exitstatus = dryRunStatus;
return 0;
}
@ -2704,6 +2722,7 @@ virCommandDoAsyncIO(virCommandPtr cmd)
/**
* virCommandSetDryRun:
* @buf: buffer to store stringified commands
* @callback: callback to process input/output/args
*
* Sometimes it's desired to not actually run given command, but
* see its string representation without having to change the
@ -2712,8 +2731,13 @@ virCommandDoAsyncIO(virCommandPtr cmd)
* virCommandRun* API. The virCommandSetDryRun allows you to
* modify this behavior: once called, every call to
* virCommandRun* results in command string representation being
* appended to @buf instead of being executed. the strings are
* escaped for a shell and separated by a newline. For example:
* appended to @buf instead of being executed. If @callback is
* 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;
* virCommandSetDryRun(&buffer);
@ -2725,10 +2749,14 @@ virCommandDoAsyncIO(virCommandPtr cmd)
*
* /bin/echo 'Hello world'\n
*
* To cancel this effect pass NULL.
* To cancel this effect pass NULL for @buf and @callback.
*/
void
virCommandSetDryRun(virBufferPtr buf)
virCommandSetDryRun(virBufferPtr buf,
virCommandDryRunCallback cb,
void *opaque)
{
dryRunBuffer = buf;
dryRunCallback = cb;
dryRunOpaque = opaque;
}

View File

@ -186,4 +186,5 @@ void virCommandAbort(virCommandPtr cmd);
void virCommandFree(virCommandPtr cmd);
void virCommandDoAsyncIO(virCommandPtr cmd);
#endif /* __VIR_COMMAND_H__ */

View File

@ -28,5 +28,16 @@
# 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__ */

View File

@ -96,7 +96,7 @@ testKModLoad(const void *args)
bool useBlacklist = info->useBlacklist;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCommandSetDryRun(&buf);
virCommandSetDryRun(&buf, NULL, NULL);
errbuf = virKModLoad(module, useBlacklist);
if (errbuf) {
@ -110,7 +110,7 @@ testKModLoad(const void *args)
ret = 0;
cleanup:
virCommandSetDryRun(NULL);
virCommandSetDryRun(NULL, NULL, NULL);
VIR_FREE(errbuf);
return ret;
}
@ -125,7 +125,7 @@ testKModUnload(const void *args)
const char *module = info->module;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCommandSetDryRun(&buf);
virCommandSetDryRun(&buf, NULL, NULL);
errbuf = virKModUnload(module);
if (errbuf) {
@ -139,7 +139,7 @@ testKModUnload(const void *args)
ret = 0;
cleanup:
virCommandSetDryRun(NULL);
virCommandSetDryRun(NULL, NULL, NULL);
VIR_FREE(errbuf);
return ret;
}

View File

@ -77,7 +77,7 @@ testVirNetDevBandwidthSet(const void *data)
if (!iface)
iface = "eth0";
virCommandSetDryRun(&buf);
virCommandSetDryRun(&buf, NULL, NULL);
if (virNetDevBandwidthSet(iface, band, info->hierarchical_class) < 0)
goto cleanup;
@ -101,6 +101,7 @@ testVirNetDevBandwidthSet(const void *data)
ret = 0;
cleanup:
virCommandSetDryRun(NULL, NULL, NULL);
virNetDevBandwidthFree(band);
virBufferFreeAndReset(&buf);
VIR_FREE(actual_cmd);