Skip to content

Commit 409e686

Browse files
authored
Merge branch '4.22' into add-performance-indexes
2 parents 29e2f0f + 2e63c60 commit 409e686

9 files changed

Lines changed: 104 additions & 37 deletions

File tree

‎api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
115115
@Parameter(name = ApiConstants.TEMPLATE_ID,
116116
type = CommandType.UUID,
117117
entityType = TemplateResponse.class,
118-
description = "The ID of the Template for the Instance")
118+
description = "The ID of the Template for the Instance. Required when the instance to import is an unmanaged KVM Instance.")
119119
private Long templateId;
120120

121121
@Parameter(name = ApiConstants.SERVICE_OFFERING_ID,

‎engine/schema/src/main/resources/META-INF/db/schema-42210to42220-cleanup.sql‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,3 @@
1818
--;
1919
-- Schema upgrade cleanup from 4.22.1.0 to 4.22.2.0
2020
--;
21-

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package com.cloud.hypervisor.kvm.resource.wrapper;
2121

22+
import org.apache.commons.lang.StringEscapeUtils;
2223
import org.libvirt.Connect;
2324
import org.libvirt.Domain;
2425
import org.libvirt.DomainInfo.DomainState;
@@ -57,7 +58,7 @@ public Answer execute(final CreateVMSnapshotCommand cmd, final LibvirtComputingR
5758
"Create Instance Snapshot Failed due to Instance is not running: " + vmName + " with domainState = " + domainState);
5859
}
5960

60-
String vmSnapshotXML = "<domainsnapshot>" + " <name>" + vmSnapshotName + "</name>"
61+
String vmSnapshotXML = "<domainsnapshot>" + " <name>" + StringEscapeUtils.escapeXml(vmSnapshotName) + "</name>"
6162
+ " <memory snapshot='internal' />" + "</domainsnapshot>";
6263

6364
dm.snapshotCreateXML(vmSnapshotXML);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.File;
2323
import java.text.MessageFormat;
2424

25+
import org.apache.commons.lang.StringEscapeUtils;
2526
import org.libvirt.Connect;
2627
import org.libvirt.Domain;
2728
import org.libvirt.DomainInfo.DomainState;
@@ -79,7 +80,7 @@ public Answer execute(final ManageSnapshotCommand command, final LibvirtComputin
7980
+ " <uuid>{1}</uuid>" + " </domain>" + " </domainsnapshot>");
8081

