[libvirt] [PATCH RFC v2] lib: provide error message in new block job error event

Nikolay Shirokovskiy posted 1 patch 6 years, 5 months ago
Failed in applying to current master (apply log)
daemon/remote.c                     | 48 +++++++++++++++++++
examples/object-events/event-test.c | 20 ++++++++
include/libvirt/libvirt-domain.h    | 25 ++++++++++
src/conf/domain_event.c             | 96 +++++++++++++++++++++++++++++++++++++
src/conf/domain_event.h             | 13 +++++
src/libvirt_private.syms            |  2 +
src/qemu/qemu_blockjob.c            |  9 +++-
src/qemu/qemu_blockjob.h            |  3 +-
src/qemu/qemu_domain.h              |  1 +
src/qemu/qemu_driver.c              | 10 ++--
src/qemu/qemu_process.c             | 12 +++--
src/remote/remote_driver.c          | 34 +++++++++++++
src/remote/remote_protocol.x        | 17 ++++++-
src/remote_protocol-structs         |  9 ++++
tools/virsh-domain.c                | 24 ++++++++++
15 files changed, 313 insertions(+), 10 deletions(-)
[libvirt] [PATCH RFC v2] lib: provide error message in new block job error event
Posted by Nikolay Shirokovskiy 6 years, 5 months ago
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.

---

 The patch is applied on top of [1] patch series (not yet pushed though).

 Diff from v1 [2]:

   1. Replace block job event version 3 with block job error event.
   2. 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/msg01292.html
 [2] https://www.redhat.com/archives/libvir-list/2017-October/msg01369.html

 daemon/remote.c                     | 48 +++++++++++++++++++
 examples/object-events/event-test.c | 20 ++++++++
 include/libvirt/libvirt-domain.h    | 25 ++++++++++
 src/conf/domain_event.c             | 96 +++++++++++++++++++++++++++++++++++++
 src/conf/domain_event.h             | 13 +++++
 src/libvirt_private.syms            |  2 +
 src/qemu/qemu_blockjob.c            |  9 +++-
 src/qemu/qemu_blockjob.h            |  3 +-
 src/qemu/qemu_domain.h              |  1 +
 src/qemu/qemu_driver.c              | 10 ++--
 src/qemu/qemu_process.c             | 12 +++--
 src/remote/remote_driver.c          | 34 +++++++++++++
 src/remote/remote_protocol.x        | 17 ++++++-
 src/remote_protocol-structs         |  9 ++++
 tools/virsh-domain.c                | 24 ++++++++++
 15 files changed, 313 insertions(+), 10 deletions(-)

diff --git a/daemon/remote.c b/daemon/remote.c
index 3f7d2d3..545bf67 100644
--- a/daemon/remote.c
+++ b/daemon/remote.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/examples/object-events/event-test.c b/examples/object-events/event-test.c
index a144638..43a3878 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 4048acf..9f5c1ba 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -4363,6 +4363,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:
  *
@@ -4405,6 +4429,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 7baccd5..3a98de7 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;
 
@@ -337,6 +350,12 @@ virDomainEventsOnceInit(void)
                       sizeof(virDomainEventBlockJob),
                       virDomainEventBlockJobDispose)))
         return -1;
