/* * Copyright (C) 2014 Red Hat, Inc. * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. * * 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 * . */ #include #include #include "testutils.h" #define VIR_FROM_THIS VIR_FROM_NONE static const char domainDef[] = "" " test-domain" " 77a6fc12-07b5-9415-8abb-a803613f2a40" " 8388608" " 2097152" " 2" " " " hvm" " " ""; static const char networkDef[] = "\n" " test\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; static const char storagePoolDef[] = "\n" " P\n" " \n" " /target-path\n" " \n" "\n"; static const char nodeDeviceDef[] = "\n" " scsi_host1\n" " \n" " \n" " 1000000023452345\n" " 2000000023452345\n" " \n" " \n" "\n"; typedef struct { int startEvents; int stopEvents; int defineEvents; int undefineEvents; int unexpectedEvents; int createdEvents; int deletedEvents; } lifecycleEventCounter; typedef struct { virConnectPtr conn; virNetworkPtr net; virStoragePoolPtr pool; virNodeDevicePtr dev; } objecteventTest; static int domainLifecycleCb(virConnectPtr conn G_GNUC_UNUSED, virDomainPtr dom G_GNUC_UNUSED, int event, int detail G_GNUC_UNUSED, void *opaque) { lifecycleEventCounter *counter = opaque; switch (event) { case VIR_DOMAIN_EVENT_STARTED: counter->startEvents++; break; case VIR_DOMAIN_EVENT_STOPPED: counter->stopEvents++; break; case VIR_DOMAIN_EVENT_DEFINED: counter->defineEvents++; break; case VIR_DOMAIN_EVENT_UNDEFINED: counter->undefineEvents++; break; default: /* Ignore other events */ break; } return 0; } static void networkLifecycleCb(virConnectPtr conn G_GNUC_UNUSED, virNetworkPtr net G_GNUC_UNUSED, int event, int detail G_GNUC_UNUSED, void* opaque) { lifecycleEventCounter *counter = opaque; if (event == VIR_NETWORK_EVENT_STARTED) counter->startEvents++; else if (event == VIR_NETWORK_EVENT_STOPPED) counter->stopEvents++; else if (event == VIR_NETWORK_EVENT_DEFINED) counter->defineEvents++; else if (event == VIR_NETWORK_EVENT_UNDEFINED) counter->undefineEvents++; } static void storagePoolLifecycleCb(virConnectPtr conn G_GNUC_UNUSED, virStoragePoolPtr pool G_GNUC_UNUSED, int event, int detail G_GNUC_UNUSED, void* opaque) { lifecycleEventCounter *counter = opaque; if (event == VIR_STORAGE_POOL_EVENT_STARTED) counter->startEvents++; else if (event == VIR_STORAGE_POOL_EVENT_STOPPED) counter->stopEvents++; else if (event == VIR_STORAGE_POOL_EVENT_DEFINED) counter->defineEvents++; else if (event == VIR_STORAGE_POOL_EVENT_UNDEFINED) counter->undefineEvents++; else if (event == VIR_STORAGE_POOL_EVENT_CREATED) counter->createdEvents++; else if (event == VIR_STORAGE_POOL_EVENT_DELETED) counter->deletedEvents++; } static void storagePoolRefreshCb(virConnectPtr conn G_GNUC_UNUSED, virStoragePoolPtr pool G_GNUC_UNUSED, void* opaque) { int *counter = opaque; (*counter)++; } static void nodeDeviceLifecycleCb(virConnectPtr conn G_GNUC_UNUSED, virNodeDevicePtr dev G_GNUC_UNUSED, int event, int detail G_GNUC_UNUSED, void* opaque) { lifecycleEventCounter *counter = opaque; if (event == VIR_NODE_DEVICE_EVENT_CREATED) counter->createdEvents++; else if (event == VIR_NODE_DEVICE_EVENT_DELETED) counter->deletedEvents++; } static int testDomainCreateXMLOld(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virDomainPtr dom = NULL; int ret = -1; bool registered = false; if (virConnectDomainEventRegister(test->conn, domainLifecycleCb, &counter, NULL) != 0) goto cleanup; registered = true; dom = virDomainCreateXML(test->conn, domainDef, 0); if (dom == NULL || virEventRunDefaultImpl() < 0) goto cleanup; if (counter.startEvents != 1 || counter.unexpectedEvents > 0) goto cleanup; if (virConnectDomainEventDeregister(test->conn, domainLifecycleCb) != 0) goto cleanup; registered = false; ret = 0; cleanup: if (registered) virConnectDomainEventDeregister(test->conn, domainLifecycleCb); if (dom) { virDomainDestroy(dom); virDomainFree(dom); } return ret; } static int testDomainCreateXMLNew(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int eventId = VIR_DOMAIN_EVENT_ID_LIFECYCLE; virDomainPtr dom = NULL; int id; int ret = -1; id = virConnectDomainEventRegisterAny(test->conn, NULL, eventId, VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), &counter, NULL); if (id < 0) goto cleanup; dom = virDomainCreateXML(test->conn, domainDef, 0); if (dom == NULL || virEventRunDefaultImpl() < 0) goto cleanup; if (counter.startEvents != 1 || counter.unexpectedEvents > 0) goto cleanup; if (virConnectDomainEventDeregisterAny(test->conn, id) != 0) goto cleanup; id = -1; ret = 0; cleanup: if (id >= 0) virConnectDomainEventDeregisterAny(test->conn, id); if (dom) { virDomainDestroy(dom); virDomainFree(dom); } return ret; } static int testDomainCreateXMLMixed(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virDomainPtr dom; int ret = -1; int id1 = -1; int id2 = -1; bool registered = false; /* Fun with mixing old and new API, also with global and * per-domain. Handler should be fired three times, once for each * registration. */ dom = virDomainDefineXML(test->conn, domainDef); if (dom == NULL) goto cleanup; id1 = virConnectDomainEventRegisterAny(test->conn, dom, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), &counter, NULL); if (id1 < 0) goto cleanup; if (virConnectDomainEventRegister(test->conn, domainLifecycleCb, &counter, NULL) != 0) goto cleanup; registered = true; id2 = virConnectDomainEventRegisterAny(test->conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), &counter, NULL); if (id2 < 0) goto cleanup; virDomainUndefine(dom); virDomainDestroy(dom); virDomainFree(dom); dom = virDomainCreateXML(test->conn, domainDef, 0); if (dom == NULL || virEventRunDefaultImpl() < 0) goto cleanup; if (counter.startEvents != 3 || counter.unexpectedEvents > 0) goto cleanup; if (virConnectDomainEventDeregister(test->conn, domainLifecycleCb) != 0) goto cleanup; registered = false; if (virConnectDomainEventDeregisterAny(test->conn, id1) != 0) goto cleanup; id1 = -1; if (virConnectDomainEventDeregisterAny(test->conn, id2) != 0) goto cleanup; id2 = -1; ret = 0; cleanup: if (id1 >= 0) virConnectDomainEventDeregisterAny(test->conn, id1); if (id2 >= 0) virConnectDomainEventDeregisterAny(test->conn, id2); if (registered) virConnectDomainEventDeregister(test->conn, domainLifecycleCb); if (dom != NULL) { virDomainUndefine(dom); virDomainDestroy(dom); virDomainFree(dom); } return ret; } static int testDomainDefine(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int eventId = VIR_DOMAIN_EVENT_ID_LIFECYCLE; virDomainPtr dom = NULL; int id; int ret = -1; id = virConnectDomainEventRegisterAny(test->conn, NULL, eventId, VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), &counter, NULL); /* Make sure the define event is triggered */ dom = virDomainDefineXML(test->conn, domainDef); if (dom == NULL || virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.defineEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } /* Make sure the undefine event is triggered */ virDomainUndefine(dom); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.undefineEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectDomainEventDeregisterAny(test->conn, id); if (dom != NULL) virDomainFree(dom); return ret; } static int testDomainStartStopEvent(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int eventId = VIR_DOMAIN_EVENT_ID_LIFECYCLE; int id; int ret = -1; virDomainPtr dom; virConnectPtr conn2 = NULL; virDomainPtr dom2 = NULL; dom = virDomainLookupByName(test->conn, "test"); if (dom == NULL) return -1; id = virConnectDomainEventRegisterAny(test->conn, dom, eventId, VIR_DOMAIN_EVENT_CALLBACK(&domainLifecycleCb), &counter, NULL); /* Test domain is started */ virDomainDestroy(dom); if (virDomainCreate(dom) < 0) goto cleanup; if (virEventRunDefaultImpl() < 0) goto cleanup; if (counter.startEvents != 1 || counter.stopEvents != 1 || counter.unexpectedEvents > 0) goto cleanup; /* Repeat the test, but this time, trigger the events via an * alternate connection. */ if (!(conn2 = virConnectOpen("test:///default"))) goto cleanup; if (!(dom2 = virDomainLookupByName(conn2, "test"))) goto cleanup; if (virDomainDestroy(dom2) < 0) goto cleanup; if (virDomainCreate(dom2) < 0) goto cleanup; if (virEventRunDefaultImpl() < 0) goto cleanup; if (counter.startEvents != 2 || counter.stopEvents != 2 || counter.unexpectedEvents > 0) goto cleanup; ret = 0; cleanup: virConnectDomainEventDeregisterAny(test->conn, id); virDomainFree(dom); if (dom2) virDomainFree(dom2); if (conn2) virConnectClose(conn2); return ret; } static int testNetworkCreateXML(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virNetworkPtr net; int id; int ret = -1; id = virConnectNetworkEventRegisterAny(test->conn, NULL, VIR_NETWORK_EVENT_ID_LIFECYCLE, VIR_NETWORK_EVENT_CALLBACK(&networkLifecycleCb), &counter, NULL); net = virNetworkCreateXML(test->conn, networkDef); if (!net || virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.startEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectNetworkEventDeregisterAny(test->conn, id); if (net) { virNetworkDestroy(net); virNetworkFree(net); } return ret; } static int testNetworkDefine(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virNetworkPtr net; int id; int ret = -1; id = virConnectNetworkEventRegisterAny(test->conn, NULL, VIR_NETWORK_EVENT_ID_LIFECYCLE, VIR_NETWORK_EVENT_CALLBACK(&networkLifecycleCb), &counter, NULL); /* Make sure the define event is triggered */ net = virNetworkDefineXML(test->conn, networkDef); if (!net || virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.defineEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } /* Make sure the undefine event is triggered */ virNetworkUndefine(net); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.undefineEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectNetworkEventDeregisterAny(test->conn, id); if (net) virNetworkFree(net); return ret; } static int testNetworkStartStopEvent(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int id; int ret = -1; if (!test->net) return -1; id = virConnectNetworkEventRegisterAny(test->conn, test->net, VIR_NETWORK_EVENT_ID_LIFECYCLE, VIR_NETWORK_EVENT_CALLBACK(&networkLifecycleCb), &counter, NULL); virNetworkCreate(test->net); virNetworkDestroy(test->net); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.startEvents != 1 || counter.stopEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectNetworkEventDeregisterAny(test->conn, id); return ret; } static int testStoragePoolCreateXML(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virStoragePoolPtr pool; int id; int ret = -1; id = virConnectStoragePoolEventRegisterAny(test->conn, NULL, VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), &counter, NULL); pool = virStoragePoolCreateXML(test->conn, storagePoolDef, 0); if (!pool || virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.startEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectStoragePoolEventDeregisterAny(test->conn, id); if (pool) { virStoragePoolDestroy(pool); virStoragePoolFree(pool); } return ret; } static int testStoragePoolDefine(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virStoragePoolPtr pool; int id; int ret = -1; id = virConnectStoragePoolEventRegisterAny(test->conn, NULL, VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), &counter, NULL); /* Make sure the define event is triggered */ pool = virStoragePoolDefineXML(test->conn, storagePoolDef, 0); if (!pool || virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.defineEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } /* Make sure the undefine event is triggered */ virStoragePoolUndefine(pool); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.undefineEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectStoragePoolEventDeregisterAny(test->conn, id); if (pool) virStoragePoolFree(pool); return ret; } static int testStoragePoolStartStopEvent(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int refreshCounter = 0; int id1, id2; int ret = -1; if (!test->pool) return -1; id1 = virConnectStoragePoolEventRegisterAny(test->conn, test->pool, VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), &counter, NULL); id2 = virConnectStoragePoolEventRegisterAny(test->conn, test->pool, VIR_STORAGE_POOL_EVENT_ID_REFRESH, VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolRefreshCb), &refreshCounter, NULL); virStoragePoolCreate(test->pool, 0); virStoragePoolRefresh(test->pool, 0); virStoragePoolDestroy(test->pool); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.startEvents != 1 || counter.stopEvents != 1 || refreshCounter != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectStoragePoolEventDeregisterAny(test->conn, id1); virConnectStoragePoolEventDeregisterAny(test->conn, id2); return ret; } static int testStoragePoolBuild(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int id; int ret = -1; id = virConnectStoragePoolEventRegisterAny(test->conn, NULL, VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), &counter, NULL); virStoragePoolBuild(test->pool, 0); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.createdEvents != 1) { goto cleanup; } ret = 0; cleanup: virConnectStoragePoolEventDeregisterAny(test->conn, id); return ret; } static int testStoragePoolDelete(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; int id; int ret = -1; id = virConnectStoragePoolEventRegisterAny(test->conn, NULL, VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), &counter, NULL); virStoragePoolDelete(test->pool, 0); if (virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.deletedEvents != 1) { goto cleanup; } ret = 0; cleanup: virConnectStoragePoolEventDeregisterAny(test->conn, id); return ret; } static int testNodeDeviceCreateXML(const void *data) { const objecteventTest *test = data; lifecycleEventCounter counter = { 0 }; virNodeDevicePtr dev; int id; int ret = -1; id = virConnectNodeDeviceEventRegisterAny(test->conn, NULL, VIR_NODE_DEVICE_EVENT_ID_LIFECYCLE, VIR_NODE_DEVICE_EVENT_CALLBACK(&nodeDeviceLifecycleCb), &counter, NULL); dev = virNodeDeviceCreateXML(test->conn, nodeDeviceDef, 0); virNodeDeviceDestroy(dev); if (!dev || virEventRunDefaultImpl() < 0) { goto cleanup; } if (counter.createdEvents != 1 || counter.deletedEvents != 1 || counter.unexpectedEvents > 0) { goto cleanup; } ret = 0; cleanup: virConnectNodeDeviceEventDeregisterAny(test->conn, id); if (dev) virNodeDeviceFree(dev); return ret; } static void timeout(int id G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED) { fputs("test taking too long; giving up", stderr); _exit(EXIT_FAILURE); } static int mymain(void) { objecteventTest test = { 0 }; int ret = EXIT_SUCCESS; int timer; virEventRegisterDefaultImpl(); /* Set up a timer to abort this test if it takes 10 seconds. */ if ((timer = virEventAddTimeout(10 * 1000, timeout, NULL, NULL)) < 0) return EXIT_FAILURE; if (!(test.conn = virConnectOpen("test:///default"))) return EXIT_FAILURE; virTestQuiesceLibvirtErrors(false); /* Domain event tests */ if (virTestRun("Domain createXML start event (old API)", testDomainCreateXMLOld, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Domain createXML start event (new API)", testDomainCreateXMLNew, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Domain createXML start event (both API)", testDomainCreateXMLMixed, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Domain (un)define events", testDomainDefine, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Domain start stop events", testDomainStartStopEvent, &test) < 0) ret = EXIT_FAILURE; /* Network event tests */ /* Tests requiring the test network not to be set up */ if (virTestRun("Network createXML start event ", testNetworkCreateXML, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Network (un)define events", testNetworkDefine, &test) < 0) ret = EXIT_FAILURE; /* Define a test network */ if (!(test.net = virNetworkDefineXML(test.conn, networkDef))) ret = EXIT_FAILURE; if (virTestRun("Network start stop events ", testNetworkStartStopEvent, &test) < 0) ret = EXIT_FAILURE; /* Cleanup */ if (test.net) { virNetworkUndefine(test.net); virNetworkFree(test.net); } /* Storage pool event tests */ if (virTestRun("Storage pool createXML start event ", testStoragePoolCreateXML, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Storage pool (un)define events", testStoragePoolDefine, &test) < 0) ret = EXIT_FAILURE; /* Define a test storage pool */ if (!(test.pool = virStoragePoolDefineXML(test.conn, storagePoolDef, 0))) ret = EXIT_FAILURE; if (virTestRun("Storage pool start stop events ", testStoragePoolStartStopEvent, &test) < 0) ret = EXIT_FAILURE; /* Storage pool build and delete events */ if (virTestRun("Storage pool build event ", testStoragePoolBuild, &test) < 0) ret = EXIT_FAILURE; if (virTestRun("Storage pool delete event ", testStoragePoolDelete, &test) < 0) ret = EXIT_FAILURE; /* Node device event tests */ if (virTestRun("Node device createXML add event ", testNodeDeviceCreateXML, &test) < 0) ret = EXIT_FAILURE; /* Cleanup */ if (test.pool) { virStoragePoolUndefine(test.pool); virStoragePoolFree(test.pool); } virConnectClose(test.conn); virEventRemoveTimeout(timer); return ret; } VIR_TEST_MAIN(mymain)