[PATCH] add a default event handle, to passthough the new events come from qemu

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Shaohe Feng <shaohef@xxxxxxxxxxxxxxxxxx>

Basically, this feature can go along with qemu monitor passthrough.
That way, if we use new commands in the monitor that generate new events, we want
 some way to receive those new events too.

In order to test this patch, see the attached python test case.  When domains are started,
it will be able to catch RESUME events.

Signed-off-by: Shaohe Feng <shaohef@xxxxxxxxxxxxxxxxxx>
---
 daemon/remote.c                       |   34 ++++++++++++++++++++++
 include/libvirt/libvirt.h.in          |   14 +++++++++
 python/libvirt-override-virConnect.py |   12 ++++++++
 python/libvirt-override.c             |   50 +++++++++++++++++++++++++++++++++
 src/conf/domain_event.c               |   46 ++++++++++++++++++++++++++++++
 src/conf/domain_event.h               |    5 +++
 src/libvirt_private.syms              |    2 +
 src/qemu/qemu_monitor.c               |    9 ++++++
 src/qemu/qemu_monitor.h               |    6 ++++
 src/qemu/qemu_monitor_json.c          |   31 ++++++++++++++++++++
 src/qemu/qemu_process.c               |   23 +++++++++++++++
 src/remote/remote_driver.c            |   31 ++++++++++++++++++++
 src/remote/remote_protocol.x          |    8 ++++-
 src/remote_protocol-structs           |    5 +++
 14 files changed, 275 insertions(+), 1 deletions(-)

diff --git a/daemon/remote.c b/daemon/remote.c
index 245d41c..ef7d513 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -426,6 +426,38 @@ mem_error:
     return -1;
 }
 
+static int remoteRelayDomainEventDefault(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                         virDomainPtr dom,
+                                         const char *rawEvent,
+                                         void *opaque)
+{
+    virNetServerClientPtr client = opaque;
+    remote_domain_event_default_event_msg data;
+
+    if (!client)
+        return -1;
+
+    VIR_DEBUG("Relaying domain default event event %s %d %s",
+              dom->name, dom->id, rawEvent);
+
+    /* build return data */
+    memset(&data, 0, sizeof data);
+    data.rawEvent = (char*)strdup(rawEvent);
+    if (data.rawEvent == NULL)
+        goto mem_error;
+    make_nonnull_domain(&data.dom, dom);
+    remoteDispatchDomainEventSend(client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT,
+                                  (xdrproc_t)xdr_remote_domain_event_default_event_msg, &data);
+
+    return 0;
+
+mem_error:
+    virReportOOMError();
+    VIR_FREE(data.rawEvent);
+    return -1;
+}
+
 
 static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED,
                                               virDomainPtr dom,
@@ -461,6 +493,8 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDefault),
+
 };
 
 verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 07617be..5ccf8c7 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2975,6 +2975,19 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn,
                                                       int type,
                                                       int status,
                                                       void *opaque);
