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, const char *details,
void *opaque); 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, int virConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
virDomainPtr dom, virDomainPtr dom,
const char *event, const char *event,

View File

@ -24,6 +24,8 @@
#include <config.h> #include <config.h>
#include <regex.h>
#include "domain_event.h" #include "domain_event.h"
#include "object_event.h" #include "object_event.h"
#include "object_event_private.h" #include "object_event_private.h"
@ -1390,6 +1392,8 @@ error:
* deregisters. */ * deregisters. */
struct virDomainQemuMonitorEventData { struct virDomainQemuMonitorEventData {
char *event; char *event;
regex_t regex;
unsigned int flags;
void *opaque; void *opaque;
virFreeCallback freecb; virFreeCallback freecb;
}; };
@ -1609,6 +1613,12 @@ virDomainQemuMonitorEventFilter(virConnectPtr conn ATTRIBUTE_UNUSED,
monitorEvent = (virDomainQemuMonitorEventPtr) event; 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); return STREQ(monitorEvent->event, data->event);
} }
@ -1619,6 +1629,8 @@ virDomainQemuMonitorEventCleanup(void *opaque)
virDomainQemuMonitorEventData *data = opaque; virDomainQemuMonitorEventData *data = opaque;
VIR_FREE(data->event); VIR_FREE(data->event);
if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX)
regfree(&data->regex);
if (data->freecb) if (data->freecb)
(data->freecb)(data->opaque); (data->freecb)(data->opaque);
VIR_FREE(data); VIR_FREE(data);
@ -1634,7 +1646,8 @@ virDomainQemuMonitorEventCleanup(void *opaque)
* @cb: function to invoke when event occurs * @cb: function to invoke when event occurs
* @opaque: data blob to pass to callback * @opaque: data blob to pass to callback
* @freecb: callback to free @opaque * @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 * @callbackID: filled with callback ID
* *
* Register the function @cb with connection @conn, from @state, for * Register the function @cb with connection @conn, from @state, for
@ -1660,12 +1673,34 @@ virDomainQemuMonitorEventStateRegisterID(virConnectPtr conn,
return -1; return -1;
if (flags != -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) if (VIR_ALLOC(data) < 0)
return -1; return -1;
if (VIR_STRDUP(data->event, event) < 0) { data->flags = flags;
VIR_FREE(data); if (flags != -1) {
return -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->opaque = opaque;
data->freecb = freecb; data->freecb = freecb;

View File

@ -227,7 +227,7 @@ error:
* @cb: callback to the function handling monitor events * @cb: callback to the function handling monitor events
* @opaque: opaque data to pass on to the callback * @opaque: opaque data to pass on to the callback
* @freecb: optional function to deallocate opaque when not used anymore * @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 * This API is QEMU specific, so it will only work with hypervisor
* connections to the QEMU driver. * connections to the QEMU driver.
@ -242,9 +242,12 @@ error:
* is non-NULL, then only the specific domain will be monitored. * 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 * 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 * non-NULL, then only specific monitor events will be reported. @flags
* is currently unused, but in the future may support a flag for passing * controls how the filtering is performed: 0 requests an exact match, while
* @event as a glob instead of a literal name to match a category of events. * 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 * The virDomainPtr object handle passed into the callback upon delivery
* of an event is only valid for the duration of execution of the callback. * 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, .type = VSH_OT_INT,
.help = N_("timeout seconds") .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} {.name = NULL}
}; };
@ -7997,6 +8005,11 @@ cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd)
const char *event = NULL; const char *event = NULL;
vshQemuEventData data; 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.ctl = ctl;
data.loop = vshCommandOptBool(cmd, "loop"); data.loop = vshCommandOptBool(cmd, "loop");
data.pretty = vshCommandOptBool(cmd, "pretty"); 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. timeout.
=item B<qemu-monitor-event> [I<domain>] [I<--event> I<event-name>] [I<--loop>] =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 Wait for arbitrary QEMU monitor events to occur, and print out the
details of events as they happen. The events can optionally be filtered 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 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. 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 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. occurs; you can send SIGINT (usually via C<Ctrl-C>) to quit immediately.