mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Mark libvirt events experimental, add properties flag (#8825)
* Mark libvirt events experimental, add properties flag * unit test fixes --------- Co-authored-by: Marcus Sorensen <mls@apple.com>
This commit is contained in:
parent
730cc5d5b8
commit
d3e020a545
@ -426,3 +426,7 @@ iscsi.session.cleanup.enabled=false
|
|||||||
|
|
||||||
# Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode
|
# Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode
|
||||||
# virtv2v.verbose.enabled=false
|
# 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
|
||||||
|
|||||||
@ -695,6 +695,13 @@ public class AgentProperties{
|
|||||||
*/
|
*/
|
||||||
public static final Property<Boolean> DEVELOPER = new Property<>("developer", false);
|
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>
|
* 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>
|
* Data type: String.<br>
|
||||||
|
|||||||
@ -92,9 +92,6 @@ import org.libvirt.SchedParameter;
|
|||||||
import org.libvirt.SchedUlongParameter;
|
import org.libvirt.SchedUlongParameter;
|
||||||
import org.libvirt.Secret;
|
import org.libvirt.Secret;
|
||||||
import org.libvirt.VcpuInfo;
|
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.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
@ -468,7 +465,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||||||
protected CPUStat cpuStat = new CPUStat();
|
protected CPUStat cpuStat = new CPUStat();
|
||||||
protected MemStat memStat = new MemStat(dom0MinMem, dom0OvercommitMem);
|
protected MemStat memStat = new MemStat(dom0MinMem, dom0OvercommitMem);
|
||||||
private final LibvirtUtilitiesHelper libvirtUtilitiesHelper = new LibvirtUtilitiesHelper();
|
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);
|
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
|
@Override
|
||||||
public void registerStatusUpdater(AgentStatusUpdater updater) {
|
public synchronized void registerStatusUpdater(AgentStatusUpdater updater) {
|
||||||
_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
|
@Override
|
||||||
@ -1878,6 +1890,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||||||
public boolean stop() {
|
public boolean stop() {
|
||||||
try {
|
try {
|
||||||
final Connect conn = LibvirtConnection.getConnection();
|
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();
|
conn.close();
|
||||||
} catch (final LibvirtException e) {
|
} catch (final LibvirtException e) {
|
||||||
s_logger.trace("Ignoring libvirt error.", e);
|
s_logger.trace("Ignoring libvirt error.", e);
|
||||||
@ -3688,50 +3704,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||||||
} catch (final CloudRuntimeException e) {
|
} catch (final CloudRuntimeException e) {
|
||||||
s_logger.debug("Unable to initialize local storage pool: " + e);
|
s_logger.debug("Unable to initialize local storage pool: " + e);
|
||||||
}
|
}
|
||||||
setupLibvirtEventListener();
|
|
||||||
return sscmd;
|
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) {
|
public String diskUuidToSerial(String uuid) {
|
||||||
String uuidWithoutHyphen = uuid.replace("-","");
|
String uuidWithoutHyphen = uuid.replace("-","");
|
||||||
return uuidWithoutHyphen.substring(0, Math.min(uuidWithoutHyphen.length(), 20));
|
return uuidWithoutHyphen.substring(0, Math.min(uuidWithoutHyphen.length(), 20));
|
||||||
|
|||||||
@ -41,7 +41,7 @@ public class LibvirtConnection {
|
|||||||
return getConnection(s_hypervisorURI);
|
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);
|
s_logger.debug("Looking for libvirtd connection at: " + hypervisorURI);
|
||||||
Connect conn = s_connections.get(hypervisorURI);
|
Connect conn = s_connections.get(hypervisorURI);
|
||||||
|
|
||||||
@ -121,6 +121,11 @@ public class LibvirtConnection {
|
|||||||
* @throws LibvirtException
|
* @throws LibvirtException
|
||||||
*/
|
*/
|
||||||
private static synchronized void setupEventListener() 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()) {
|
if (libvirtEventThread == null || !libvirtEventThread.isAlive()) {
|
||||||
// Registers a default event loop, must be called before connecting to hypervisor
|
// Registers a default event loop, must be called before connecting to hypervisor
|
||||||
Library.initEventLoop();
|
Library.initEventLoop();
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -59,14 +59,6 @@ public class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
|
|||||||
String message = String.format("Unable to scale %s due to [%s].", scalingDetails, e.getMessage());
|
String message = String.format("Unable to scale %s due to [%s].", scalingDetails, e.getMessage());
|
||||||
logger.error(message, e);
|
logger.error(message, e);
|
||||||
return new ScaleVmAnswer(command, false, message);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -222,7 +222,6 @@ public class KVMHostInfo {
|
|||||||
We used to check if this was supported, but that is no longer required
|
We used to check if this was supported, but that is no longer required
|
||||||
*/
|
*/
|
||||||
this.capabilities.add("snapshot");
|
this.capabilities.add("snapshot");
|
||||||
conn.close();
|
|
||||||
} catch (final LibvirtException e) {
|
} catch (final LibvirtException e) {
|
||||||
LOGGER.error("Caught libvirt exception while fetching host information", e);
|
LOGGER.error("Caught libvirt exception while fetching host information", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,6 @@ public class KVMHostInfoTest {
|
|||||||
Mockito.when(LibvirtConnection.getConnection()).thenReturn(conn);
|
Mockito.when(LibvirtConnection.getConnection()).thenReturn(conn);
|
||||||
Mockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
|
Mockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
|
||||||
Mockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
|
Mockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
|
||||||
Mockito.when(conn.close()).thenReturn(0);
|
|
||||||
int manualSpeed = 500;
|
int manualSpeed = 500;
|
||||||
|
|
||||||
KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, manualSpeed, 0);
|
KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, manualSpeed, 0);
|
||||||
@ -92,8 +91,6 @@ public class KVMHostInfoTest {
|
|||||||
Mockito.when(LibvirtConnection.getConnection()).thenReturn(conn);
|
Mockito.when(LibvirtConnection.getConnection()).thenReturn(conn);
|
||||||
Mockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
|
Mockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
|
||||||
Mockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
|
Mockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
|
||||||
Mockito.when(conn.close()).thenReturn(0);
|
|
||||||
int manualSpeed = 500;
|
|
||||||
|
|
||||||
KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, 100, 2);
|
KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, 100, 2);
|
||||||
Assert.assertEquals("reserve two CPU cores", 8, kvmHostInfo.getAllocatableCpus());
|
Assert.assertEquals("reserve two CPU cores", 8, kvmHostInfo.getAllocatableCpus());
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user