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:
parent
d0ba8dd764
commit
43b17dd444
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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");
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user