+/**
+ * virConnectDomainEventDefaultCallback:
+ * @conn: connection object
+ * @dom: domain on which the event occurred
+ * @rawEvent: the content of the unknow or un-implementation event
+ *
+ * The callback signature to use when registering for an event of type
+ * VIR_DOMAIN_EVENT_ID_DEFAULT with virConnectDomainEventRegisterAny()
+ */
+typedef void (*virConnectDomainEventDefaultCallback)(virConnectPtr conn,
+                                                     virDomainPtr dom,
+                                                     const char *rawEvent,
+                                                     void *opaque);
 
 /**
  * VIR_DOMAIN_EVENT_CALLBACK:
@@ -2995,6 +3008,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_DEFAULT = 9,         /* virConnectDomainEventDefaultCallback */
 
     /*
      * 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..f00cbb9 100644
--- a/python/libvirt-override-virConnect.py
+++ b/python/libvirt-override-virConnect.py
@@ -125,6 +125,18 @@
         except AttributeError:
             pass
 
+    def dispatchDomainEventDefaultCallback(self, dom, path, cbData):
+        """Dispatches events to python user Default event callbacks
+        """
+        try:
+            cb = cbData["cb"]
+            opaque = cbData["opaque"]
+
+            cb(self, virDomain(self, _obj=dom), path, opaque)
+            return 0
+        except AttributeError:
+            pass
+
     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 d65423d..c674390 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -4329,6 +4329,53 @@ libvirt_virConnectDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSE
     return ret;
 }
 
+static int
+libvirt_virConnectDomainEventDefaultCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                             virDomainPtr dom,
+                                             const char *rawEvent,
+                                             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*)"dispatchDomainEventDefaultCallback",
+                                    (char*)"OsO",
+                                    pyobj_dom, rawEvent, pyobj_cbData);
+
+    Py_DECREF(pyobj_cbData);
+    Py_DECREF(pyobj_dom);
+
+    if (!pyobj_ret) {
+#if DEBUG_ERROR
+        printf("%s - ret:%p\n", __FUNCTION__, pyobj_ret);
+#endif
+        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)
@@ -4386,6 +4433,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_DEFAULT:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventDefaultCallback);
+        break;
     }
 
     if (!cb) {
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c
index 3189346..04f44fb 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -88,6 +88,9 @@ struct _virDomainEvent {
             int type;
             int status;
         } blockJob;
+        struct {
+            char *rawEvent;
+        }defaultEvent;
     } data;
 };
 
@@ -509,6 +512,9 @@ void virDomainEventFree(virDomainEventPtr event)
     case VIR_DOMAIN_EVENT_ID_BLOCK_JOB:
         VIR_FREE(event->data.blockJob.path);
         break;
+    case VIR_DOMAIN_EVENT_ID_DEFAULT:
+        VIR_FREE(event->data.defaultEvent.rawEvent);
+        break;
     }
 
     VIR_FREE(event->dom.name);
@@ -923,6 +929,40 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom,
                                      path, type, status);
 }
 
+static virDomainEventPtr
+virDomainEventDefaultNew(int id, const char *name, unsigned char *uuid,
+                         const char *rawEvent)
+{
+    virDomainEventPtr ev =
+        virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_DEFAULT,
+                                  id, name, uuid);
+    if (ev) {
+        if (!(ev->data.defaultEvent.rawEvent = strdup(rawEvent))) {
+            virReportOOMError();
+            VIR_FREE(ev->dom.name);
+            VIR_FREE(ev);
+            return NULL;
+        }
+    }
+
+    return ev;
+}
+
+virDomainEventPtr virDomainEventDefaultNewFromObj(virDomainObjPtr obj,
+                                                  const char *rawEvent)
+{
+
+    return virDomainEventDefaultNew(obj->def->id, obj->def->name,
+                                    obj->def->uuid, rawEvent);
+}
+
+virDomainEventPtr virDomainEventDefaultNewFromDom(virDomainPtr dom,
+                                                  const char *rawEvent)
+{
+    return virDomainEventDefaultNew(dom->id, dom->name, dom->uuid,
+                                    rawEvent);
+}
+
 virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom)
 {
     virDomainEventPtr ev =
@@ -1083,6 +1123,12 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn,
                                                     cbopaque);
         break;
 
+    case VIR_DOMAIN_EVENT_ID_DEFAULT:
+        ((virConnectDomainEventDefaultCallback)cb)(conn, dom,
+                                                   event->data.defaultEvent.rawEvent,
+                                                   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 b06be16..401f781 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -178,6 +178,11 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom,
                                                     int type,
                                                     int status);
 
+virDomainEventPtr virDomainEventDefaultNewFromObj(virDomainObjPtr obj,
+                                                  const char *rawEvent);
+virDomainEventPtr virDomainEventDefaultNewFromDom(virDomainPtr dom,
+                                                  const char *rawEvent);
+
 int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue,
                             virDomainEventPtr event);
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1ac486f..1dcfc3e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -457,6 +457,8 @@ virDomainWatchdogModelTypeToString;
 # domain_event.h
 virDomainEventBlockJobNewFromObj;
 virDomainEventBlockJobNewFromDom;
+virDomainEventDefaultNewFromObj;
+virDomainEventDefaultNewFromDom;
 virDomainEventCallbackListAdd;
 virDomainEventCallbackListAddID;
 virDomainEventCallbackListCount;
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index c9dd69e..a2b4036 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -976,6 +976,15 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
     return ret;
 }
 
+int qemuMonitorEmitDefaultEvent(qemuMonitorPtr mon,
+                                const char *rawEvent)
+{
+    int ret = -1;
+    VIR_DEBUG("mon=%p", mon);
+    QEMU_MONITOR_CALLBACK(mon, ret, domainDefaultEvent, mon->vm,
+                          rawEvent);
+    return ret;
+}
 
 
 int qemuMonitorSetCapabilities(qemuMonitorPtr mon)
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 3ec78ad..23a03e5 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -123,6 +123,9 @@ struct _qemuMonitorCallbacks {
                           const char *diskAlias,
                           int type,
                           int status);
+    int (*domainDefaultEvent)(qemuMonitorPtr mon,
+                              virDomainObjPtr vm,
+                              const char *rawEvent)
 };
 
 
@@ -194,6 +197,9 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
                             int type,
                             int status);
 
+int qemuMonitorEmitDefaultEvent(qemuMonitorPtr mon,
+                                const char *rawEvent);
+
 
 
 int qemuMonitorStartCPUs(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 3d383c8..dab03cd 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -58,6 +58,7 @@ static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr
 static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleDefaultEvent(qemuMonitorPtr mon, virJSONValuePtr data);
 
 struct {
     const char *type;
@@ -74,6 +75,7 @@ struct {
     { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
     { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
     { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, },
+    { "DEFAULT_UNKNOW_EVENT", qemuMonitorJSONHandleDefaultEvent, },
 };
 
 
@@ -83,6 +85,7 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon,
 {
     const char *type;
     int i;
+    int findEventFlag = -1;
     VIR_DEBUG("mon=%p obj=%p", mon, obj);
 
     type = virJSONValueObjectGetString(obj, "event");
@@ -98,9 +101,24 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon,
             VIR_DEBUG("handle %s handler=%p data=%p", type,
                       eventHandlers[i].handler, data);
             (eventHandlers[i].handler)(mon, data);
+            findEventFlag = 0;
             break;
         }
     }
+    if (findEventFlag != 0) {
+        if (!STREQ(eventHandlers[ARRAY_CARDINALITY(eventHandlers)-1].type, "DEFAULT_UNKNOW_EVENT")) {
+            VIR_ERROR("the last element is not the default event handler");
+        }
+        else {
+            char *event = NULL;
+            event = virJSONValueToString(obj);
+            if (event != NULL){
+                VIR_DEBUG("Unknow event,call default event handler %s",event);
+                free(event);
+            }
+            (eventHandlers[ARRAY_CARDINALITY(eventHandlers)-1].handler)(mon, obj);
+        }
+    }
     return 0;
 }
 
@@ -720,6 +738,19 @@ out:
 }
 
 
+static void qemuMonitorJSONHandleDefaultEvent(qemuMonitorPtr mon, virJSONValuePtr data)
+{
+    char *defaultEventStr = NULL;
+    defaultEventStr = virJSONValueToString(data);
+    if (defaultEventStr == NULL){
+        VIR_ERROR("Can not get string form JSONValue");
+        return;
+    }
+    qemuMonitorEmitDefaultEvent(mon, defaultEventStr);
+    free(defaultEventStr);
+}
+
+
 int
 qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
                                   const char *cmd_str,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index a7fe86c..46881eb 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -713,6 +713,28 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
 }
 
 static int
+qemuProcessHandleDefaultEvent(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+                              virDomainObjPtr vm,
+                              const char *rawEvent)
+{
+    struct qemud_driver *driver = qemu_driver;
+    virDomainEventPtr event = NULL;
+
+    virDomainObjLock(vm);
+    event = virDomainEventDefaultNewFromObj(vm, rawEvent);
+
+    virDomainObjUnlock(vm);
+
+    if (event) {
+        qemuDriverLock(driver);
+        qemuDomainEventQueue(driver, event);
+        qemuDriverUnlock(driver);
+    }
+
+    return 0;
+}
+
+static int
 qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
                           virDomainObjPtr vm,
                           int phase,
@@ -829,6 +851,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
     .domainIOError = qemuProcessHandleIOError,
     .domainGraphics = qemuProcessHandleGraphics,
     .domainBlockJob = qemuProcessHandleBlockJob,
+    .domainDefaultEvent = qemuProcessHandleDefaultEvent,
 };
 
 static int
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 2b2f41e..9648661 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
+remoteDomainBuildEventDefaultEvent(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_DEFAULT_EVENT,
+      remoteDomainBuildEventDefaultEvent,
+      sizeof(remote_domain_event_default_event_msg),
+      (xdrproc_t)xdr_remote_domain_event_default_event_msg },
 };
 
 enum virDrvOpenRemoteFlags {
@@ -3220,6 +3229,28 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
 }
 
 static void
+remoteDomainBuildEventDefaultEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+                                   virNetClientPtr client ATTRIBUTE_UNUSED,
+                                   void *evdata, void *opaque)
+{
+    virConnectPtr conn = opaque;
+    struct private_data *priv = conn->privateData;
+    remote_domain_event_default_event_msg *msg = evdata;
+    virDomainPtr dom;
+    virDomainEventPtr event = NULL;
+
+    dom = get_nonnull_domain(conn, msg->dom);
+    if (!dom)
+        return;
+
+    event = virDomainEventDefaultNewFromDom(dom, msg->rawEvent);
+
+    virDomainFree(dom);
+
+    remoteDomainEventQueue(priv, event);
+}
+
+static void
 remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                virNetClientPtr client ATTRIBUTE_UNUSED,
                                void *evdata, void *opaque)
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index c8a92fd..81c6a4e 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2010,6 +2010,11 @@ struct remote_domain_event_block_job_msg {
     int status;
 };
 
+struct remote_domain_event_default_event_msg {
+    remote_nonnull_domain dom;
+    remote_nonnull_string rawEvent;
+};
+
 struct remote_domain_managed_save_args {
     remote_nonnull_domain dom;
     unsigned int flags;
@@ -2525,7 +2530,8 @@ enum remote_procedure {
     REMOTE_PROC_DOMAIN_MIGRATE_GET_MAX_SPEED = 242, /* autogen autogen */
     REMOTE_PROC_DOMAIN_BLOCK_STATS_FLAGS = 243, /* skipgen skipgen */
     REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244, /* autogen autogen */
-    REMOTE_PROC_DOMAIN_RESET = 245 /* autogen autogen */
+    REMOTE_PROC_DOMAIN_RESET = 245, /* autogen autogen */
+    REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT = 246 /* 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 69175cc..7d0d0d4 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -1509,6 +1509,10 @@ struct remote_domain_event_block_job_msg {
         int                        type;
         int                        status;
 };
+struct remote_domain_event_default_event_msg {
+        remote_nonnull_domain      dom;
+        remote_nonnull_string      rawEvent;
+};
 struct remote_domain_managed_save_args {
         remote_nonnull_domain      dom;
         u_int                      flags;
@@ -1971,4 +1975,5 @@ enum remote_procedure {
         REMOTE_PROC_DOMAIN_BLOCK_STATS_FLAGS = 243,
         REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244,
         REMOTE_PROC_DOMAIN_RESET = 245,
+        REMOTE_PROC_DOMAIN_EVENT_DEFAULT_EVENT = 246,
 };
-- 
1.7.6

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]