8182
final String vmUuid = vm.getUUIDString();
82-
final Object[] args = new Object[] {snapshotName, vmUuid};
83+
final Object[] args = new Object[] {StringEscapeUtils.escapeXml(snapshotName), vmUuid};
8384
final String snapshot = snapshotXML.format(args);
8485
logger.debug(snapshot);
8586
if (command.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
2727
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
2828
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
29+
import org.apache.commons.lang.StringEscapeUtils;
2930
import org.apache.logging.log4j.Logger;
3031
import org.apache.logging.log4j.LogManager;
3132
import org.libvirt.Connect;
@@ -112,9 +113,9 @@ public Script buildScript(final String scriptPath) {
112113
}
113114

114115
public String generateVMSnapshotXML(VMSnapshotTO snapshot, VMSnapshotTO parent, String domainXmlDesc) {
115-
String parentName = (parent == null)? "": (" <parent><name>" + parent.getSnapshotName() + "</name></parent>\n");
116+
String parentName = (parent == null)? "": (" <parent><name>" + StringEscapeUtils.escapeXml(parent.getSnapshotName()) + "</name></parent>\n");
116117
String vmSnapshotXML = "<domainsnapshot>\n"
117-
+ " <name>" + snapshot.getSnapshotName() + "</name>\n"
118+
+ " <name>" + StringEscapeUtils.escapeXml(snapshot.getSnapshotName()) + "</name>\n"
118119
+ " <state>running</state>\n"
119120
+ parentName
120121
+ " <creationTime>" + (int) Math.rint(snapshot.getCreateTime()/1000) + "</creationTime>\n"

‎plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUtilitiesHelperTest.java‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.mockito.Mockito;
2626
import org.mockito.junit.MockitoJUnitRunner;
2727

28+
import com.cloud.agent.api.VMSnapshotTO;
2829
import com.cloud.utils.Pair;
2930

3031
import junit.framework.TestCase;
@@ -75,4 +76,30 @@ public void validateIsLibvirtVersionEqualOrHigherThanVersionInParameterLibvirtVe
7576
Assert.assertEquals(String.valueOf(libvirtVersion), result.first());
7677
Assert.assertTrue(result.second());
7778
}
79+
80+
@Test
81+
public void generateVMSnapshotXMLEscapesSnapshotName() {
82+
VMSnapshotTO snapshot = Mockito.mock(VMSnapshotTO.class);
83+
Mockito.doReturn("i-2-3-VM_VS_</name><disks><disk name='vda'/></disks><name>x").when(snapshot).getSnapshotName();
84+
Mockito.doReturn(1000L).when(snapshot).getCreateTime();
85+
86+
String xml = libvirtUtilitiesHelperSpy.generateVMSnapshotXML(snapshot, null, "<domain/>");
87+
88+
Assert.assertTrue(xml.contains("<name>i-2-3-VM_VS_&lt;/name&gt;&lt;disks&gt;&lt;disk name=&apos;vda&apos;/&gt;&lt;/disks&gt;&lt;name&gt;x</name>"));
89+
Assert.assertFalse(xml.contains("<disks>"));
90+
}
91+
92+
@Test
93+
public void generateVMSnapshotXMLEscapesParentSnapshotName() {
94+
VMSnapshotTO snapshot = Mockito.mock(VMSnapshotTO.class);
95+
Mockito.doReturn("child").when(snapshot).getSnapshotName();
96+
Mockito.doReturn(1000L).when(snapshot).getCreateTime();
97+
VMSnapshotTO parent = Mockito.mock(VMSnapshotTO.class);
98+
Mockito.doReturn("parent</name><disks/>").when(parent).getSnapshotName();
99+
100+
String xml = libvirtUtilitiesHelperSpy.generateVMSnapshotXML(snapshot, parent, "<domain/>");
101+
102+
Assert.assertTrue(xml.contains("<parent><name>parent&lt;/name&gt;&lt;disks/&gt;</name></parent>"));
103+
Assert.assertFalse(xml.contains("<disks/>"));
104+
}
78105
}

