Skip to content

Commit 19f79b1

Browse files
committed
Merge branch '4.19'
2 parents a44f28b + d3e020a commit 19f79b1

File tree

14 files changed

+233
-107
lines changed

14 files changed

+233
-107
lines changed

agent/conf/agent.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,7 @@ iscsi.session.cleanup.enabled=false
426426

427427
# Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode
428428
# virtv2v.verbose.enabled=false
429+
430+
# If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or
431+
# unexpectedly stopped. Experimental, requires agent restart.
432+
# libvirt.events.enabled=false

agent/src/main/java/com/cloud/agent/properties/AgentProperties.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,13 @@ public class AgentProperties{
695695
*/
696696
public static final Property<Boolean> DEVELOPER = new Property<>("developer", false);
697697

698+
/**
699+
* If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or unexpectedly
700+
* stopped VMs. Experimental, requires agent restart.
701+
* Default value: <code>false</code>
702+
*/
703+
public static final Property<Boolean> LIBVIRT_EVENTS_ENABLED = new Property<>("libvirt.events.enabled", false);
704+
698705
/**
699706
* Can only be used if developer = true. This property is used to define the local bridge name and private network name.<br>
700707
* Data type: String.<br>

engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Lon
130130

131131
boolean canVmRestartOnAnotherServer(long vmId);
132132

133+
void saveVolumeDetails(Long diskOfferingId, Long volumeId);
134+
133135
/**
134136
* Allocate a volume or multiple volumes in case of template is registered with the 'deploy-as-is' option, allowing multiple disks
135137
*/

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -864,18 +864,7 @@ public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offeri
864864
vol.setFormat(getSupportedImageFormatForCluster(vm.getHypervisorType()));
865865
vol = _volsDao.persist(vol);
866866

867-
List<VolumeDetailVO> volumeDetailsVO = new ArrayList<VolumeDetailVO>();
868-
DiskOfferingDetailVO bandwidthLimitDetail = _diskOfferingDetailDao.findDetail(offering.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS);
869-
if (bandwidthLimitDetail != null) {
870-
volumeDetailsVO.add(new VolumeDetailVO(vol.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS, bandwidthLimitDetail.getValue(), false));
871-
}
872-
DiskOfferingDetailVO iopsLimitDetail = _diskOfferingDetailDao.findDetail(offering.getId(), Volume.IOPS_LIMIT);
873-
if (iopsLimitDetail != null) {
874-
volumeDetailsVO.add(new VolumeDetailVO(vol.getId(), Volume.IOPS_LIMIT, iopsLimitDetail.getValue(), false));
875-
}
876-
if (!volumeDetailsVO.isEmpty()) {
877-
_volDetailDao.saveDetails(volumeDetailsVO);
878-
}
867+
saveVolumeDetails(offering.getId(), vol.getId());
879868

880869
// Save usage event and update resource count for user vm volumes
881870
if (vm.getType() == VirtualMachine.Type.User) {
@@ -890,6 +879,32 @@ public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offeri
890879
return diskProfile;
891880
}
892881

882+
@Override
883+
public void saveVolumeDetails(Long diskOfferingId, Long volumeId) {
884+
List<VolumeDetailVO> volumeDetailsVO = new ArrayList<>();
885+
DiskOfferingDetailVO bandwidthLimitDetail = _diskOfferingDetailDao.findDetail(diskOfferingId, Volume.BANDWIDTH_LIMIT_IN_MBPS);
886+
if (bandwidthLimitDetail != null) {
887+
volumeDetailsVO.add(new VolumeDetailVO(volumeId, Volume.BANDWIDTH_LIMIT_IN_MBPS, bandwidthLimitDetail.getValue(), false));
888+
} else {
889+
VolumeDetailVO bandwidthLimit = _volDetailDao.findDetail(volumeId, Volume.BANDWIDTH_LIMIT_IN_MBPS);
890+
if (bandwidthLimit != null) {
891+
_volDetailDao.remove(bandwidthLimit.getId());
892+
}
893+
}
894+
DiskOfferingDetailVO iopsLimitDetail = _diskOfferingDetailDao.findDetail(diskOfferingId, Volume.IOPS_LIMIT);
895+
if (iopsLimitDetail != null) {
896+
volumeDetailsVO.add(new VolumeDetailVO(volumeId, Volume.IOPS_LIMIT, iopsLimitDetail.getValue(), false));
897+
} else {
898+
VolumeDetailVO iopsLimit = _volDetailDao.findDetail(volumeId, Volume.IOPS_LIMIT);
899+
if (iopsLimit != null) {
900+
_volDetailDao.remove(iopsLimit.getId());
901+
}
902+
}
903+
if (!volumeDetailsVO.isEmpty()) {
904+
_volDetailDao.saveDetails(volumeDetailsVO);
905+
}
906+
}
907+
893908
private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,
894909
Account owner, long deviceId, String configurationId) {
895910
assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template.";

engine/schema/src/main/java/com/cloud/storage/VolumeDetailVO.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,7 @@ public boolean isDisplay() {
8080
return display;
8181
}
8282

83+
public void setValue(String value) {
84+
this.value = value;
85+
}
8386
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@
9393
import org.libvirt.SchedUlongParameter;
9494
import org.libvirt.Secret;
9595
import org.libvirt.VcpuInfo;
96-
import org.libvirt.event.DomainEvent;
97-
import org.libvirt.event.DomainEventDetail;
98-
import org.libvirt.event.StoppedDetail;
9996
import org.w3c.dom.Document;
10097
import org.w3c.dom.Element;
10198
import org.w3c.dom.Node;
@@ -469,7 +466,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
469466
protected CPUStat cpuStat = new CPUStat();
470467
protected MemStat memStat = new MemStat(dom0MinMem, dom0OvercommitMem);
471468
private final LibvirtUtilitiesHelper libvirtUtilitiesHelper = new LibvirtUtilitiesHelper();
472-
private AgentStatusUpdater _agentStatusUpdater;
469+
private LibvirtDomainListener libvirtDomainListener;
473470

474471
protected Boolean enableManuallySettingCpuTopologyOnKvmVm = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM);
475472

