If a disk source gets dropped because it is not accessible, mgmt application might want to be informed about this. Therefore we need to emit an event. The event presented in this patch is however a bit superset of what written above. The reason is simple: an intention to be easily expanded, e.g. on 'user ejected disk in guest' events. Therefore, callback gets target string (which should be unique among a domain) and reason (an integer); It can't get alias, as some disk changes (esp. dropping source caused by on_missing feature) might happen on inactive domain where no disks aliases exists. --- daemon/remote.c | 37 ++++++++++++++ examples/domain-events/events-c/event-test.c | 27 ++++++++++- examples/domain-events/events-python/event-test.py | 4 ++ include/libvirt/libvirt.h.in | 28 +++++++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 51 ++++++++++++++++++++ src/conf/domain_event.c | 50 +++++++++++++++++++ src/conf/domain_event.h | 7 +++ src/libvirt_private.syms | 2 + src/qemu/qemu_domain.c | 7 +++ src/remote/remote_driver.c | 34 +++++++++++++ src/remote/remote_protocol.x | 9 +++- src/remote_protocol-structs | 5 ++ 13 files changed, 268 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 550bed4..4d63288 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -451,6 +451,42 @@ static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSE } +static int remoteRelayDomainEventDiskEject(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *target, + int reason, + void *opaque) +{ + virNetServerClientPtr client = opaque; + remote_domain_event_disk_eject_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain %s %d disk eject %s %d", + dom->name, dom->id, target, reason); + + /* build return data */ + memset(&data, 0, sizeof data); + data.target = strdup(target); + if (!data.target) + goto mem_error; + data.reason = reason; + + make_nonnull_domain(&data.dom, dom); + + remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_DISK_EJECT, + (xdrproc_t)xdr_remote_domain_event_disk_eject_msg, &data); + + return 0; + +mem_error: + virReportOOMError(); + return -1; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -461,6 +497,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskEject), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 6a3ed26..08e371a 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -285,6 +285,23 @@ static int myDomainEventControlErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED } +const char *diskEjectReasonStrings[] = { + "on_missing", /* 0 */ + /* add new reason here */ +}; +static int myDomainEventDiskEjectCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *target, + int reason, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) disk eject target: %s reason: %s\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), + target, diskEjectReasonStrings[reason]); + return 0; +} + + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -319,6 +336,7 @@ int main(int argc, char **argv) int callback6ret = -1; int callback7ret = -1; int callback8ret = -1; + int callback9ret = -1; struct sigaction action_stop; memset(&action_stop, 0, sizeof action_stop); @@ -382,6 +400,11 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventControlErrorCallback), strdup("callback control error"), myFreeFunc); + callback9ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_DISK_EJECT, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventDiskEjectCallback), + strdup("disk eject"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && @@ -389,7 +412,8 @@ int main(int argc, char **argv) (callback4ret != -1) && (callback5ret != -1) && (callback6ret != -1) && - (callback7ret != -1)) { + (callback7ret != -1) && + (callback9ret != -1)) { while (run) { if (virEventRunDefaultImpl() < 0) { virErrorPtr err = virGetLastError(); @@ -406,6 +430,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback5ret); virConnectDomainEventDeregisterAny(dconn, callback6ret); virConnectDomainEventDeregisterAny(dconn, callback7ret); + virConnectDomainEventDeregisterAny(dconn, callback9ret); if (callback8ret != -1) virConnectDomainEventDeregisterAny(dconn, callback8ret); } diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 4df9b98..b0ad603 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -469,6 +469,9 @@ def myDomainEventIOErrorCallback(conn, dom, srcpath, devalias, action, opaque): def myDomainEventGraphicsCallback(conn, dom, phase, localAddr, remoteAddr, authScheme, subject, opaque): print "myDomainEventGraphicsCallback: Domain %s(%s) %d %s" % (dom.name(), dom.ID(), phase, authScheme) +def myDomainEventDiskEjectCallback(conn, dom, target, reason, opaque): + print "myDomainEventDiskEjectCallback: Domain %s(%s) disk eject target: %s reason: %s" % ( + dom.name(), dom.ID(), target, reason) def usage(out=sys.stderr): print >>out, "usage: "+os.path.basename(sys.argv[0])+" [-hdl] [uri]" print >>out, " uri will default to qemu:///system" @@ -526,6 +529,7 @@ def main(): vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR, myDomainEventIOErrorCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, myDomainEventWatchdogCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, myDomainEventGraphicsCallback, None) + vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_DISK_EJECT, myDomainEventDiskEjectCallback, None) # The rest of your app would go here normally, but for sake # of demo we'll just go to sleep. The other option is to diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 361881a..d6d928a 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2996,6 +2996,33 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventDiskEjectReason: + * + * The reason describing why this callback is called + */ +typedef enum { + VIR_DOMAIN_DISK_EJECT_ON_MISSING = 0, +} virConnectDomainEventDiskEjectReason; + +/** + * virConnectDomainEventDiskEjectCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @target: target which changed status + * @reason: reason why this callback was called; any of + * virConnectDomainEventDiskEjectReason + * @opaque: application specified data + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_IO_ERROR with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventDiskEjectCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *target, + int reason, + void *opaque); + +/** * VIR_DOMAIN_EVENT_CALLBACK: * * Used to cast the event specific callback into the generic one @@ -3014,6 +3041,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON = 6, /* virConnectDomainEventIOErrorReasonCallback */ VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ + VIR_DOMAIN_EVENT_ID_DISK_EJECT = 9, /* virConnectDomainEventDiskEjectCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 65b5342..e945361 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -125,6 +125,15 @@ except AttributeError: pass + def _dispatchDomainEventDiskEjectCallback(self, dom, target, reason, cbData): + """Dispatches event to python user domain diskEject event callbacks + """ + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, virDomain(self, _obj=dom), target, reason, opaque) + return 0; + def domainEventDeregisterAny(self, callbackID): """Removes a Domain Event Callback. De-registering for a domain callback will disable delivery of this event type """ diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 523c03b..46da472 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -4374,6 +4374,54 @@ libvirt_virConnectDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSE return ret; } +static int +libvirt_virConnectDomainEventDiskEjectCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *target, + int reason, + void *opaque) +{ + PyObject *pyobj_cbData = (PyObject*)opaque; + PyObject *pyobj_dom; + PyObject *pyobj_ret; + PyObject *pyobj_conn; + PyObject *dictKey; + int ret = -1; + + LIBVIRT_ENSURE_THREAD_STATE; + /* Create a python instance of this virDomainPtr */ + virDomainRef(dom); + + pyobj_dom = libvirt_virDomainPtrWrap(dom); + Py_INCREF(pyobj_cbData); + + dictKey = libvirt_constcharPtrWrap("conn"); + pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey); + Py_DECREF(dictKey); + + /* Call the Callback Dispatcher */ + pyobj_ret = PyObject_CallMethod(pyobj_conn, + (char*)"_dispatchDomainEventDiskEjectCallback", + (char*)"OsiO", + pyobj_dom, + target, reason, + pyobj_cbData); + + Py_DECREF(pyobj_cbData); + Py_DECREF(pyobj_dom); + + if(!pyobj_ret) { + DEBUG("%s - ret:%p\n", __FUNCTION__, pyobj_ret); + PyErr_Print(); + } else { + Py_DECREF(pyobj_ret); + ret = 0; + } + + LIBVIRT_RELEASE_THREAD_STATE; + return ret; +} + static PyObject * libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) @@ -4431,6 +4479,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBlockJobCallback); break; + case VIR_DOMAIN_EVENT_ID_DISK_EJECT: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventDiskEjectCallback); + break; } if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index f712c34..da6206a 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -88,6 +88,10 @@ struct _virDomainEvent { int type; int status; } blockJob; + struct { + char *target; + int reason; + } diskEject; } data; }; @@ -509,6 +513,10 @@ void virDomainEventFree(virDomainEventPtr event) case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: VIR_FREE(event->data.blockJob.path); break; + + case VIR_DOMAIN_EVENT_ID_DISK_EJECT: + VIR_FREE(event->data.diskEject.target); + break; } VIR_FREE(event->dom.name); @@ -961,6 +969,41 @@ virDomainEventPtr virDomainEventControlErrorNewFromObj(virDomainObjPtr obj) return ev; } +static virDomainEventPtr +virDomainEventDiskEjectNew(int id, const char *name, unsigned char *uuid, + const char *target, int reason) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_DISK_EJECT, + id, name, uuid); + + if (ev) { + if (!(ev->data.diskEject.target = strdup(target))) { + virReportOOMError(); + virDomainEventFree(ev); + return NULL; + } + ev->data.diskEject.reason = reason; + } + + return ev; +} + +virDomainEventPtr virDomainEventDiskEjectNewFromObj(virDomainObjPtr obj, + const char *target, + int reason) +{ + return virDomainEventDiskEjectNew(obj->def->id, obj->def->name, + obj->def->uuid, target, reason); +} + +virDomainEventPtr virDomainEventDiskEjectNewFromDom(virDomainPtr dom, + const char *target, + int reason) +{ + return virDomainEventDiskEjectNew(dom->id, dom->name, dom->uuid, + target, reason); +} /** * virDomainEventQueuePop: @@ -1104,6 +1147,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_DISK_EJECT: + ((virConnectDomainEventDiskEjectCallback)cb)(conn, dom, + event->data.diskEject.target, + event->data.diskEject.reason, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 08930ed..add91c2 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -179,6 +179,13 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, int type, int status); +virDomainEventPtr virDomainEventDiskEjectNewFromObj(virDomainObjPtr obj, + const char *target, + int reason); +virDomainEventPtr virDomainEventDiskEjectNewFromDom(virDomainPtr dom, + const char *target, + int reason); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ae18a0d..026ef48 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -478,6 +478,8 @@ virDomainEventCallbackListRemoveConn; virDomainEventCallbackListRemoveID; virDomainEventControlErrorNewFromDom; virDomainEventControlErrorNewFromObj; +virDomainEventDiskEjectNewFromDom; +virDomainEventDiskEjectNewFromObj; virDomainEventDispatch; virDomainEventDispatchDefaultFunc; virDomainEventFree; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index ffcee29..5547188 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -35,6 +35,7 @@ #include "ignore-value.h" #include "uuid.h" #include "virfile.h" +#include "domain_event.h" #include <sys/time.h> #include <fcntl.h> @@ -1625,6 +1626,7 @@ qemuDomainCheckDiskPresence(struct qemud_driver *driver, int accessRet; virDomainDiskDefPtr disk; char uuid[VIR_UUID_STRING_BUFLEN] ATTRIBUTE_UNUSED; + virDomainEventPtr event = NULL; virUUIDFormat(vm->def->uuid, uuid); @@ -1672,6 +1674,11 @@ qemuDomainCheckDiskPresence(struct qemud_driver *driver, "due to not accessible source '%s'", disk->dst, vm->def->name, uuid, disk->src); + event = virDomainEventDiskEjectNewFromObj(vm, disk->dst, + VIR_DOMAIN_DISK_EJECT_ON_MISSING); + if (event) + qemuDomainEventQueue(driver, event); + VIR_FREE(disk->src); } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1dea327..a4db884 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -228,6 +228,11 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventDiskEject(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, remoteDomainBuildEventRTCChange, @@ -265,6 +270,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventBlockJob, sizeof(remote_domain_event_block_job_msg), (xdrproc_t)xdr_remote_domain_event_block_job_msg }, + { REMOTE_PROC_DOMAIN_EVENT_DISK_EJECT, + remoteDomainBuildEventDiskEject, + sizeof(remote_domain_event_disk_eject_msg), + (xdrproc_t)xdr_remote_domain_event_disk_eject_msg }, }; enum virDrvOpenRemoteFlags { @@ -3327,6 +3336,31 @@ remoteDomainBuildEventControlError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, } +static void +remoteDomainBuildEventDiskEject(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_disk_eject_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventDiskEjectNewFromDom(dom, + msg->target, + msg->reason); + + virDomainFree(dom); + + remoteDomainEventQueue(priv, event); +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen(virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f95253e..1fb31d6 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2010,6 +2010,12 @@ struct remote_domain_event_block_job_msg { int status; }; +struct remote_domain_event_disk_eject_msg { + remote_nonnull_domain dom; + remote_nonnull_string target; + int reason; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; @@ -2546,7 +2552,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_RESET = 245, /* autogen autogen */ REMOTE_PROC_DOMAIN_SNAPSHOT_NUM_CHILDREN = 246, /* autogen autogen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247 /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247, /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_EVENT_DISK_EJECT = 248 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 7894441..6ac514f 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1509,6 +1509,11 @@ struct remote_domain_event_block_job_msg { int type; int status; }; +struct remote_domain_event_disk_eject_msg { + remote_nonnull_domain dom; + remote_nonnull_string target; + int reason; +}; struct remote_domain_managed_save_args { remote_nonnull_domain dom; u_int flags; -- 1.7.3.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list