‎plugins/integrations/kubernetes-service/src/main/resources/script/delete-pv-reclaimpolicy-delete‎

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,33 @@ delete_workloads_using_pvc() {
3333
local deleted_count=0
3434

3535
# Find & delete any deployment using the PVC
36-
/opt/bin/kubectl get deployments -n "$namespace" -o json 2>/dev/null | grep -l "$pvc_name" | \
37-
while IFS= read -r deployment; do
38-
if [ -n "$deployment" ]; then
39-
deployment_name=$(echo "$deployment" | cut -d'/' -f2)
40-
echo "$(timestamp) - Deleting Deployment: $deployment_name"
41-
/opt/bin/kubectl delete deployment "$deployment_name" -n "$namespace" --ignore-not-found=true
36+
# Iterate resource names and check the volumes via jsonpath to avoid grep -l on JSON (which prints "(standard input)")
37+
/opt/bin/kubectl get deployments -n "$namespace" -o name 2>/dev/null | \
38+
while IFS= read -r resource; do
39+
if [ -z "$resource" ]; then
40+
continue
41+
fi
42+
name=${resource#*/}
43+
dep_volumes=$(/opt/bin/kubectl get deployment "$name" -n "$namespace" -o jsonpath='{.spec.template.spec.volumes[*].persistentVolumeClaim.claimName}' 2>/dev/null || echo "")
44+
if [ -n "$dep_volumes" ] && echo "$dep_volumes" | grep -F -q "$pvc_name"; then
45+
echo "$(timestamp) - Deleting Deployment: $name"
46+
/opt/bin/kubectl delete deployment "$name" -n "$namespace" --ignore-not-found=true
4247
deleted_count=$((deleted_count + 1))
4348
fi
4449
done
4550

4651
# Find and delete any StatefulSet using the PVC
47-
/opt/bin/kubectl get statefulsets -n "$namespace" -o json 2>/dev/null | grep -l "$pvc_name" | \
48-
while IFS= read -r sts; do
49-
if [ -n "$sts" ]; then
50-
sts_name=$(echo "$sts" | cut -d'/' -f2)
51-
echo "$(timestamp) - Deleting StatefulSet: $sts_name"
52-
/opt/bin/kubectl delete statefulset "$sts_name" -n "$namespace" --ignore-not-found=true
52+
/opt/bin/kubectl get statefulsets -n "$namespace" -o name 2>/dev/null | \
53+
while IFS= read -r resource; do
54+
if [ -z "$resource" ]; then
55+
continue
56+
fi
57+
name=${resource#*/}
58+
# Check both template volumes and volumeClaimTemplates
59+
sts_volumes=$(/opt/bin/kubectl get statefulset "$name" -n "$namespace" -o jsonpath='{.spec.template.spec.volumes[*].persistentVolumeClaim.claimName} {.spec.volumeClaimTemplates[*].metadata.name}' 2>/dev/null || echo "")
60+
if [ -n "$sts_volumes" ] && echo "$sts_volumes" | grep -F -q "$pvc_name"; then
61+
echo "$(timestamp) - Deleting StatefulSet: $name"
62+
/opt/bin/kubectl delete statefulset "$name" -n "$namespace" --ignore-not-found=true
5363
deleted_count=$((deleted_count + 1))
5464
fi
5565
done
@@ -71,34 +81,46 @@ delete_workloads_using_pvc() {
7181
done
7282

7383
# Find and delete any DaemonSet using the PVC
74-
/opt/bin/kubectl get daemonsets -n "$namespace" -o json 2>/dev/null | grep -l "$pvc_name" | \
75-
while IFS= read -r ds; do
76-
if [ -n "$ds" ]; then
77-
ds_name=$(echo "$ds" | cut -d'/' -f2)
78-
echo "$(timestamp) - Deleting DaemonSet: $ds_name"
79-
/opt/bin/kubectl delete daemonset "$ds_name" -n "$namespace" --ignore-not-found=true
84+
/opt/bin/kubectl get daemonsets -n "$namespace" -o name 2>/dev/null | \
85+
while IFS= read -r resource; do
86+
if [ -z "$resource" ]; then
87+
continue
88+
fi
89+
name=${resource#*/}
90+
ds_volumes=$(/opt/bin/kubectl get daemonset "$name" -n "$namespace" -o jsonpath='{.spec.template.spec.volumes[*].persistentVolumeClaim.claimName}' 2>/dev/null || echo "")
91+
if [ -n "$ds_volumes" ] && echo "$ds_volumes" | grep -F -q "$pvc_name"; then
92+
echo "$(timestamp) - Deleting DaemonSet: $name"
93+
/opt/bin/kubectl delete daemonset "$name" -n "$namespace" --ignore-not-found=true
8094
deleted_count=$((deleted_count + 1))
8195
fi
8296
done
8397

8498
# Find and delete any Job using the PVC
85-
/opt/bin/kubectl get jobs -n "$namespace" -o json 2>/dev/null | grep -l "$pvc_name" | \
86-
while IFS= read -r job; do
87-
if [ -n "$job" ]; then
88-
job_name=$(echo "$job" | cut -d'/' -f2)
89-
echo "$(timestamp) - Deleting Job: $job_name"
90-
/opt/bin/kubectl delete job "$job_name" -n "$namespace" --ignore-not-found=true
99+
/opt/bin/kubectl get jobs -n "$namespace" -o name 2>/dev/null | \
100+
while IFS= read -r resource; do
101+
if [ -z "$resource" ]; then
102+
continue
103+
fi
104+
name=${resource#*/}
105+
job_volumes=$(/opt/bin/kubectl get job "$name" -n "$namespace" -o jsonpath='{.spec.template.spec.volumes[*].persistentVolumeClaim.claimName}' 2>/dev/null || echo "")
106+
if [ -n "$job_volumes" ] && echo "$job_volumes" | grep -F -q "$pvc_name"; then
107+
echo "$(timestamp) - Deleting Job: $name"
108+
/opt/bin/kubectl delete job "$name" -n "$namespace" --ignore-not-found=true
91109
deleted_count=$((deleted_count + 1))
92110
fi
93111
done
94112

95113
# Find and delete any CronJobs using the PVC
96-
/opt/bin/kubectl get cronjobs -n "$namespace" -o json 2>/dev/null | grep -l "$pvc_name" | \
97-
while IFS= read -r cronjob; do
98-
if [ -n "$cronjob" ]; then
99-
cronjob_name=$(echo "$cronjob" | cut -d'/' -f2)
100-
echo "$(timestamp) - Deleting CronJob: $cronjob_name"
101-
/opt/bin/kubectl delete cronjob "$cronjob_name" -n "$namespace" --ignore-not-found=true
114+
/opt/bin/kubectl get cronjobs -n "$namespace" -o name 2>/dev/null | \
115+
while IFS= read -r resource; do
116+
if [ -z "$resource" ]; then
117+
continue
118+
fi
119+
name=${resource#*/}
120+
cron_volumes=$(/opt/bin/kubectl get cronjob "$name" -n "$namespace" -o jsonpath='{.spec.jobTemplate.spec.template.spec.volumes[*].persistentVolumeClaim.claimName}' 2>/dev/null || echo "")
121+
if [ -n "$cron_volumes" ] && echo "$cron_volumes" | grep -F -q "$pvc_name"; then
122+
echo "$(timestamp) - Deleting CronJob: $name"
123+
/opt/bin/kubectl delete cronjob "$name" -n "$namespace" --ignore-not-found=true
102124
deleted_count=$((deleted_count + 1))
103125
fi
104126
done

‎plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,10 @@ public static String getSnapshotPath(com.linbit.linstor.api.model.StoragePool sp
204204
final String path;
205205
switch (sp.getProviderKind()) {
206206
case LVM_THIN:
207+
// device-mapper doubles every dash in the VG and LV name, so the VG part needs escaping too
208+
final String vgName = backingPool.split("/")[0];
207209
path = String.format("/dev/mapper/%s-%s_%s_%s",
208-
backingPool.split("/")[0], rscName.replace("-", "--"), suffix, snapshotName.replace("-", "--"));
210+
vgName.replace("-", "--"), rscName.replace("-", "--"), suffix, snapshotName.replace("-", "--"));
209211
break;
210212
case ZFS:
211213
case ZFS_THIN:

‎plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/util/LinstorUtilTest.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ public void testGetSnapshotPath() {
103103
Assert.assertEquals("/dev/mapper/storage-cs--cb32532a--dd8f--47e0--a81c--8a75573d3545_00000_snap3", snapPath);
104104
}
105105

106+
{
107+
// dashes in the volume group name must be escaped as well (GH issue #14011)
108+
StoragePool spLVMThin = new StoragePool();
109+
Properties lvmThinProps = new Properties();
110+
lvmThinProps.put("StorDriver/StorPoolName", "linstor_pool-lvm-thin/thin");
111+
spLVMThin.setProps(lvmThinProps);
112+
spLVMThin.setProviderKind(ProviderKind.LVM_THIN);
113+
String snapPath = LinstorUtil.getSnapshotPath(spLVMThin,
114+
"cs-12fc4055-3985-4025-8eb5-d6fd53effe37", "cs-d7aea646-5f40-46a2-b9dc-77e41ea29336");
115+
Assert.assertEquals(
116+
"/dev/mapper/linstor_pool--lvm--thin-cs--12fc4055--3985--4025--8eb5--d6fd53effe37_00000_cs--d7aea646--5f40--46a2--b9dc--77e41ea29336",
117+
snapPath);
118+
}
119+
106120
{
107121
StoragePool spZFS = new StoragePool();
108122
Properties zfsProps = new Properties();

0 commit comments

Comments
 (0)