If block job is completed with error qemu additionally provides error message. This patch introduces new event VIR_DOMAIN_EVENT_ID_BLOCK_JOB_ERROR to pass error message to client. This error message has no semantics and should not be interpreted. API and RPC layer also have reserved 'code' field to pass semantics loaded error code value in the future. Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@xxxxxxxxxxxxx> --- Diff from v1 [1]: - replace block job event version 3 with block job error event - add code field to API/RPC to pass error code in the future Peter, I decided not to use enum/union as this looks like to much effort to extend code generation for this simple case. Using typed params looks unsuitable too. [1] https://www.redhat.com/archives/libvir-list/2017-October/msg01369.html examples/object-events/event-test.c | 20 ++++++++ include/libvirt/libvirt-domain.h | 25 ++++++++++ src/conf/domain_event.c | 92 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 13 ++++++ src/libvirt_private.syms | 2 + src/qemu/qemu_blockjob.c | 10 +++- src/qemu/qemu_blockjob.h | 3 +- src/qemu/qemu_domain.c | 4 +- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 9 ++-- src/qemu/qemu_process.c | 1 + src/remote/remote_daemon_dispatch.c | 48 +++++++++++++++++++ src/remote/remote_driver.c | 34 ++++++++++++++ src/remote/remote_protocol.x | 17 ++++++- src/remote_protocol-structs | 9 ++++ tools/virsh-domain.c | 24 ++++++++++ 16 files changed, 304 insertions(+), 8 deletions(-) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 8499e0b..23d5a3f 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -936,6 +936,25 @@ myDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSED, static int +myDomainEventBlockJobErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *dev, + int type, + unsigned int code, + const char *message, + void *opaque) +{ + const char *eventName = opaque; + + printf("%s EVENT: Domain %s(%d) block job error callback '%s' disk '%s', " + "type '%s' code '%u' message '%s'", + __func__, virDomainGetName(dom), virDomainGetID(dom), eventName, + dev, blockJobTypeToStr(type), code, NULLSTR(message)); + return 0; +} + + +static int myDomainEventBlockThresholdCallback(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, const char *dev, @@ -1082,6 +1101,7 @@ struct domainEventData domainEvents[] = { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, myDomainEventMetadataChangeCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback), + DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_JOB_ERROR, myDomainEventBlockJobErrorCallback), }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 12fd340..ee55ab9 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4370,6 +4370,30 @@ typedef void (*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn, unsigned long long excess, void *opaque); + +/** + * virConnectDomainEventBlockJobErrorCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @dev: name associated with the affected disk or storage backing chain + * element + * @type: type of block job (virDomainBlockJobType) + * @code: always 0, reserved for future use + * @message: error message with no semantics, can be NULL + * @opaque: application specified data + * + * The callback occurs when block job is completed with error and provides + * error message in @message. + * + */ +typedef void (*virConnectDomainEventBlockJobErrorCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *dev, + int type, + unsigned int code, + const char *message, + void *opaque); + /** * VIR_DOMAIN_EVENT_CALLBACK: * @@ -4412,6 +4436,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED = 22, /* virConnectDomainEventDeviceRemovalFailedCallback */ VIR_DOMAIN_EVENT_ID_METADATA_CHANGE = 23, /* virConnectDomainEventMetadataChangeCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD = 24, /* virConnectDomainEventBlockThresholdCallback */ + VIR_DOMAIN_EVENT_ID_BLOCK_JOB_ERROR = 25, /* virConnectDomainEventBlockJobErrorCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 9752070..54f4cf4 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -47,6 +47,7 @@ static virClassPtr virDomainEventWatchdogClass; static virClassPtr virDomainEventIOErrorClass; static virClassPtr virDomainEventGraphicsClass; static virClassPtr virDomainEventBlockJobClass; +static virClassPtr virDomainEventBlockJobErrorClass; static virClassPtr virDomainEventDiskChangeClass; static virClassPtr virDomainEventTrayChangeClass; static virClassPtr virDomainEventBalloonChangeClass; @@ -69,6 +70,7 @@ static void virDomainEventWatchdogDispose(void *obj); static void virDomainEventIOErrorDispose(void *obj); static void virDomainEventGraphicsDispose(void *obj); static void virDomainEventBlockJobDispose(void *obj); +static void virDomainEventBlockJobErrorDispose(void *obj); static void virDomainEventDiskChangeDispose(void *obj); static void virDomainEventTrayChangeDispose(void *obj); static void virDomainEventBalloonChangeDispose(void *obj); @@ -151,6 +153,17 @@ struct _virDomainEventBlockJob { typedef struct _virDomainEventBlockJob virDomainEventBlockJob; typedef virDomainEventBlockJob *virDomainEventBlockJobPtr; +struct _virDomainEventBlockJobError { + virDomainEvent parent; + + char *dev; + int type; + unsigned int code; + char *message; +}; +typedef struct _virDomainEventBlockJobError virDomainEventBlockJobError; +typedef virDomainEventBlockJobError *virDomainEventBlockJobErrorPtr; + struct _virDomainEventGraphics { virDomainEvent parent; @@ -309,6 +322,8 @@ virDomainEventsOnceInit(void) return -1; if (!VIR_CLASS_NEW(virDomainEventBlockJob, virDomainEventClass)) return -1; + if (!VIR_CLASS_NEW(virDomainEventBlockJobError, virDomainEventClass)) + return -1; if (!VIR_CLASS_NEW(virDomainEventDiskChange, virDomainEventClass)) return -1; if (!VIR_CLASS_NEW(virDomainEventTrayChange, virDomainEventClass)) @@ -420,6 +435,16 @@ virDomainEventBlockJobDispose(void *obj) } static void +virDomainEventBlockJobErrorDispose(void *obj) +{ + virDomainEventBlockJobErrorPtr event = obj; + VIR_DEBUG("obj=%p", event); + + VIR_FREE(event->dev); + VIR_FREE(event->message); +} + +static void virDomainEventDiskChangeDispose(void *obj) { virDomainEventDiskChangePtr event = obj; @@ -977,6 +1002,59 @@ virDomainEventBlockJob2NewFromDom(virDomainPtr dom, dst, type, status); } +static virObjectEventPtr +virDomainEventBlockJobErrorNew(int id, + const char *name, + unsigned char *uuid, + const char *dev, + int type, + unsigned int code, + const char *message) +{ + virDomainEventBlockJobErrorPtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventBlockJobErrorClass, + VIR_DOMAIN_EVENT_ID_BLOCK_JOB_ERROR, + id, name, uuid))) + return NULL; + + if (VIR_STRDUP(ev->dev, dev) < 0) { + virObjectUnref(ev); + return NULL; + } + ignore_value(VIR_STRDUP_QUIET(ev->message, message)); + ev->type = type; + ev->code = code; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventBlockJobErrorNewFromObj(virDomainObjPtr obj, + const char *dev, + int type, + unsigned int code, + const char *message) +{ + return virDomainEventBlockJobErrorNew(obj->def->id, obj->def->name, + obj->def->uuid, dev, type, code, + message); +} + +virObjectEventPtr +virDomainEventBlockJobErrorNewFromDom(virDomainPtr dom, + const char *dev, + int type, + unsigned int code, + const char *message) +{ + return virDomainEventBlockJobErrorNew(dom->id, dom->name, dom->uuid, + dev, type, code, message); +} + virObjectEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { @@ -1787,6 +1865,20 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_BLOCK_JOB_ERROR: + { + virDomainEventBlockJobErrorPtr blockJobErrorEvent; + + blockJobErrorEvent = (virDomainEventBlockJobErrorPtr)event; + ((virConnectDomainEventBlockJobErrorCallback)cb)(conn, dom, + blockJobErrorEvent->dev, + blockJobErrorEvent->type, + blockJobErrorEvent->code, + blockJobErrorEvent->message, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_DISK_CHANGE: { virDomainEventDiskChangePtr diskChangeEvent; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 3992a29..456026c 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -138,6 +138,19 @@ virDomainEventBlockJob2NewFromDom(virDomainPtr dom, int status); virObjectEventPtr +virDomainEventBlockJobErrorNewFromObj(virDomainObjPtr obj, + const char *dev, + int type, + unsigned int code, + const char *message); +virObjectEventPtr +virDomainEventBlockJobErrorNewFromDom(virDomainPtr dom, + const char *dev, + int type, + unsigned int code, + const char *message); + +virObjectEventPtr virDomainEventDiskChangeNewFromObj(virDomainObjPtr obj, const char *oldSrcPath, const char *newSrcPath, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b31f599..303f28c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -589,6 +589,8 @@ virDomainEventBalloonChangeNewFromDom; virDomainEventBalloonChangeNewFromObj; virDomainEventBlockJob2NewFromDom; virDomainEventBlockJob2NewFromObj; +virDomainEventBlockJobErrorNewFromDom; +virDomainEventBlockJobErrorNewFromObj; virDomainEventBlockJobNewFromDom; virDomainEventBlockJobNewFromObj; virDomainEventBlockThresholdNewFromDom; diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 617e4ee..4df4ed6 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -70,7 +70,8 @@ qemuBlockJobUpdate(virQEMUDriverPtr driver, if (status != -1) { qemuBlockJobEventProcess(driver, vm, disk, asyncJob, diskPriv->blockJobType, - diskPriv->blockJobStatus); + diskPriv->blockJobStatus, + diskPriv->blockJobError); diskPriv->blockJobStatus = -1; if (error) VIR_STEAL_PTR(*error, diskPriv->blockJobError); @@ -100,10 +101,12 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, virDomainDiskDefPtr disk, qemuDomainAsyncJob asyncJob, int type, - int status) + int status, + const char *error) { virObjectEventPtr event = NULL; virObjectEventPtr event2 = NULL; + virObjectEventPtr errorEvent = NULL; const char *path; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainDiskDefPtr persistDisk = NULL; @@ -123,6 +126,8 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, path = virDomainDiskGetSource(disk); event = virDomainEventBlockJobNewFromObj(vm, path, type, status); event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status); + if (error) + errorEvent = virDomainEventBlockJobErrorNewFromObj(vm, disk->dst, type, 0, error); /* If we completed a block pull or commit, then update the XML * to match. */ @@ -213,6 +218,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, qemuDomainEventQueue(driver, event); qemuDomainEventQueue(driver, event2); + qemuDomainEventQueue(driver, errorEvent); virObjectUnref(cfg); } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index e71d691..18bcaaa 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -36,7 +36,8 @@ void qemuBlockJobEventProcess(virQEMUDriverPtr driver, virDomainDiskDefPtr disk, qemuDomainAsyncJob asyncJob, int type, - int status); + int status, + const char *error); void qemuBlockJobSyncBegin(virDomainDiskDefPtr disk); void qemuBlockJobSyncEnd(virQEMUDriverPtr driver, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index e2a8450..883652d 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -11951,12 +11951,14 @@ qemuProcessEventFree(struct qemuProcessEvent *event) case QEMU_PROCESS_EVENT_GUESTPANIC: qemuMonitorEventPanicInfoFree(event->data); break; + case QEMU_PROCESS_EVENT_MONITOR_EOF: + VIR_FREE(event->error); + ATTRIBUTE_FALLTHROUGH; case QEMU_PROCESS_EVENT_WATCHDOG: case QEMU_PROCESS_EVENT_DEVICE_DELETED: case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED: case QEMU_PROCESS_EVENT_SERIAL_CHANGED: case QEMU_PROCESS_EVENT_BLOCK_JOB: - case QEMU_PROCESS_EVENT_MONITOR_EOF: VIR_FREE(event->data); break; case QEMU_PROCESS_EVENT_LAST: diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 2c27dfb..f3b262f 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -456,6 +456,7 @@ struct qemuProcessEvent { int action; int status; void *data; + char *error; }; void qemuProcessEventFree(struct qemuProcessEvent *event); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5673d9f..cefbe52 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4748,7 +4748,8 @@ processBlockJobEvent(virQEMUDriverPtr driver, virDomainObjPtr vm, const char *diskAlias, int type, - int status) + int status, + char *error) { virDomainDiskDefPtr disk; @@ -4761,7 +4762,8 @@ processBlockJobEvent(virQEMUDriverPtr driver, } if ((disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias))) - qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, type, status); + qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, + type, status, error); endjob: qemuDomainObjEndJob(driver, vm); @@ -4846,7 +4848,8 @@ static void qemuProcessEventHandler(void *data, void *opaque) processBlockJobEvent(driver, vm, processEvent->data, processEvent->action, - processEvent->status); + processEvent->status, + processEvent->error); break; case QEMU_PROCESS_EVENT_MONITOR_EOF: processMonitorEOFEvent(driver, vm); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 6a5262a..12b156f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1026,6 +1026,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, processEvent->vm = virObjectRef(vm); processEvent->action = type; processEvent->status = status; + ignore_value(VIR_STRDUP_QUIET(processEvent->error, error)); if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { ignore_value(virObjectUnref(vm)); diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index a8a5932..07c9dbb 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -1342,6 +1342,53 @@ remoteRelayDomainEventBlockThreshold(virConnectPtr conn, } +static int +remoteRelayDomainEventBlockJobError(virConnectPtr conn, + virDomainPtr dom, + const char *dev, + int type, + unsigned int code, + const char *message, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_block_job_error_msg msg; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain block job error event %s %d %s %i %u %s, callback %d", + dom->name, dom->id, dev, type, code, NULLSTR(message), + callback->callbackID); + + memset(&msg, 0, sizeof(msg)); + msg.callbackID = callback->callbackID; + if (VIR_STRDUP(msg.dev, dev) < 0) + return -1; + if (message) { + if (VIR_ALLOC(msg.message) < 0 || + VIR_STRDUP(*(msg.message), message) < 0) + goto error; + } + msg.type = type; + msg.code = code; + make_nonnull_domain(&msg.dom, dom); + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_ERROR, + (xdrproc_t)xdr_remote_domain_event_block_job_error_msg, + &msg); + return 0; + + error: + VIR_FREE(msg.dev); + VIR_FREE(msg.message); + + return -1; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -1368,6 +1415,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJobError), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d3b588c..0d30bf3 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -405,6 +405,11 @@ remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_U virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); +static void +remoteDomainBuildEventBlockJobError(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static virNetClientProgramEvent remoteEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE, remoteDomainBuildEventLifecycle, @@ -611,6 +616,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventBlockThreshold, sizeof(remote_domain_event_block_threshold_msg), (xdrproc_t)xdr_remote_domain_event_block_threshold_msg }, + { REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_ERROR, + remoteDomainBuildEventBlockJobError, + sizeof(remote_domain_event_block_job_error_msg), + (xdrproc_t)xdr_remote_domain_event_block_job_error_msg }, }; static void @@ -5533,6 +5542,31 @@ remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog ATTRIBUTE_UNUSE } +static void +remoteDomainBuildEventBlockJobError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_block_job_error_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventBlockJobErrorNewFromDom(dom, msg->dev, msg->type, + msg->code, + msg->message ? *msg->message : NULL); + + virObjectUnref(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + + static int remoteStreamSend(virStreamPtr st, const char *data, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 296a087..051a773 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3103,6 +3103,15 @@ struct remote_domain_event_block_job_2_msg { int status; }; +struct remote_domain_event_block_job_error_msg { + int callbackID; + remote_nonnull_domain dom; + remote_nonnull_string dev; + int type; + int code; + remote_string message; +}; + struct remote_domain_event_block_threshold_msg { int callbackID; remote_nonnull_domain dom; @@ -6135,5 +6144,11 @@ enum remote_procedure { * @priority: high * @acl: storage_pool:getattr */ - REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_TARGET_PATH = 391 + REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_TARGET_PATH = 391, + + /** + * @generate: none + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_ERROR = 392 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index fe163db..567f307 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2541,6 +2541,14 @@ struct remote_domain_event_block_job_2_msg { int type; int status; }; +struct remote_domain_event_block_job_error_msg { + int callbackID; + remote_nonnull_domain dom; + remote_nonnull_string dev; + int type; + int code; + remote_string message; +}; struct remote_domain_event_block_threshold_msg { int callbackID; remote_nonnull_domain dom; @@ -3269,4 +3277,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MANAGED_SAVE_DEFINE_XML = 389, REMOTE_PROC_DOMAIN_SET_LIFECYCLE_ACTION = 390, REMOTE_PROC_STORAGE_POOL_LOOKUP_BY_TARGET_PATH = 391, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_ERROR = 392, }; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 2b775fc..40d3c82 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -13286,6 +13286,28 @@ virshEventBlockThresholdPrint(virConnectPtr conn ATTRIBUTE_UNUSED, } +static void +virshEventBlockJobErrorPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *dev, + int type, + unsigned int code ATTRIBUTE_UNUSED, + const char *message, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event '%s' for domain %s: %s for %s, " + "error: %s\n"), + ((virshDomEventData *) opaque)->cb->name, + virDomainGetName(dom), + virshDomainBlockJobToString(type), + dev, + NULLSTR(message)); + virshEventPrint(opaque, &buf); +} + + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, @@ -13335,6 +13357,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, { "block-threshold", VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), }, + { "block-job-error", + VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobErrorPrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list