mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-21 21:25:25 +00:00
virsh: Move 'cmdEvent' and all of its machinery to virsh-domain-event.c
'cmdEvent' along with all the helper functions it needs is ~950 LOC. Move it out from virsh-domain.c to virsh-domain-event.c along with the completer function so that the new module doesn't have to expose any new types. Semantically this creates a new category in 'virsh help' but all other behaviour stays the same. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
parent
c704d27d4a
commit
ea3e64fefd
@ -354,6 +354,7 @@
|
||||
@SRCDIR@tools/virsh-checkpoint.c
|
||||
@SRCDIR@tools/virsh-completer-host.c
|
||||
@SRCDIR@tools/virsh-console.c
|
||||
@SRCDIR@tools/virsh-domain-event.c
|
||||
@SRCDIR@tools/virsh-domain-monitor.c
|
||||
@SRCDIR@tools/virsh-domain.c
|
||||
@SRCDIR@tools/virsh-edit.c
|
||||
|
@ -174,6 +174,7 @@ executable(
|
||||
'virsh-completer-volume.c',
|
||||
'virsh-console.c',
|
||||
'virsh-domain.c',
|
||||
'virsh-domain-event.c',
|
||||
'virsh-domain-monitor.c',
|
||||
'virsh-host.c',
|
||||
'virsh-interface.c',
|
||||
|
@ -357,25 +357,6 @@ virshDomainBlockjobBaseTopCompleter(vshControl *ctl,
|
||||
return ret;
|
||||
}
|
||||
|
||||
char **
|
||||
virshDomainEventNameCompleter(vshControl *ctl G_GNUC_UNUSED,
|
||||
const vshCmd *cmd G_GNUC_UNUSED,
|
||||
unsigned int flags)
|
||||
{
|
||||
size_t i = 0;
|
||||
g_auto(GStrv) tmp = NULL;
|
||||
|
||||
virCheckFlags(0, NULL);
|
||||
|
||||
tmp = g_new0(char *, VIR_DOMAIN_EVENT_ID_LAST + 1);
|
||||
|
||||
for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++)
|
||||
tmp[i] = g_strdup(virshDomainEventCallbacks[i].name);
|
||||
|
||||
return g_steal_pointer(&tmp);
|
||||
}
|
||||
|
||||
|
||||
char **
|
||||
virshDomainInterfaceStateCompleter(vshControl *ctl,
|
||||
const vshCmd *cmd,
|
||||
|
@ -41,11 +41,6 @@ virshDomainDiskTargetCompleter(vshControl *ctl,
|
||||
const vshCmd *cmd,
|
||||
unsigned int flags);
|
||||
|
||||
char **
|
||||
virshDomainEventNameCompleter(vshControl *ctl,
|
||||
const vshCmd *cmd,
|
||||
unsigned int flags);
|
||||
|
||||
char **
|
||||
virshDomainInterfaceStateCompleter(vshControl *ctl,
|
||||
const vshCmd *cmd,
|
||||
|
1007
tools/virsh-domain-event.c
Normal file
1007
tools/virsh-domain-event.c
Normal file
File diff suppressed because it is too large
Load Diff
23
tools/virsh-domain-event.h
Normal file
23
tools/virsh-domain-event.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* virsh-domain-event.h: Commands for domain events
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "virsh.h"
|
||||
|
||||
extern const vshCmdDef domEventCmds[];
|
@ -12887,946 +12887,6 @@ cmdEdit(vshControl *ctl, const vshCmd *cmd)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* "event" command
|
||||
*/
|
||||
VIR_ENUM_DECL(virshDomainEvent);
|
||||
VIR_ENUM_IMPL(virshDomainEvent,
|
||||
VIR_DOMAIN_EVENT_LAST,
|
||||
N_("Defined"),
|
||||
N_("Undefined"),
|
||||
N_("Started"),
|
||||
N_("Suspended"),
|
||||
N_("Resumed"),
|
||||
N_("Stopped"),
|
||||
N_("Shutdown"),
|
||||
N_("PMSuspended"),
|
||||
N_("Crashed"));
|
||||
|
||||
static const char *
|
||||
virshDomainEventToString(int event)
|
||||
{
|
||||
const char *str = virshDomainEventTypeToString(event);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventDefined);
|
||||
VIR_ENUM_IMPL(virshDomainEventDefined,
|
||||
VIR_DOMAIN_EVENT_DEFINED_LAST,
|
||||
N_("Added"),
|
||||
N_("Updated"),
|
||||
N_("Renamed"),
|
||||
N_("Snapshot"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventUndefined);
|
||||
VIR_ENUM_IMPL(virshDomainEventUndefined,
|
||||
VIR_DOMAIN_EVENT_UNDEFINED_LAST,
|
||||
N_("Removed"),
|
||||
N_("Renamed"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventStarted);
|
||||
VIR_ENUM_IMPL(virshDomainEventStarted,
|
||||
VIR_DOMAIN_EVENT_STARTED_LAST,
|
||||
N_("Booted"),
|
||||
N_("Migrated"),
|
||||
N_("Restored"),
|
||||
N_("Snapshot"),
|
||||
N_("Event wakeup"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventSuspended);
|
||||
VIR_ENUM_IMPL(virshDomainEventSuspended,
|
||||
VIR_DOMAIN_EVENT_SUSPENDED_LAST,
|
||||
N_("Paused"),
|
||||
N_("Migrated"),
|
||||
N_("I/O Error"),
|
||||
N_("Watchdog"),
|
||||
N_("Restored"),
|
||||
N_("Snapshot"),
|
||||
N_("API error"),
|
||||
N_("Post-copy"),
|
||||
N_("Post-copy Error"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventResumed);
|
||||
VIR_ENUM_IMPL(virshDomainEventResumed,
|
||||
VIR_DOMAIN_EVENT_RESUMED_LAST,
|
||||
N_("Unpaused"),
|
||||
N_("Migrated"),
|
||||
N_("Snapshot"),
|
||||
N_("Post-copy"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventStopped);
|
||||
VIR_ENUM_IMPL(virshDomainEventStopped,
|
||||
VIR_DOMAIN_EVENT_STOPPED_LAST,
|
||||
N_("Shutdown"),
|
||||
N_("Destroyed"),
|
||||
N_("Crashed"),
|
||||
N_("Migrated"),
|
||||
N_("Saved"),
|
||||
N_("Failed"),
|
||||
N_("Snapshot"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventShutdown);
|
||||
VIR_ENUM_IMPL(virshDomainEventShutdown,
|
||||
VIR_DOMAIN_EVENT_SHUTDOWN_LAST,
|
||||
N_("Finished"),
|
||||
N_("Finished after guest request"),
|
||||
N_("Finished after host request"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventPMSuspended);
|
||||
VIR_ENUM_IMPL(virshDomainEventPMSuspended,
|
||||
VIR_DOMAIN_EVENT_PMSUSPENDED_LAST,
|
||||
N_("Memory"),
|
||||
N_("Disk"));
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventCrashed);
|
||||
VIR_ENUM_IMPL(virshDomainEventCrashed,
|
||||
VIR_DOMAIN_EVENT_CRASHED_LAST,
|
||||
N_("Panicked"),
|
||||
N_("Crashloaded"));
|
||||
|
||||
static const char *
|
||||
virshDomainEventDetailToString(int event, int detail)
|
||||
{
|
||||
const char *str = NULL;
|
||||
switch ((virDomainEventType) event) {
|
||||
case VIR_DOMAIN_EVENT_DEFINED:
|
||||
str = virshDomainEventDefinedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_UNDEFINED:
|
||||
str = virshDomainEventUndefinedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_STARTED:
|
||||
str = virshDomainEventStartedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_SUSPENDED:
|
||||
str = virshDomainEventSuspendedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_RESUMED:
|
||||
str = virshDomainEventResumedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_STOPPED:
|
||||
str = virshDomainEventStoppedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_SHUTDOWN:
|
||||
str = virshDomainEventShutdownTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_PMSUSPENDED:
|
||||
str = virshDomainEventPMSuspendedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_CRASHED:
|
||||
str = virshDomainEventCrashedTypeToString(detail);
|
||||
break;
|
||||
case VIR_DOMAIN_EVENT_LAST:
|
||||
break;
|
||||
}
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventWatchdog);
|
||||
VIR_ENUM_IMPL(virshDomainEventWatchdog,
|
||||
VIR_DOMAIN_EVENT_WATCHDOG_LAST,
|
||||
N_("none"),
|
||||
N_("pause"),
|
||||
N_("reset"),
|
||||
N_("poweroff"),
|
||||
N_("shutdown"),
|
||||
N_("debug"),
|
||||
N_("inject-nmi"));
|
||||
|
||||
static const char *
|
||||
virshDomainEventWatchdogToString(int action)
|
||||
{
|
||||
const char *str = virshDomainEventWatchdogTypeToString(action);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventIOError);
|
||||
VIR_ENUM_IMPL(virshDomainEventIOError,
|
||||
VIR_DOMAIN_EVENT_IO_ERROR_LAST,
|
||||
N_("none"),
|
||||
N_("pause"),
|
||||
N_("report"));
|
||||
|
||||
static const char *
|
||||
virshDomainEventIOErrorToString(int action)
|
||||
{
|
||||
const char *str = virshDomainEventIOErrorTypeToString(action);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshGraphicsPhase);
|
||||
VIR_ENUM_IMPL(virshGraphicsPhase,
|
||||
VIR_DOMAIN_EVENT_GRAPHICS_LAST,
|
||||
N_("connect"),
|
||||
N_("initialize"),
|
||||
N_("disconnect"));
|
||||
|
||||
static const char *
|
||||
virshGraphicsPhaseToString(int phase)
|
||||
{
|
||||
const char *str = virshGraphicsPhaseTypeToString(phase);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshGraphicsAddress);
|
||||
VIR_ENUM_IMPL(virshGraphicsAddress,
|
||||
VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
|
||||
N_("IPv4"),
|
||||
N_("IPv6"),
|
||||
N_("unix"));
|
||||
|
||||
static const char *
|
||||
virshGraphicsAddressToString(int family)
|
||||
{
|
||||
const char *str = virshGraphicsAddressTypeToString(family);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshDomainBlockJobStatus);
|
||||
VIR_ENUM_IMPL(virshDomainBlockJobStatus,
|
||||
VIR_DOMAIN_BLOCK_JOB_LAST,
|
||||
N_("completed"),
|
||||
N_("failed"),
|
||||
N_("canceled"),
|
||||
N_("ready"));
|
||||
|
||||
static const char *
|
||||
virshDomainBlockJobStatusToString(int status)
|
||||
{
|
||||
const char *str = virshDomainBlockJobStatusTypeToString(status);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventDiskChange);
|
||||
VIR_ENUM_IMPL(virshDomainEventDiskChange,
|
||||
VIR_DOMAIN_EVENT_DISK_CHANGE_LAST,
|
||||
N_("changed"),
|
||||
N_("dropped"));
|
||||
|
||||
static const char *
|
||||
virshDomainEventDiskChangeToString(int reason)
|
||||
{
|
||||
const char *str = virshDomainEventDiskChangeTypeToString(reason);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshDomainEventTrayChange);
|
||||
VIR_ENUM_IMPL(virshDomainEventTrayChange,
|
||||
VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST,
|
||||
N_("opened"),
|
||||
N_("closed"));
|
||||
|
||||
static const char *
|
||||
virshDomainEventTrayChangeToString(int reason)
|
||||
{
|
||||
const char *str = virshDomainEventTrayChangeTypeToString(reason);
|
||||
return str ? _(str) : _("unknown");
|
||||
}
|
||||
|
||||
struct virshDomEventData {
|
||||
vshControl *ctl;
|
||||
bool loop;
|
||||
int *count;
|
||||
bool timestamp;
|
||||
virshDomainEventCallback *cb;
|
||||
int id;
|
||||
};
|
||||
typedef struct virshDomEventData virshDomEventData;
|
||||
|
||||
/**
|
||||
* virshEventPrint:
|
||||
*
|
||||
* @data: opaque data passed to all event callbacks
|
||||
* @buf: string buffer describing the event
|
||||
*
|
||||
* Print the event description found in @buf and update virshDomEventData.
|
||||
*
|
||||
* This function resets @buf and frees all memory consumed by its content.
|
||||
*/
|
||||
static void
|
||||
virshEventPrint(virshDomEventData *data,
|
||||
virBuffer *buf)
|
||||
{
|
||||
g_autofree char *msg = NULL;
|
||||
|
||||
if (!(msg = virBufferContentAndReset(buf)))
|
||||
return;
|
||||
|
||||
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: %s", timestamp, msg);
|
||||
} else {
|
||||
vshPrint(data->ctl, "%s", msg);
|
||||
}
|
||||
|
||||
(*data->count)++;
|
||||
if (!data->loop)
|
||||
vshEventDone(data->ctl);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event '%s' for domain '%s'\n"),
|
||||
((virshDomEventData *) opaque)->cb->name,
|
||||
virDomainGetName(dom));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int event,
|
||||
int detail,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'lifecycle' for domain '%s': %s %s\n"),
|
||||
virDomainGetName(dom),
|
||||
virshDomainEventToString(event),
|
||||
virshDomainEventDetailToString(event, detail));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
long long utcoffset,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'rtc-change' for domain '%s': %lld\n"),
|
||||
virDomainGetName(dom),
|
||||
utcoffset);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int action,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'watchdog' for domain '%s': %s\n"),
|
||||
virDomainGetName(dom),
|
||||
virshDomainEventWatchdogToString(action));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *srcPath,
|
||||
const char *devAlias,
|
||||
int action,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'io-error' for domain '%s': %s (%s) %s\n"),
|
||||
virDomainGetName(dom),
|
||||
srcPath,
|
||||
devAlias,
|
||||
virshDomainEventIOErrorToString(action));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int phase,
|
||||
const virDomainEventGraphicsAddress *local,
|
||||
const virDomainEventGraphicsAddress *remote,
|
||||
const char *authScheme,
|
||||
const virDomainEventGraphicsSubject *subject,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
size_t i;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'graphics' for domain '%s': "
|
||||
"%s local[%s %s %s] remote[%s %s %s] %s\n"),
|
||||
virDomainGetName(dom),
|
||||
virshGraphicsPhaseToString(phase),
|
||||
virshGraphicsAddressToString(local->family),
|
||||
local->node,
|
||||
local->service,
|
||||
virshGraphicsAddressToString(remote->family),
|
||||
remote->node,
|
||||
remote->service,
|
||||
authScheme);
|
||||
for (i = 0; i < subject->nidentity; i++) {
|
||||
virBufferAsprintf(&buf, "\t%s=%s\n",
|
||||
subject->identities[i].type,
|
||||
subject->identities[i].name);
|
||||
}
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *srcPath,
|
||||
const char *devAlias,
|
||||
int action,
|
||||
const char *reason,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'io-error-reason' for domain '%s': "
|
||||
"%s (%s) %s due to %s\n"),
|
||||
virDomainGetName(dom),
|
||||
srcPath,
|
||||
devAlias,
|
||||
virshDomainEventIOErrorToString(action),
|
||||
reason);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *disk,
|
||||
int type,
|
||||
int status,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event '%s' for domain '%s': %s for %s %s\n"),
|
||||
((virshDomEventData *) opaque)->cb->name,
|
||||
virDomainGetName(dom),
|
||||
virshDomainBlockJobToString(type),
|
||||
disk,
|
||||
virshDomainBlockJobStatusToString(status));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *oldSrc,
|
||||
const char *newSrc,
|
||||
const char *alias,
|
||||
int reason,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'disk-change' for domain '%s' disk %s: "
|
||||
"%s -> %s: %s\n"),
|
||||
virDomainGetName(dom),
|
||||
alias,
|
||||
NULLSTR(oldSrc),
|
||||
NULLSTR(newSrc),
|
||||
virshDomainEventDiskChangeToString(reason));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *alias,
|
||||
int reason,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'tray-change' for domain '%s' disk %s: %s\n"),
|
||||
virDomainGetName(dom),
|
||||
alias,
|
||||
virshDomainEventTrayChangeToString(reason));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int reason G_GNUC_UNUSED,
|
||||
void *opaque)
|
||||
{
|
||||
/* As long as libvirt.h doesn't define any reasons, we might as
|
||||
* well treat all PM state changes as generic events. */
|
||||
virshEventGenericPrint(conn, dom, opaque);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
unsigned long long actual,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'balloon-change' for domain '%s': %lluKiB\n"),
|
||||
virDomainGetName(dom),
|
||||
actual);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *alias,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'device-removed' for domain '%s': %s\n"),
|
||||
virDomainGetName(dom),
|
||||
alias);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *alias,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'device-added' for domain '%s': %s\n"),
|
||||
virDomainGetName(dom),
|
||||
alias);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
virTypedParameterPtr params,
|
||||
int nparams,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
size_t i;
|
||||
char *value;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'tunable' for domain '%s':\n"),
|
||||
virDomainGetName(dom));
|
||||
for (i = 0; i < nparams; i++) {
|
||||
value = virTypedParameterToString(¶ms[i]);
|
||||
if (value) {
|
||||
virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
|
||||
VIR_FREE(value);
|
||||
}
|
||||
}
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshEventAgentLifecycleState);
|
||||
VIR_ENUM_IMPL(virshEventAgentLifecycleState,
|
||||
VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST,
|
||||
N_("unknown"),
|
||||
N_("connected"),
|
||||
N_("disconnected"));
|
||||
|
||||
VIR_ENUM_DECL(virshEventAgentLifecycleReason);
|
||||
VIR_ENUM_IMPL(virshEventAgentLifecycleReason,
|
||||
VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST,
|
||||
N_("unknown"),
|
||||
N_("domain started"),
|
||||
N_("channel event"));
|
||||
|
||||
#define UNKNOWNSTR(str) (str ? str : N_("unsupported value"))
|
||||
static void
|
||||
virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int state,
|
||||
int reason,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain '%s': state: "
|
||||
"'%s' reason: '%s'\n"),
|
||||
virDomainGetName(dom),
|
||||
UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)),
|
||||
UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason)));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int iteration,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'migration-iteration' for domain '%s': "
|
||||
"iteration: '%d'\n"),
|
||||
virDomainGetName(dom),
|
||||
iteration);
|
||||
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
static void
|
||||
virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
virTypedParameterPtr params,
|
||||
int nparams,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
size_t i;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'job-completed' for domain '%s':\n"),
|
||||
virDomainGetName(dom));
|
||||
for (i = 0; i < nparams; i++) {
|
||||
g_autofree char *value = virTypedParameterToString(¶ms[i]);
|
||||
if (value)
|
||||
virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
|
||||
}
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *alias,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain '%s': %s\n"),
|
||||
virDomainGetName(dom),
|
||||
alias);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
VIR_ENUM_DECL(virshEventMetadataChangeType);
|
||||
VIR_ENUM_IMPL(virshEventMetadataChangeType,
|
||||
VIR_DOMAIN_METADATA_LAST,
|
||||
N_("description"),
|
||||
N_("title"),
|
||||
N_("element"));
|
||||
|
||||
static void
|
||||
virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int type,
|
||||
const char *nsuri,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'metadata-change' for domain '%s': type %s, uri %s\n"),
|
||||
virDomainGetName(dom),
|
||||
UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)),
|
||||
NULLSTR(nsuri));
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *dev,
|
||||
const char *path,
|
||||
unsigned long long threshold,
|
||||
unsigned long long excess,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'block-threshold' for domain '%s': "
|
||||
"dev: %s(%s) %llu %llu\n"),
|
||||
virDomainGetName(dom),
|
||||
dev, NULLSTR(path), threshold, excess);
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
|
||||
VIR_ENUM_DECL(virshEventMemoryFailureRecipientType);
|
||||
VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType,
|
||||
VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST,
|
||||
N_("hypervisor"),
|
||||
N_("guest"));
|
||||
|
||||
VIR_ENUM_DECL(virshEventMemoryFailureActionType);
|
||||
VIR_ENUM_IMPL(virshEventMemoryFailureActionType,
|
||||
VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST,
|
||||
N_("ignore"),
|
||||
N_("inject"),
|
||||
N_("fatal"),
|
||||
N_("reset"));
|
||||
|
||||
static void
|
||||
virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
int recipient,
|
||||
int action,
|
||||
unsigned int flags,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf, _("event 'memory-failure' for domain '%s':\n"
|
||||
"recipient: %s\naction: %s\n"),
|
||||
virDomainGetName(dom),
|
||||
UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)),
|
||||
UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action)));
|
||||
virBufferAsprintf(&buf, _("flags:\n"
|
||||
"\taction required: %d\n\trecursive: %d\n"),
|
||||
!!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED),
|
||||
!!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE));
|
||||
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED,
|
||||
virDomainPtr dom,
|
||||
const char *alias,
|
||||
unsigned long long size,
|
||||
void *opaque)
|
||||
{
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
virBufferAsprintf(&buf,
|
||||
_("event 'memory-device-size-change' for domain '%s':\n"
|
||||
"alias: %s\nsize: %llu\n"),
|
||||
virDomainGetName(dom), alias, size);
|
||||
|
||||
virshEventPrint(opaque, &buf);
|
||||
}
|
||||
|
||||
|
||||
virshDomainEventCallback virshDomainEventCallbacks[] = {
|
||||
{ "lifecycle",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
|
||||
{ "reboot", virshEventGenericPrint, },
|
||||
{ "rtc-change",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), },
|
||||
{ "watchdog",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), },
|
||||
{ "io-error",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), },
|
||||
{ "graphics",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), },
|
||||
{ "io-error-reason",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), },
|
||||
{ "control-error", virshEventGenericPrint, },
|
||||
{ "block-job",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
|
||||
{ "disk-change",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), },
|
||||
{ "tray-change",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), },
|
||||
{ "pm-wakeup",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
|
||||
{ "pm-suspend",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
|
||||
{ "balloon-change",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), },
|
||||
{ "pm-suspend-disk",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
|
||||
{ "device-removed",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), },
|
||||
{ "block-job-2",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
|
||||
{ "tunable",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), },
|
||||
{ "agent-lifecycle",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), },
|
||||
{ "device-added",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), },
|
||||
{ "migration-iteration",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), },
|
||||
{ "job-completed",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), },
|
||||
{ "device-removal-failed",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), },
|
||||
{ "metadata-change",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
|
||||
{ "block-threshold",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
|
||||
{ "memory-failure",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), },
|
||||
{ "memory-device-size-change",
|
||||
VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), },
|
||||
};
|
||||
G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks));
|
||||
|
||||
static const vshCmdInfo info_event[] = {
|
||||
{.name = "help",
|
||||
.data = N_("Domain Events")
|
||||
},
|
||||
{.name = "desc",
|
||||
.data = N_("List event types, or wait for domain events to occur")
|
||||
},
|
||||
{.name = NULL}
|
||||
};
|
||||
|
||||
static const vshCmdOptDef opts_event[] = {
|
||||
VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
|
||||
0, 0),
|
||||
{.name = "event",
|
||||
.type = VSH_OT_STRING,
|
||||
.completer = virshDomainEventNameCompleter,
|
||||
.help = N_("which event type to wait for")
|
||||
},
|
||||
{.name = "all",
|
||||
.type = VSH_OT_BOOL,
|
||||
.help = N_("wait for all events instead of just one type")
|
||||
},
|
||||
{.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}
|
||||
};
|
||||
|
||||
static bool
|
||||
cmdEvent(vshControl *ctl, const vshCmd *cmd)
|
||||
{
|
||||
g_autoptr(virshDomain) dom = NULL;
|
||||
bool ret = false;
|
||||
int timeout = 0;
|
||||
virshDomEventData *data = NULL;
|
||||
size_t i;
|
||||
const char *eventName = NULL;
|
||||
int event = -1;
|
||||
bool all = vshCommandOptBool(cmd, "all");
|
||||
bool loop = vshCommandOptBool(cmd, "loop");
|
||||
bool timestamp = vshCommandOptBool(cmd, "timestamp");
|
||||
int count = 0;
|
||||
virshControl *priv = ctl->privData;
|
||||
|
||||
VSH_EXCLUSIVE_OPTIONS("all", "event");
|
||||
VSH_EXCLUSIVE_OPTIONS("list", "all");
|
||||
VSH_EXCLUSIVE_OPTIONS("list", "event");
|
||||
|
||||
if (vshCommandOptBool(cmd, "list")) {
|
||||
for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
|
||||
vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
|
||||
return false;
|
||||
if (eventName) {
|
||||
for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
|
||||
if (STREQ(eventName, virshDomainEventCallbacks[event].name))
|
||||
break;
|
||||
if (event == VIR_DOMAIN_EVENT_ID_LAST) {
|
||||
vshError(ctl, _("unknown event type %s"), eventName);
|
||||
return false;
|
||||
}
|
||||
} else if (!all) {
|
||||
vshError(ctl, "%s",
|
||||
_("one of --list, --all, or --event <type> is required"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (all) {
|
||||
data = g_new0(virshDomEventData, VIR_DOMAIN_EVENT_ID_LAST);
|
||||
for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) {
|
||||
data[i].ctl = ctl;
|
||||
data[i].loop = loop;
|
||||
data[i].count = &count;
|
||||
data[i].timestamp = timestamp;
|
||||
data[i].cb = &virshDomainEventCallbacks[i];
|
||||
data[i].id = -1;
|
||||
}
|
||||
} else {
|
||||
data = g_new0(virshDomEventData, 1);
|
||||
data[0].ctl = ctl;
|
||||
data[0].loop = vshCommandOptBool(cmd, "loop");
|
||||
data[0].count = &count;
|
||||
data[0].timestamp = timestamp;
|
||||
data[0].cb = &virshDomainEventCallbacks[event];
|
||||
data[0].id = -1;
|
||||
}
|
||||
if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (vshCommandOptBool(cmd, "domain"))
|
||||
if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
|
||||
goto cleanup;
|
||||
|
||||
if (vshEventStart(ctl, timeout) < 0)
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
|
||||
if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom,
|
||||
all ? i : event,
|
||||
data[i].cb->cb,
|
||||
&data[i],
|
||||
NULL)) < 0) {
|
||||
/* When registering for all events: if the first
|
||||
* registration succeeds, silently ignore failures on all
|
||||
* later registrations on the assumption that the server
|
||||
* is older and didn't know quite as many events. */
|
||||
if (i)
|
||||
vshResetLibvirtError();
|
||||
else
|
||||
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"), count);
|
||||
if (count)
|
||||
ret = true;
|
||||
|
||||
cleanup:
|
||||
vshEventCleanup(ctl);
|
||||
if (data) {
|
||||
for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
|
||||
if (data[i].id >= 0 &&
|
||||
virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0)
|
||||
ret = false;
|
||||
}
|
||||
VIR_FREE(data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* "change-media" command
|
||||
*/
|
||||
@ -14851,12 +13911,6 @@ const vshCmdDef domManagementCmds[] = {
|
||||
.info = info_edit,
|
||||
.flags = 0
|
||||
},
|
||||
{.name = "event",
|
||||
.handler = cmdEvent,
|
||||
.opts = opts_event,
|
||||
.info = info_event,
|
||||
.flags = 0
|
||||
},
|
||||
{.name = "get-user-sshkeys",
|
||||
.handler = cmdGetUserSSHKeys,
|
||||
.opts = opts_get_user_sshkeys,
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "virsh-checkpoint.h"
|
||||
#include "virsh-console.h"
|
||||
#include "virsh-domain.h"
|
||||
#include "virsh-domain-event.h"
|
||||
#include "virsh-domain-monitor.h"
|
||||
#include "virsh-host.h"
|
||||
#include "virsh-interface.h"
|
||||
@ -814,6 +815,7 @@ static const vshCmdDef virshCmds[] = {
|
||||
static const vshCmdGrp cmdGroups[] = {
|
||||
{VIRSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds},
|
||||
{VIRSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds},
|
||||
{VIRSH_CMD_GRP_DOM_EVENTS, "events", domEventCmds},
|
||||
{VIRSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds},
|
||||
{VIRSH_CMD_GRP_CHECKPOINT, "checkpoint", checkpointCmds},
|
||||
{VIRSH_CMD_GRP_IFACE, "interface", ifaceCmds},
|
||||
|
@ -41,6 +41,7 @@
|
||||
#define VIRSH_CMD_GRP_CHECKPOINT "Checkpoint"
|
||||
#define VIRSH_CMD_GRP_DOM_MANAGEMENT "Domain Management"
|
||||
#define VIRSH_CMD_GRP_DOM_MONITORING "Domain Monitoring"
|
||||
#define VIRSH_CMD_GRP_DOM_EVENTS "Domain Events"
|
||||
#define VIRSH_CMD_GRP_STORAGE_POOL "Storage Pool"
|
||||
#define VIRSH_CMD_GRP_STORAGE_VOL "Storage Volume"
|
||||
#define VIRSH_CMD_GRP_NETWORK "Networking"
|
||||
|
Loading…
Reference in New Issue
Block a user