Add nodedev-event support for node device lifecycle events
---
tools/virsh-nodedev.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh-nodedev.h | 10 +++
tools/virsh.pod | 18 +++++
3 files changed, 239 insertions(+)
diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c
index a715b61..a48f0bd 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -31,6 +31,7 @@
#include "viralloc.h"
#include "virfile.h"
#include "virstring.h"
+#include "virtime.h"
#include "conf/node_device_conf.h"
/*
@@ -739,6 +740,210 @@ cmdNodeDeviceReset(vshControl *ctl, const vshCmd *cmd)
return ret;
}
+/*
+ * "nodedev-event" command
+ */
+VIR_ENUM_DECL(virshNodeDeviceEvent)
+VIR_ENUM_IMPL(virshNodeDeviceEvent,
+ VIR_NODE_DEVICE_EVENT_LAST,
+ N_("Created"),
+ N_("Deleted"))
+
+static const char *
+virshNodeDeviceEventToString(int event)
+{
+ const char *str = virshNodeDeviceEventTypeToString(event);
+ return str ? _(str) : _("unknown");
+}
+
+struct virshNodeDeviceEventData {
+ vshControl *ctl;
+ bool loop;
+ bool timestamp;
+ int count;
+};
+typedef struct virshNodeDeviceEventData virshNodeDeviceEventData;
+
+VIR_ENUM_DECL(virshNodeDeviceEventId)
+VIR_ENUM_IMPL(virshNodeDeviceEventId,
+ VIR_NODE_DEVICE_EVENT_ID_LAST,
+ "lifecycle")
+
+static void
+vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virNodeDevicePtr dev,
+ int event,
+ int detail ATTRIBUTE_UNUSED,
+ void *opaque)
+{
+ virshNodeDeviceEventData *data = opaque;
+
+ if (!data->loop && data->count)
+ return;
+
+ if (data->timestamp) {
+ char timestamp[VIR_TIME_STRING_BUFLEN];
+
+ if (virTimeStringNowRaw(timestamp) < 0)
+ timestamp[0] = '\0';
+
+ vshPrint(data->ctl, _("%s: event 'lifecycle' for node device %s:
%s\n"),
+ timestamp,
+ virNodeDeviceGetName(dev), virshNodeDeviceEventToString(event));
+ } else {
+ vshPrint(data->ctl, _("event 'lifecycle' for node device %s:
%s\n"),
+ virNodeDeviceGetName(dev), virshNodeDeviceEventToString(event));
+ }
+
+ data->count++;
+ if (!data->loop)
+ vshEventDone(data->ctl);
+}
+
+static const vshCmdInfo info_node_device_event[] = {
+ {.name = "help",
+ .data = N_("Node Device Events")
+ },
+ {.name = "desc",
+ .data = N_("List event types, or wait for node device events to occur")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_node_device_event[] = {
+ {.name = "node device",
+ .type = VSH_OT_STRING,
+ .help = N_("filter by node device name")
+ },
+ {.name = "event",
+ .type = VSH_OT_STRING,
+ .help = N_("which event type to wait for")
+ },
+ {.name = "loop",
+ .type = VSH_OT_BOOL,
+ .help = N_("loop until timeout or interrupt, rather than one-shot")
+ },
+ {.name = "timeout",
+ .type = VSH_OT_INT,
+ .help = N_("timeout seconds")
+ },
+ {.name = "list",
+ .type = VSH_OT_BOOL,
+ .help = N_("list valid event types")
+ },
+ {.name = "timestamp",
+ .type = VSH_OT_BOOL,
+ .help = N_("show timestamp for each printed event")
+ },
+ {.name = NULL}
+};
+
+virNodeDevicePtr
+virshCommandOptNodeDeviceBy(vshControl *ctl, const vshCmd *cmd,
+ const char **name, unsigned int flags)
+{
+ virNodeDevicePtr dev = NULL;
+ const char *n = NULL;
+ const char *optname = "node device";
+ virCheckFlags(VIRSH_BYNAME, NULL);
+ virshControlPtr priv = ctl->privData;
+
+ if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
+ return NULL;
+
+ vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
+ cmd->def->name, optname, n);
+
+ if (name)
+ *name = n;
+
+ /* try it by NAME */
+ if (!dev && (flags & VIRSH_BYNAME)) {
+ vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as node device
NAME\n",
+ cmd->def->name, optname);
+ dev = virNodeDeviceLookupByName(priv->conn, n);
+ }
+
+ if (!dev)
+ vshError(ctl, _("failed to get node device '%s'"), n);
+
+ return dev;
+}
+
+static bool
+cmdNodeDeviceEvent(vshControl *ctl, const vshCmd *cmd)
+{
+ virNodeDevicePtr dev = NULL;
+ bool ret = false;
+ int eventId = -1;
+ int timeout = 0;
+ virshNodeDeviceEventData data;
+ const char *eventName = NULL;
+ int event;
+ virshControlPtr priv = ctl->privData;
+
+ if (vshCommandOptBool(cmd, "list")) {
+ size_t i;
+
+ for (i = 0; i < VIR_NODE_DEVICE_EVENT_ID_LAST; i++)
+ vshPrint(ctl, "%s\n", virshNodeDeviceEventIdTypeToString(i));
+ return true;
+ }
+
+ if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
+ return false;
+ if (!eventName) {
+ vshError(ctl, "%s", _("either --list or event type is
required"));
+ return false;
+ }
+ if ((event = virshNodeDeviceEventIdTypeFromString(eventName)) < 0) {
+ vshError(ctl, _("unknown event type %s"), eventName);
+ return false;
+ }
+
+ data.ctl = ctl;
+ data.loop = vshCommandOptBool(cmd, "loop");
+ data.timestamp = vshCommandOptBool(cmd, "timestamp");
+ data.count = 0;
+ if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
+ return false;
+
+ if (vshCommandOptBool(cmd, "node device"))
+ dev = virshCommandOptNodeDevice(ctl, cmd, NULL);
+ if (vshEventStart(ctl, timeout) < 0)
+ goto cleanup;
+
+ if ((eventId = virConnectNodeDeviceEventRegisterAny(priv->conn, dev, event,
+
VIR_NODE_DEVICE_EVENT_CALLBACK(vshEventLifecyclePrint),
+ &data, NULL)) < 0)
+ goto cleanup;
+ switch (vshEventWait(ctl)) {
+ case VSH_EVENT_INTERRUPT:
+ vshPrint(ctl, "%s", _("event loop interrupted\n"));
+ break;
+ case VSH_EVENT_TIMEOUT:
+ vshPrint(ctl, "%s", _("event loop timed out\n"));
+ break;
+ case VSH_EVENT_DONE:
+ break;
+ default:
+ goto cleanup;
+ }
+ vshPrint(ctl, _("events received: %d\n"), data.count);
+ if (data.count)
+ ret = true;
+
+ cleanup:
+ vshEventCleanup(ctl);
+ if (eventId >= 0 &&
+ virConnectNodeDeviceEventDeregisterAny(priv->conn, eventId) < 0)
+ ret = false;
+ if (dev)
+ virNodeDeviceFree(dev);
+ return ret;
+}
+
+
const vshCmdDef nodedevCmds[] = {
{.name = "nodedev-create",
.handler = cmdNodeDeviceCreate,
@@ -788,5 +993,11 @@ const vshCmdDef nodedevCmds[] = {
.info = info_node_device_reset,
.flags = 0
},
+ {.name = "nodedev-event",
+ .handler = cmdNodeDeviceEvent,
+ .opts = opts_node_device_event,
+ .info = info_node_device_event,
+ .flags = 0
+ },
{.name = NULL}
};
diff --git a/tools/virsh-nodedev.h b/tools/virsh-nodedev.h
index c64f7df..b6b2e02 100644
--- a/tools/virsh-nodedev.h
+++ b/tools/virsh-nodedev.h
@@ -28,6 +28,16 @@
# include "virsh.h"
+virNodeDevicePtr
+virshCommandOptNodeDeviceBy(vshControl *ctl, const vshCmd *cmd,
+ const char **name, unsigned int flags);
+
+/* default is lookup by Name */
+# define virshCommandOptNodeDevice(_ctl, _cmd, _name) \
+ virshCommandOptNodeDeviceBy(_ctl, _cmd, _name, \
+ VIRSH_BYNAME)
+
+
extern const vshCmdDef nodedevCmds[];
#endif /* VIRSH_NODEDEV_H */
diff --git a/tools/virsh.pod b/tools/virsh.pod
index d7cd10e..e3530a1 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -2932,6 +2932,24 @@ a node device between guest passthrough or the host. Libvirt will
often do this action implicitly when required, but this command
allows an explicit reset when needed.
+=item B<nodedev-event> {[I<nodedev>] I<event> [I<--loop>]
[I<--timeout>
+I<seconds>] [I<--timestamp>] | I<--list>}
+
+Wait for a class of node device events to occur, and print appropriate
+details of events as they happen. The events can optionally be filtered
+by I<nodedev>. Using I<--list> as the only argument will provide a list
+of possible I<event> values known by this client, although the connection
+might not allow registering for all these events.
+
+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.
+If I<--timeout> is specified, the command gives up waiting for events
+after I<seconds> have elapsed. With I<--loop>, the command prints all
+events until a timeout or interrupt key.
+
+When I<--timestamp> is used, a human-readable timestamp will be printed
+before the event.
+
=back
=head1 VIRTUAL NETWORK COMMANDS
--
2.7.4