qemu: allow filtering events by regex

When listening for a subset of monitor events, it can be tedious
to register for each event name in series; nicer is to register
for multiple events in one go.  Implement a flag to use regex
interpretation of the event filter.

While at it, prove how much I hate the shift key, by adding a
way to filter for 'shutdown' instead of 'SHUTDOWN'. :)

* include/libvirt/libvirt-qemu.h
(virConnectDomainQemuMonitorEventRegisterFlags): New enum.
* src/libvirt-qemu.c (virConnectDomainQemuMonitorEventRegister):
Document flags.
* tools/virsh-domain.c (cmdQemuMonitorEvent): Expose them.
* tools/virsh.pod (qemu-monitor-event): Document this.
* src/conf/domain_event.c
(virDomainQemuMonitorEventStateRegisterID): Add flags.
(virDomainQemuMonitorEventFilter): Handle regex, and optimize
client side.
(virDomainQemuMonitorEventCleanup): Clean up regex.

Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Eric Blake 2014-02-06 14:46:52 -07:00
parent d0ba8dd764
commit 43b17dd444
5 changed files with 74 additions and 10 deletions

View File

@ -76,6 +76,16 @@ typedef void (*virConnectDomainQemuMonitorEventCallback)(virConnectPtr conn,
const char *details,
void *opaque);
typedef enum {
/* Event filter is a regex rather than a literal string */
VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX = (1 << 0),
/* Event filter is case insensitive */
VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE = (1 << 1),
} virConnectDomainQemuMonitorEventRegisterFlags;
int virConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
virDomainPtr dom,
const char *event,

View File

@ -24,6 +24,8 @@
#include <config.h>
#include <regex.h>
#include "domain_event.h"
#include "object_event.h"
#include "object_event_private.h"
@ -1390,6 +1392,8 @@ error:
* deregisters. */
struct virDomainQemuMonitorEventData {
char *event;
regex_t regex;
unsigned int flags;
void *opaque;
virFreeCallback freecb;
};
@ -1609,6 +1613,12 @@ virDomainQemuMonitorEventFilter(virConnectPtr conn ATTRIBUTE_UNUSED,
monitorEvent = (virDomainQemuMonitorEventPtr) event;
if (data->flags == -1)
return true;
if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX)
return regexec(&data->regex, monitorEvent->event, 0, NULL, 0) == 0;
if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE)
return STRCASEEQ(monitorEvent->event, data->event);
return STREQ(monitorEvent->event, data->event);
}
@ -1619,6 +1629,8 @@ virDomainQemuMonitorEventCleanup(void *opaque)
virDomainQemuMonitorEventData *data = opaque;
VIR_FREE(data->event);
if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX)
regfree(&data->regex);
if (data->freecb)
(data->freecb)(data->opaque);
VIR_FREE(data);
@ -1634,7 +1646,8 @@ virDomainQemuMonitorEventCleanup(void *opaque)
* @cb: function to invoke when event occurs
* @opaque: data blob to pass to callback
* @freecb: callback to free @opaque
* @flags: -1 for client, or set of registration flags on server
* @flags: -1 for client, valid virConnectDomainQemuMonitorEventRegisterFlags
* for server
* @callbackID: filled with callback ID
*
* Register the function @cb with connection @conn, from @state, for
@ -1660,12 +1673,34 @@ virDomainQemuMonitorEventStateRegisterID(virConnectPtr conn,
return -1;
if (flags != -1)
virCheckFlags(0, -1);
virCheckFlags(VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX |
VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE,
-1);
if (VIR_ALLOC(data) < 0)
return -1;
if (VIR_STRDUP(data->event, event) < 0) {
VIR_FREE(data);
return -1;
data->flags = flags;
if (flags != -1) {
int rflags = REG_NOSUB;
if (flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE)
rflags |= REG_ICASE;
if (flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX) {
int err = regcomp(&data->regex, event, rflags);
if (err) {
char error[100];
regerror(err, &data->regex, error, sizeof(error));
virReportError(VIR_ERR_INVALID_ARG,
_("failed to compile regex '%s': %s"),
event, error);
regfree(&data->regex);
VIR_FREE(data);
return -1;
}
} else if (VIR_STRDUP(data->event, event) < 0) {
VIR_FREE(data);
return -1;
}
}
data->opaque = opaque;
data->freecb = freecb;

View File

@ -227,7 +227,7 @@ error:
* @cb: callback to the function handling monitor events
* @opaque: opaque data to pass on to the callback
* @freecb: optional function to deallocate opaque when not used anymore
* @flags: extra flags; not used yet, so callers should always pass 0
* @flags: bitwise-OR of virConnectDomainQemuMonitorEventRegisterFlags
*
* This API is QEMU specific, so it will only work with hypervisor
* connections to the QEMU driver.
@ -242,9 +242,12 @@ error:
* is non-NULL, then only the specific domain will be monitored.
*
* If @event is NULL, then all monitor events will be reported. If @event is
* non-NULL, then only the specific monitor event will be reported. @flags
* is currently unused, but in the future may support a flag for passing
* @event as a glob instead of a literal name to match a category of events.
* non-NULL, then only specific monitor events will be reported. @flags
* controls how the filtering is performed: 0 requests an exact match, while
* VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX states that @event
* is a basic regular expression. Additionally, including
* VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE lets @event match
* case-insensitively.
*
* The virDomainPtr object handle passed into the callback upon delivery
* of an event is only valid for the duration of execution of the callback.

View File

@ -7983,6 +7983,14 @@ static const vshCmdOptDef opts_qemu_monitor_event[] = {
.type = VSH_OT_INT,
.help = N_("timeout seconds")
},
{.name = "regex",
.type = VSH_OT_BOOL,
.help = N_("treat event as a regex rather than literal filter")
},
{.name = "no-case",
.type = VSH_OT_BOOL,
.help = N_("treat event case-insensitively")
},
{.name = NULL}
};
@ -7997,6 +8005,11 @@ cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd)
const char *event = NULL;
vshQemuEventData data;
if (vshCommandOptBool(cmd, "regex"))
flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX;
if (vshCommandOptBool(cmd, "no-case"))
flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE;
data.ctl = ctl;
data.loop = vshCommandOptBool(cmd, "loop");
data.pretty = vshCommandOptBool(cmd, "pretty");

View File

@ -3385,12 +3385,15 @@ failed. And when I<--block> is given, the command waits forever with blocking
timeout.
=item B<qemu-monitor-event> [I<domain>] [I<--event> I<event-name>] [I<--loop>]
[I<--timeout> I<seconds>] [I<--pretty>]
[I<--timeout> I<seconds>] [I<--pretty>] [I<--regex>] [I<--no-case>]
Wait for arbitrary QEMU monitor events to occur, and print out the
details of events as they happen. The events can optionally be filtered
by I<domain> or I<event-name>. The 'query-events' QMP command can be
used via I<qemu-monitor-command> to learn what events are supported.
If I<--regex> is used, I<event-name> is a basic regular expression
instead of a literal string. If I<--no-case> is used, I<event-name>
will match case-insensitively.
By default, this command is one-shot, and returns success once an event
occurs; you can send SIGINT (usually via C<Ctrl-C>) to quit immediately.