@@ -503,8 +500,23 @@ protected long getHypervisorQemuVersion() {
503500
}
504501

505502
@Override
506-
public void registerStatusUpdater(AgentStatusUpdater updater) {
507-
_agentStatusUpdater = updater;
503+
public synchronized void registerStatusUpdater(AgentStatusUpdater updater) {
504+
if (AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_EVENTS_ENABLED)) {
505+
try {
506+
Connect conn = LibvirtConnection.getConnection();
507+
if (libvirtDomainListener != null) {
508+
LOGGER.debug("Clearing old domain listener");
509+
conn.removeLifecycleListener(libvirtDomainListener);
510+
}
511+
libvirtDomainListener = new LibvirtDomainListener(updater);
512+
conn.addLifecycleListener(libvirtDomainListener);
513+
LOGGER.debug("Set up the libvirt domain event lifecycle listener");
514+
} catch (LibvirtException e) {
515+
LOGGER.error("Failed to get libvirt connection for domain event lifecycle", e);
516+
}
517+
} else {
518+
LOGGER.debug("Libvirt event listening is disabled, not registering status updater");
519+
}
508520
}
509521

510522
@Override
@@ -1879,6 +1891,10 @@ public String startVM(final Connect conn, final String vmName, final String doma
18791891
public boolean stop() {
18801892
try {
18811893
final Connect conn = LibvirtConnection.getConnection();
1894+
if (AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_EVENTS_ENABLED) && libvirtDomainListener != null) {
1895+
LOGGER.debug("Clearing old domain listener");
1896+
conn.removeLifecycleListener(libvirtDomainListener);
1897+
}
18821898
conn.close();
18831899
} catch (final LibvirtException e) {
18841900
LOGGER.trace("Ignoring libvirt error.", e);
@@ -3699,50 +3715,9 @@ private StartupStorageCommand createLocalStoragePool(String localStoragePath, St
36993715
} catch (final CloudRuntimeException e) {
37003716
LOGGER.debug("Unable to initialize local storage pool: " + e);
37013717
}
3702-
setupLibvirtEventListener();
37033718
return sscmd;
37043719
}
37053720

3706-
private void setupLibvirtEventListener() {
3707-
try {
3708-
Connect conn = LibvirtConnection.getConnection();
3709-
conn.addLifecycleListener(this::onDomainLifecycleChange);
3710-
3711-
logger.debug("Set up the libvirt domain event lifecycle listener");
3712-
} catch (LibvirtException e) {
3713-
logger.error("Failed to get libvirt connection for domain event lifecycle", e);
3714-
}
3715-
}
3716-
3717-
private int onDomainLifecycleChange(Domain domain, DomainEvent domainEvent) {
3718-
try {
3719-
logger.debug(String.format("Got event lifecycle change on Domain %s, event %s", domain.getName(), domainEvent));
3720-
if (domainEvent != null) {
3721-
switch (domainEvent.getType()) {
3722-
case STOPPED:
3723-
/* libvirt-destroyed VMs have detail StoppedDetail.DESTROYED, self shutdown guests are StoppedDetail.SHUTDOWN
3724-
* Checking for this helps us differentiate between events where cloudstack or admin stopped the VM vs guest
3725-
* initiated, and avoid pushing extra updates for actions we are initiating without a need for extra tracking */
3726-
DomainEventDetail detail = domainEvent.getDetail();
3727-
if (StoppedDetail.SHUTDOWN.equals(detail) || StoppedDetail.CRASHED.equals(detail) || StoppedDetail.FAILED.equals(detail)) {
3728-
logger.info("Triggering out of band status update due to completed self-shutdown or crash of VM");
3729-
_agentStatusUpdater.triggerUpdate();
3730-
} else {
3731-
logger.debug("Event detail: " + detail);
3732-
}
3733-
break;
3734-
default:
3735-
logger.debug(String.format("No handling for event %s", domainEvent));
3736-
}
3737-
}
3738-
} catch (LibvirtException e) {
3739-
logger.error("Libvirt exception while processing lifecycle event", e);
3740-
} catch (Throwable e) {
3741-
logger.error("Error during lifecycle", e);
3742-
}
3743-
return 0;
3744-
}
3745-
37463721
public String diskUuidToSerial(String uuid) {
37473722
String uuidWithoutHyphen = uuid.replace("-","");
37483723
return uuidWithoutHyphen.substring(0, Math.min(uuidWithoutHyphen.length(), 20));

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtConnection.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static public Connect getConnection() throws LibvirtException {
4242
return getConnection(s_hypervisorURI);
4343
}
4444

45-
static public Connect getConnection(String hypervisorURI) throws LibvirtException {
45+
static synchronized public Connect getConnection(String hypervisorURI) throws LibvirtException {
4646
LOGGER.debug("Looking for libvirtd connection at: " + hypervisorURI);
4747
Connect conn = s_connections.get(hypervisorURI);
4848

@@ -122,6 +122,11 @@ static String getHypervisorURI(String hypervisorType) {
122122
* @throws LibvirtException
123123
*/
124124
private static synchronized void setupEventListener() throws LibvirtException {
125+
if (!AgentPropertiesFileHandler.getPropertyValue(AgentProperties.LIBVIRT_EVENTS_ENABLED)) {
126+
LOGGER.debug("Libvirt event listening is disabled, not setting up event loop");
127+
return;
128+
}
129+
125130
if (libvirtEventThread == null || !libvirtEventThread.isAlive()) {
126131
// Registers a default event loop, must be called before connecting to hypervisor
127132
Library.initEventLoop();
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
package com.cloud.hypervisor.kvm.resource;
16+
17+
import com.cloud.resource.AgentStatusUpdater;
18+
import org.apache.log4j.Logger;
19+
import org.libvirt.Domain;
20+
import org.libvirt.LibvirtException;
21+
import org.libvirt.event.DomainEvent;
22+
import org.libvirt.event.DomainEventDetail;
23+
import org.libvirt.event.LifecycleListener;
24+
import org.libvirt.event.StoppedDetail;
25+
26+
public class LibvirtDomainListener implements LifecycleListener {
27+
private static final Logger LOGGER = Logger.getLogger(LibvirtDomainListener.class);
28+
29+
private final AgentStatusUpdater agentStatusUpdater;
30+
31+
public LibvirtDomainListener(AgentStatusUpdater updater) {
32+
agentStatusUpdater = updater;
33+
}
34+
35+
public int onLifecycleChange(Domain domain, DomainEvent domainEvent) {
36+
try {
37+
LOGGER.debug(String.format("Got event lifecycle change on Domain %s, event %s", domain.getName(), domainEvent));
38+
if (domainEvent != null) {
39+
switch (domainEvent.getType()) {
40+
case STOPPED:
41+
/* libvirt-destroyed VMs have detail StoppedDetail.DESTROYED, self shutdown guests are StoppedDetail.SHUTDOWN
42+
* Checking for this helps us differentiate between events where cloudstack or admin stopped the VM vs guest
43+
* initiated, and avoid pushing extra updates for actions we are initiating without a need for extra tracking */
44+
DomainEventDetail detail = domainEvent.getDetail();
45+
if (StoppedDetail.SHUTDOWN.equals(detail) || StoppedDetail.CRASHED.equals(detail) || StoppedDetail.FAILED.equals(detail)) {
46+
if (agentStatusUpdater != null) {
47+
LOGGER.info("Triggering out of band status update due to completed self-shutdown or crash of VM");
48+
agentStatusUpdater.triggerUpdate();
49+
}
50+
} else {
51+
LOGGER.debug("Event detail: " + detail);
52+
}
53+
break;
54+
default:
55+
LOGGER.debug(String.format("No handling for event %s", domainEvent));
56+
}
57+
}
58+
} catch (LibvirtException e) {
59+
LOGGER.error("Libvirt exception while processing lifecycle event", e);
60+
} catch (Throwable e) {
61+
LOGGER.error("Error during lifecycle", e);
62+
}
63+
return 0;
64+
}
65+
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,6 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
5959
String message = String.format("Unable to scale %s due to [%s].", scalingDetails, e.getMessage());
6060
logger.error(message, e);
6161
return new ScaleVmAnswer(command, false, message);
62-
} finally {
63-
if (conn != null) {
64-
try {
65-
conn.close();
66-
} catch (LibvirtException ex) {
67-
logger.warn(String.format("Error trying to close libvirt connection [%s]", ex.getMessage()), ex);
68-
}
69-
}
7062
}
7163
}
7264

plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ private void getHostInfoFromLibvirt() {
223223
We used to check if this was supported, but that is no longer required
224224
*/
225225
this.capabilities.add("snapshot");
226-
conn.close();
227226
} catch (final LibvirtException e) {
228227
LOGGER.error("Caught libvirt exception while fetching host information", e);
229228
}

0 commit comments

Comments
 (0)