+    if (!(virDomainEventBlockJobErrorClass =
+          virClassNew(virDomainEventClass,
+                      "virDomainEventBlockJobError",
+                      sizeof(virDomainEventBlockJobError),
+                      virDomainEventBlockJobErrorDispose)))
+        return -1;
     if (!(virDomainEventDiskChangeClass =
           virClassNew(virDomainEventClass,
                       "virDomainEventDiskChange",
@@ -504,6 +523,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;
@@ -1061,6 +1090,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)
 {
@@ -1871,6 +1953,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 448d962..594c792 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -562,6 +562,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 0b1616a..7edc30a 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 event3 = NULL;
     const char *path;
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
     virDomainDiskDefPtr persistDisk = NULL;
@@ -123,6 +126,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver,
     path = virDomainDiskGetSource(disk);
     event = virDomainEventBlockJobNewFromObj(vm, path, type, status);
     event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status);
+    event3 = virDomainEventBlockJobErrorNewFromObj(vm, disk->dst, type, 0, error);
 
     /* If we completed a block pull or commit, then update the XML
      * to match.  */
@@ -212,6 +216,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver,
 
     qemuDomainEventQueue(driver, event);
     qemuDomainEventQueue(driver, event2);
+    qemuDomainEventQueue(driver, event3);
 
     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.h b/src/qemu/qemu_domain.h
index 735e810..99a71b3 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -457,6 +457,7 @@ struct qemuProcessEvent {
     int action;
     int status;
     void *data;
+    char *error;
 };
 
 typedef struct _qemuDomainLogContext qemuDomainLogContext;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 725b46a..c6626bb 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4715,7 +4715,8 @@ processBlockJobEvent(virQEMUDriverPtr driver,
                      virDomainObjPtr vm,
                      char *diskAlias,
                      int type,
-                     int status)
+                     int status,
+                     char *error)
 {
     virDomainDiskDefPtr disk;
 
@@ -4728,12 +4729,14 @@ 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);
  cleanup:
     VIR_FREE(diskAlias);
+    VIR_FREE(error);
 }
 
 
@@ -4815,7 +4818,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 4bfad5d..6a3d9bc 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1008,6 +1008,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
     virDomainDiskDefPtr disk;
     qemuDomainDiskPrivatePtr diskPriv;
     char *data = NULL;
+    char *errorCopy = NULL;
 
     virObjectLock(vm);
 
@@ -1018,13 +1019,14 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
         goto error;
     diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
 
+    ignore_value(VIR_STRDUP_QUIET(errorCopy, error));
+
     if (diskPriv->blockJobSync) {
         /* We have a SYNC API waiting for this event, dispatch it back */
         diskPriv->blockJobType = type;
         diskPriv->blockJobStatus = status;
         VIR_FREE(diskPriv->blockJobError);
-        if (error && VIR_STRDUP_QUIET(diskPriv->blockJobError, error) < 0)
-            VIR_WARN("Can not pass error message further: %s", error);
+        VIR_STEAL_PTR(diskPriv->blockJobError, errorCopy);
         virDomainObjBroadcast(vm);
     } else {
         /* there is no waiting SYNC API, dispatch the update to a thread */
@@ -1038,6 +1040,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
         processEvent->vm = vm;
         processEvent->action = type;
         processEvent->status = status;
+        VIR_STEAL_PTR(processEvent->error, errorCopy);
 
         virObjectRef(vm);
         if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
@@ -1047,11 +1050,14 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
     }
 
  cleanup:
+    VIR_FREE(errorCopy);
     virObjectUnlock(vm);
     return 0;
  error:
-    if (processEvent)
+    if (processEvent) {
         VIR_FREE(processEvent->data);
+        VIR_FREE(processEvent->error);
+    }
     VIR_FREE(processEvent);
     goto cleanup;
 }
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 06719bb..69d93aa 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
@@ -5610,6 +5619,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 0aed252..3aba4a3 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3095,6 +3095,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;
@@ -6120,5 +6129,11 @@ enum remote_procedure {
      * @generate: both
      * @acl: domain:write
      */
-    REMOTE_PROC_DOMAIN_SET_LIFECYCLE_ACTION = 390
+    REMOTE_PROC_DOMAIN_SET_LIFECYCLE_ACTION = 390,
+
+    /**
+     * @generate: none
+     * @acl: none
+     */
+    REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_ERROR = 391
 };
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 59b0ace..29dcfc1 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -2535,6 +2535,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;
@@ -3262,4 +3270,5 @@ enum remote_procedure {
         REMOTE_PROC_DOMAIN_MANAGED_SAVE_GET_XML_DESC = 388,
         REMOTE_PROC_DOMAIN_MANAGED_SAVE_DEFINE_XML = 389,
         REMOTE_PROC_DOMAIN_SET_LIFECYCLE_ACTION = 390,
+        REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB_3 = 391,
 };
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 1e33e82..7fec0b3 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -13253,6 +13253,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), },
@@ -13302,6 +13324,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@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list