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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list