This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch 4.19
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.19 by this push:
new d3e020a5452 Mark libvirt events experimental, add properties flag
(#8825)
d3e020a5452 is described below
commit d3e020a5452a584907f815f292d5b8adf50be844
Author: Suresh Kumar Anaparti <[email protected]>
AuthorDate: Thu Apr 11 17:06:33 2024 +0530
Mark libvirt events experimental, add properties flag (#8825)
* Mark libvirt events experimental, add properties flag
* unit test fixes
---------
Co-authored-by: Marcus Sorensen <[email protected]>
---
agent/conf/agent.properties | 4 ++
.../cloud/agent/properties/AgentProperties.java | 7 +++
.../kvm/resource/LibvirtComputingResource.java | 69 +++++++---------------
.../hypervisor/kvm/resource/LibvirtConnection.java | 7 ++-
.../kvm/resource/LibvirtDomainListener.java | 65 ++++++++++++++++++++
.../wrapper/LibvirtScaleVmCommandWrapper.java | 8 ---
.../apache/cloudstack/utils/linux/KVMHostInfo.java | 1 -
.../cloudstack/utils/linux/KVMHostInfoTest.java | 3 -
8 files changed, 104 insertions(+), 60 deletions(-)
diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 6adde7435ab..e600e8f8f20 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -426,3 +426,7 @@ iscsi.session.cleanup.enabled=false
# Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode
# virtv2v.verbose.enabled=false
+
+# If set to "true", the agent will register for libvirt domain events,
allowing for immediate updates on crashed or
+# unexpectedly stopped. Experimental, requires agent restart.
+# libvirt.events.enabled=false
diff --git
a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
index 322a0ed2444..24a09ae2ac1 100644
--- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
@@ -695,6 +695,13 @@ public class AgentProperties{
*/
public static final Property<Boolean> DEVELOPER = new
Property<>("developer", false);
+ /**
+ * If set to "true", the agent will register for libvirt domain events,
allowing for immediate updates on crashed or unexpectedly
+ * stopped VMs. Experimental, requires agent restart.
+ * Default value: <code>false</code>
+ */
+ public static final Property<Boolean> LIBVIRT_EVENTS_ENABLED = new
Property<>("libvirt.events.enabled", false);
+
/**
* Can only be used if developer = true. This property is used to define
the local bridge name and private network name.<br>
* Data type: String.<br>
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index fef5d4aa6de..37aba35bb9c 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -92,9 +92,6 @@ import org.libvirt.SchedParameter;
import org.libvirt.SchedUlongParameter;
import org.libvirt.Secret;
import org.libvirt.VcpuInfo;
-import org.libvirt.event.DomainEvent;
-import org.libvirt.event.DomainEventDetail;
-import org.libvirt.event.StoppedDetail;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -468,7 +465,7 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
protected CPUStat cpuStat = new CPUStat();
protected MemStat memStat = new MemStat(dom0MinMem, dom0OvercommitMem);
private final LibvirtUtilitiesHelper libvirtUtilitiesHelper = new
LibvirtUtilitiesHelper();
- private AgentStatusUpdater _agentStatusUpdater;
+ private LibvirtDomainListener libvirtDomainListener;
protected Boolean enableManuallySettingCpuTopologyOnKvmVm =
AgentPropertiesFileHandler.getPropertyValue(AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM);
@@ -502,8 +499,23 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
}
@Override
- public void registerStatusUpdater(AgentStatusUpdater updater) {
- _agentStatusUpdater = updater;
+ public synchronized void registerStatusUpdater(AgentStatusUpdater updater)
{
+ if
(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_EVENTS_ENABLED))
{
+ try {
+ Connect conn = LibvirtConnection.getConnection();
+ if (libvirtDomainListener != null) {
+ s_logger.debug("Clearing old domain listener");
+ conn.removeLifecycleListener(libvirtDomainListener);
+ }
+ libvirtDomainListener = new LibvirtDomainListener(updater);
+ conn.addLifecycleListener(libvirtDomainListener);
+ s_logger.debug("Set up the libvirt domain event lifecycle
listener");
+ } catch (LibvirtException e) {
+ s_logger.error("Failed to get libvirt connection for domain
event lifecycle", e);
+ }
+ } else {
+ s_logger.debug("Libvirt event listening is disabled, not
registering status updater");
+ }
}
@Override
@@ -1878,6 +1890,10 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
public boolean stop() {
try {
final Connect conn = LibvirtConnection.getConnection();
+ if
(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_EVENTS_ENABLED)
&& libvirtDomainListener != null) {
+ s_logger.debug("Clearing old domain listener");
+ conn.removeLifecycleListener(libvirtDomainListener);
+ }
conn.close();
} catch (final LibvirtException e) {
s_logger.trace("Ignoring libvirt error.", e);
@@ -3688,50 +3704,9 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
} catch (final CloudRuntimeException e) {
s_logger.debug("Unable to initialize local storage pool: " + e);
}
- setupLibvirtEventListener();
return sscmd;
}
- private void setupLibvirtEventListener() {
- try {
- Connect conn = LibvirtConnection.getConnection();
- conn.addLifecycleListener(this::onDomainLifecycleChange);
-
- s_logger.debug("Set up the libvirt domain event lifecycle
listener");
- } catch (LibvirtException e) {
- s_logger.error("Failed to get libvirt connection for domain event
lifecycle", e);
- }
- }
-
- private int onDomainLifecycleChange(Domain domain, DomainEvent
domainEvent) {
- try {
- s_logger.debug(String.format("Got event lifecycle change on Domain
%s, event %s", domain.getName(), domainEvent));
- if (domainEvent != null) {
- switch (domainEvent.getType()) {
- case STOPPED:
- /* libvirt-destroyed VMs have detail
StoppedDetail.DESTROYED, self shutdown guests are StoppedDetail.SHUTDOWN
- * 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) || 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 {
- s_logger.debug("Event detail: " + detail);
- }
- break;
- default:
- s_logger.debug(String.format("No handling for event
%s", domainEvent));
- }
- }
- } catch (LibvirtException e) {
- s_logger.error("Libvirt exception while processing lifecycle
event", e);
- } catch (Throwable e) {
- s_logger.error("Error during lifecycle", e);
- }
- return 0;
- }
-
public String diskUuidToSerial(String uuid) {
String uuidWithoutHyphen = uuid.replace("-","");
return uuidWithoutHyphen.substring(0,
Math.min(uuidWithoutHyphen.length(), 20));
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtConnection.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtConnection.java
index 1f4ab23a43f..0fa012b2c90 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtConnection.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtConnection.java
@@ -41,7 +41,7 @@ public class LibvirtConnection {
return getConnection(s_hypervisorURI);
}
- static public Connect getConnection(String hypervisorURI) throws
LibvirtException {
+ static synchronized public Connect getConnection(String hypervisorURI)
throws LibvirtException {
s_logger.debug("Looking for libvirtd connection at: " + hypervisorURI);
Connect conn = s_connections.get(hypervisorURI);
@@ -121,6 +121,11 @@ public class LibvirtConnection {
* @throws LibvirtException
*/
private static synchronized void setupEventListener() throws
LibvirtException {
+ if
(!AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_EVENTS_ENABLED))
{
+ s_logger.debug("Libvirt event listening is disabled, not setting
up event loop");
+ return;
+ }
+
if (libvirtEventThread == null || !libvirtEventThread.isAlive()) {
// Registers a default event loop, must be called before
connecting to hypervisor
Library.initEventLoop();
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainListener.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainListener.java
new file mode 100644
index 00000000000..281de01c5da
--- /dev/null
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainListener.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.cloud.hypervisor.kvm.resource;
+
+import com.cloud.resource.AgentStatusUpdater;
+import org.apache.log4j.Logger;
+import org.libvirt.Domain;
+import org.libvirt.LibvirtException;
+import org.libvirt.event.DomainEvent;
+import org.libvirt.event.DomainEventDetail;
+import org.libvirt.event.LifecycleListener;
+import org.libvirt.event.StoppedDetail;
+
+public class LibvirtDomainListener implements LifecycleListener {
+ private static final Logger LOGGER =
Logger.getLogger(LibvirtDomainListener.class);
+
+ private final AgentStatusUpdater agentStatusUpdater;
+
+ public LibvirtDomainListener(AgentStatusUpdater updater) {
+ agentStatusUpdater = updater;
+ }
+
+ public int onLifecycleChange(Domain domain, DomainEvent domainEvent) {
+ try {
+ LOGGER.debug(String.format("Got event lifecycle change on Domain
%s, event %s", domain.getName(), domainEvent));
+ if (domainEvent != null) {
+ switch (domainEvent.getType()) {
+ case STOPPED:
+ /* libvirt-destroyed VMs have detail
StoppedDetail.DESTROYED, self shutdown guests are StoppedDetail.SHUTDOWN
+ * 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) || StoppedDetail.FAILED.equals(detail)) {
+ if (agentStatusUpdater != null) {
+ LOGGER.info("Triggering out of band status
update due to completed self-shutdown or crash of VM");
+ agentStatusUpdater.triggerUpdate();
+ }
+ } else {
+ LOGGER.debug("Event detail: " + detail);
+ }
+ break;
+ default:
+ LOGGER.debug(String.format("No handling for event %s",
domainEvent));
+ }
+ }
+ } catch (LibvirtException e) {
+ LOGGER.error("Libvirt exception while processing lifecycle event",
e);
+ } catch (Throwable e) {
+ LOGGER.error("Error during lifecycle", e);
+ }
+ return 0;
+ }
+}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
index 79d43ba2735..1536984f2e8 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
@@ -59,14 +59,6 @@ public class LibvirtScaleVmCommandWrapper extends
CommandWrapper<ScaleVmCommand,
String message = String.format("Unable to scale %s due to [%s].",
scalingDetails, e.getMessage());
logger.error(message, e);
return new ScaleVmAnswer(command, false, message);
- } finally {
- if (conn != null) {
- try {
- conn.close();
- } catch (LibvirtException ex) {
- logger.warn(String.format("Error trying to close libvirt
connection [%s]", ex.getMessage()), ex);
- }
- }
}
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
index d160cbfac3b..21da711a9cb 100644
---
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
+++
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
@@ -222,7 +222,6 @@ public class KVMHostInfo {
We used to check if this was supported, but that is no longer
required
*/
this.capabilities.add("snapshot");
- conn.close();
} catch (final LibvirtException e) {
LOGGER.error("Caught libvirt exception while fetching host
information", e);
}
diff --git
a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
index 3b5422f564c..b41e2bf214a 100644
---
a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
@@ -70,7 +70,6 @@ public class KVMHostInfoTest {
Mockito.when(LibvirtConnection.getConnection()).thenReturn(conn);
Mockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
Mockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
- Mockito.when(conn.close()).thenReturn(0);
int manualSpeed = 500;
KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, manualSpeed, 0);
@@ -92,8 +91,6 @@ public class KVMHostInfoTest {
Mockito.when(LibvirtConnection.getConnection()).thenReturn(conn);
Mockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
Mockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
- Mockito.when(conn.close()).thenReturn(0);
- int manualSpeed = 500;
KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, 100, 2);
Assert.assertEquals("reserve two CPU cores", 8,
kvmHostInfo.getAllocatableCpus());