Fix libvirt domain event listener by properly processing events (#364)

Co-authored-by: Marcus Sorensen <mls@apple.com>
This commit is contained in:
Marcus Sorensen 2024-01-29 02:16:20 -07:00 committed by GitHub
parent 0201e0af95
commit e610d2c54c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 15 deletions

View File

@ -83,7 +83,6 @@ import org.libvirt.DomainInfo;
import org.libvirt.DomainInfo.DomainState;
import org.libvirt.DomainInterfaceStats;
import org.libvirt.DomainSnapshot;
import org.libvirt.Library;
import org.libvirt.LibvirtException;
import org.libvirt.MemoryStatistic;
import org.libvirt.Network;
@ -3610,20 +3609,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
private void setupLibvirtEventListener() {
final Thread libvirtListenerThread = new Thread(() -> {
try {
Library.runEventLoop();
} catch (LibvirtException e) {
s_logger.error("LibvirtException was thrown in event loop: ", e);
} catch (InterruptedException e) {
s_logger.error("Libvirt event loop was interrupted: ", e);
}
});
try {
libvirtListenerThread.setDaemon(true);
libvirtListenerThread.start();
Connect conn = LibvirtConnection.getConnection();
conn.addLifecycleListener(this::onDomainLifecycleChange);
@ -3643,7 +3629,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
* Checking for this helps us differentiate between events where cloudstack or admin stopped the VM vs guest
* initiated, and avoid pushing extra updates for actions we are initiating without a need for extra tracking */
DomainEventDetail detail = domainEvent.getDetail();
if (StoppedDetail.SHUTDOWN.equals(detail) || StoppedDetail.CRASHED.equals(detail)) {
if (StoppedDetail.SHUTDOWN.equals(detail) || StoppedDetail.CRASHED.equals(detail) || StoppedDetail.FAILED.equals(detail)) {
s_logger.info("Triggering out of band status update due to completed self-shutdown or crash of VM");
_agentStatusUpdater.triggerUpdate();
} else {

View File

@ -21,6 +21,7 @@ import java.util.Map;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
import org.libvirt.Library;
import org.libvirt.LibvirtException;
import com.cloud.hypervisor.Hypervisor;
@ -32,6 +33,7 @@ public class LibvirtConnection {
static private Connect s_connection;
static private String s_hypervisorURI;
static private Thread libvirtEventThread;
static public Connect getConnection() throws LibvirtException {
return getConnection(s_hypervisorURI);
@ -43,6 +45,8 @@ public class LibvirtConnection {
if (conn == null) {
s_logger.info("No existing libvirtd connection found. Opening a new one");
setupEventListener();
conn = new Connect(hypervisorURI, false);
s_logger.debug("Successfully connected to libvirt at: " + hypervisorURI);
s_connections.put(hypervisorURI, conn);
@ -51,7 +55,15 @@ public class LibvirtConnection {
conn.getVersion();
} catch (LibvirtException e) {
s_logger.error("Connection with libvirtd is broken: " + e.getMessage());
try {
conn.close();
} catch (LibvirtException closeEx) {
s_logger.debug("Ignoring error while trying to close broken connection:" + closeEx.getMessage());
}
s_logger.debug("Opening a new libvirtd connection to: " + hypervisorURI);
setupEventListener();
conn = new Connect(hypervisorURI, false);
s_connections.put(hypervisorURI, conn);
}
@ -94,4 +106,29 @@ public class LibvirtConnection {
return "qemu:///system";
}
}
// stand up libvirt event handling and polling. This is not specific to a connection object instance, but needs to
// exist prior to creating connections.
private static synchronized void setupEventListener() throws LibvirtException {
if (libvirtEventThread == null || !libvirtEventThread.isAlive()) {
// Registers a default event loop, must be called before connecting to hypervisor
Library.initEventLoop();
libvirtEventThread = new Thread(() -> {
while (true) {
try {
// This blocking call contains a loop of its own that will process events until the event loop is stopped or exception is thrown.
Library.runEventLoop();
} catch (LibvirtException e) {
s_logger.error("LibvirtException was thrown in event loop: ", e);
} catch (InterruptedException e) {
s_logger.error("Libvirt event loop was interrupted: ", e);
}
}
});
// Process events in separate thread. Failure to run event loop regularly will cause connections to close due to keepalive timeout.
libvirtEventThread.setDaemon(true);
libvirtEventThread.start();
}
}
}