Skip to content

Commit 151cd06

Browse files
committed
Merge remote-tracking branch 'origin/4.22' into fix-reset-link-default-value
2 parents 6b761c9 + 4708121 commit 151cd06

File tree

25 files changed

+386
-66
lines changed

25 files changed

+386
-66
lines changed

core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.cloud.utils.Pair;
5353
import com.cloud.utils.UriUtils;
5454
import com.cloud.utils.exception.CloudRuntimeException;
55+
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
5556
import com.cloud.utils.net.Proxy;
5657

5758
/**
@@ -125,6 +126,7 @@ private GetMethod createRequest(String downloadUrl) {
125126
GetMethod request = new GetMethod(downloadUrl);
126127
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
127128
request.setFollowRedirects(followRedirects);
129+
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
128130
return request;
129131
}
130132

core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
//
1919
package com.cloud.storage.template;
2020

21+
2122
import com.cloud.storage.StorageLayer;
2223
import com.cloud.utils.UriUtils;
24+
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
25+
2326
import org.apache.commons.httpclient.HttpClient;
2427
import org.apache.commons.httpclient.HttpMethod;
2528
import org.apache.commons.httpclient.HttpMethodRetryHandler;
@@ -59,6 +62,7 @@ protected GetMethod createRequest(String downloadUrl) {
5962
GetMethod request = new GetMethod(downloadUrl);
6063
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
6164
request.setFollowRedirects(followRedirects);
65+
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
6266
if (!toFileSet) {
6367
String[] parts = downloadUrl.split("/");
6468
String filename = parts[parts.length - 1];

core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.apache.commons.lang3.StringUtils;
4545

4646
import com.cloud.storage.StorageLayer;
47+
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
4748

4849
public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implements TemplateDownloader {
4950
private static final MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager();
@@ -95,6 +96,7 @@ private GetMethod createRequest(String downloadUrl) {
9596
GetMethod request = new GetMethod(downloadUrl);
9697
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);
9798
request.setFollowRedirects(followRedirects);
99+
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
98100
return request;
99101
}
100102

@@ -141,6 +143,7 @@ private void tryAndGetTotalRemoteSize() {
141143
continue;
142144
}
143145
HeadMethod headMethod = new HeadMethod(downloadUrl);
146+
headMethod.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
144147
try {
145148
if (client.executeMethod(headMethod) != HttpStatus.SC_OK) {
146149
continue;

core/src/main/java/org/apache/cloudstack/direct/download/HttpDirectTemplateDownloader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.apache.cloudstack.direct.download;
2121

22+
2223
import java.io.File;
2324
import java.io.FileOutputStream;
2425
import java.io.IOException;
@@ -32,13 +33,15 @@
3233
import com.cloud.utils.Pair;
3334
import com.cloud.utils.UriUtils;
3435
import com.cloud.utils.exception.CloudRuntimeException;
36+
import com.cloud.utils.net.HttpClientCloudStackUserAgent;
3537
import com.cloud.utils.storage.QCOW2Utils;
3638
import org.apache.commons.collections.MapUtils;
3739
import org.apache.commons.httpclient.HttpClient;
3840
import org.apache.commons.httpclient.HttpStatus;
3941
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
4042
import org.apache.commons.httpclient.methods.GetMethod;
4143
import org.apache.commons.httpclient.methods.HeadMethod;
44+
import org.apache.commons.httpclient.params.HttpMethodParams;
4245
import org.apache.commons.io.IOUtils;
4346

4447
public class HttpDirectTemplateDownloader extends DirectTemplateDownloaderImpl {
@@ -68,6 +71,7 @@ public HttpDirectTemplateDownloader(String url, Long templateId, String destPool
6871
protected GetMethod createRequest(String downloadUrl, Map<String, String> headers) {
6972
GetMethod request = new GetMethod(downloadUrl);
7073
request.setFollowRedirects(this.isFollowRedirects());
74+
request.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
7175
if (MapUtils.isNotEmpty(headers)) {
7276
for (String key : headers.keySet()) {
7377
request.setRequestHeader(key, headers.get(key));
@@ -111,6 +115,7 @@ protected Pair<Boolean, String> performDownload() {
111115
public boolean checkUrl(String url) {
112116
HeadMethod httpHead = new HeadMethod(url);
113117
httpHead.setFollowRedirects(this.isFollowRedirects());
118+
httpHead.getParams().setParameter(HttpMethodParams.USER_AGENT, HttpClientCloudStackUserAgent.CLOUDSTACK_USER_AGENT);
114119
try {
115120
int responseCode = client.executeMethod(httpHead);
116121
if (responseCode != HttpStatus.SC_OK) {

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.cloud.vm;
1919

20+
import static com.cloud.configuration.ConfigurationManagerImpl.EXPOSE_ERRORS_TO_USER;
2021
import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS;
2122

2223
import java.lang.reflect.Field;
@@ -931,10 +932,22 @@ public void start(final String vmUuid, final Map<VirtualMachineProfile.Param, Ob
931932
public void start(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) {
932933
try {
933934
advanceStart(vmUuid, params, planToDeploy, planner);
934-
} catch (ConcurrentOperationException | InsufficientCapacityException e) {
935-
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
935+
} catch (ConcurrentOperationException e) {
936+
final CallContext cctxt = CallContext.current();
937+
final Account account = cctxt.getCallingAccount();
938+
if (canExposeError(account)) {
939+
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
940+
}
941+
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to concurrent operation.", vmUuid), e).add(VirtualMachine.class, vmUuid);
942+
} catch (final InsufficientCapacityException e) {
943+
final CallContext cctxt = CallContext.current();
944+
final Account account = cctxt.getCallingAccount();
945+
if (canExposeError(account)) {
946+
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
947+
}
948+
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to insufficient capacity.", vmUuid), e).add(VirtualMachine.class, vmUuid);
936949
} catch (final ResourceUnavailableException e) {
937-
if (e.getScope() != null && e.getScope().equals(VirtualRouter.class)){
950+
if (e.getScope() != null && e.getScope().equals(VirtualRouter.class)) {
938951
throw new CloudRuntimeException("Network is unavailable. Please contact administrator", e).add(VirtualMachine.class, vmUuid);
939952
}
940953
throw new CloudRuntimeException(String.format("Unable to start a VM [%s] due to [%s].", vmUuid, e.getMessage()), e).add(VirtualMachine.class, vmUuid);
@@ -1361,6 +1374,7 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
13611374

13621375
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
13631376

1377+
Throwable lastKnownError = null;
13641378
boolean canRetry = true;
13651379
ExcludeList avoids = null;
13661380
try {
@@ -1384,7 +1398,8 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
13841398

13851399
int retry = StartRetry.value();
13861400
while (retry-- != 0) {
1387-
logger.debug("Instance start attempt #{}", (StartRetry.value() - retry));
1401+
int attemptNumber = StartRetry.value() - retry;
1402+
logger.debug("Instance start attempt #{}", attemptNumber);
13881403

13891404
if (reuseVolume) {
13901405
final List<VolumeVO> vols = _volsDao.findReadyRootVolumesByInstance(vm.getId());
@@ -1450,8 +1465,13 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
14501465
reuseVolume = false;
14511466
continue;
14521467
}
1453-
throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId(),
1454-
areAffinityGroupsAssociated(vmProfile));
1468+
String message = String.format("Unable to create a deployment for %s after %s attempts", vmProfile, attemptNumber);
1469+
if (canExposeError(account) && lastKnownError != null) {
1470+
message += String.format(" Last known error: %s", lastKnownError.getMessage());
1471+
throw new CloudRuntimeException(message, lastKnownError);
1472+
} else {
1473+
throw new InsufficientServerCapacityException(message, DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile));
1474+
}
14551475
}
14561476

14571477
avoids.addHost(dest.getHost().getId());
@@ -1619,11 +1639,15 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
16191639
throw new ExecutionException("Unable to start VM:" + vm.getUuid() + " due to error in finalizeStart, not retrying");
16201640
}
16211641
}
1622-
logger.info("Unable to start VM on {} due to {}", dest.getHost(), (startAnswer == null ? " no start answer" : startAnswer.getDetails()));
1642+
String msg = String.format("Unable to start VM on %s due to %s", dest.getHost(), startAnswer == null ? "no start command answer" : startAnswer.getDetails());
1643+
lastKnownError = new ExecutionException(msg);
1644+
16231645
if (startAnswer != null && startAnswer.getContextParam("stopRetry") != null) {
1646+
logger.error(msg, lastKnownError);
16241647
break;
16251648
}
16261649

1650+
logger.debug(msg, lastKnownError);
16271651
} catch (OperationTimedoutException e) {
16281652
logger.debug("Unable to send the start command to host {} failed to start VM: {}", dest.getHost(), vm);
16291653
if (e.isActive()) {
@@ -1633,6 +1657,7 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
16331657
throw new AgentUnavailableException("Unable to start " + vm.getHostName(), destHostId, e);
16341658
} catch (final ResourceUnavailableException e) {
16351659
logger.warn("Unable to contact resource.", e);
1660+
lastKnownError = e;
16361661
if (!avoids.add(e)) {
16371662
if (e.getScope() == Volume.class || e.getScope() == Nic.class) {
16381663
throw e;
@@ -1689,10 +1714,22 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
16891714
}
16901715

16911716
if (startedVm == null) {
1692-
throw new CloudRuntimeException("Unable to start Instance '" + vm.getHostName() + "' (" + vm.getUuid() + "), see management server log for details");
1717+
String messageTmpl = "Unable to start Instance '%s' (%s)%s";
1718+
String details;
1719+
if (canExposeError(account) && lastKnownError != null) {
1720+
details = ": " + lastKnownError.getMessage();
1721+
} else {
1722+
details = ", see management server log for details";
1723+
}
1724+
String message = String.format(messageTmpl, vm.getHostName(), vm.getUuid(), details);
1725+
throw new CloudRuntimeException(message, lastKnownError);
16931726
}
16941727
}
16951728

1729+
private boolean canExposeError(Account account) {
1730+
return (account != null && account.getType() == Account.Type.ADMIN) || Boolean.TRUE.equals(EXPOSE_ERRORS_TO_USER.value());
1731+
}
1732+
16961733
protected void updateStartCommandWithExternalDetails(Host host, VirtualMachineTO vmTO, StartCommand command) {
16971734
if (!HypervisorType.External.equals(host.getHypervisorType())) {
16981735
return;

engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ protected void init() {
193193
PersistentNetworkSearch.and("id", PersistentNetworkSearch.entity().getId(), Op.NEQ);
194194
PersistentNetworkSearch.and("guestType", PersistentNetworkSearch.entity().getGuestType(), Op.IN);
195195
PersistentNetworkSearch.and("broadcastUri", PersistentNetworkSearch.entity().getBroadcastUri(), Op.EQ);
196+
PersistentNetworkSearch.and("dc", PersistentNetworkSearch.entity().getDataCenterId(), Op.EQ);
196197
PersistentNetworkSearch.and("removed", PersistentNetworkSearch.entity().getRemoved(), Op.NULL);
197198
final SearchBuilder<NetworkOfferingVO> persistentNtwkOffJoin = _ntwkOffDao.createSearchBuilder();
198199
persistentNtwkOffJoin.and("persistent", persistentNtwkOffJoin.entity().isPersistent(), Op.EQ);

engine/schema/src/main/resources/META-INF/db/schema-42200to42210.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ UPDATE `cloud`.`configuration` SET description = 'True if the management server
3636

3737
UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template';
3838

39+
-- Update existing vm_template records with NULL type to "USER"
40+
UPDATE `cloud`.`vm_template` SET `type` = 'USER' WHERE `type` IS NULL;
41+
3942
-- Update `user.password.reset.mail.template` configuration value to match new logic
4043
UPDATE `cloud`.`configuration`
4144
SET value = 'Hello {{username}}!\nYou have requested to reset your password. Please click the following link to reset your password:\n{{{resetLink}}}\nIf you did not request a password reset,please ignore this email.\n\nRegards,\nThe CloudStack Team'

engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
4141
import org.apache.cloudstack.storage.to.VolumeObjectTO;
4242
import org.apache.commons.collections.CollectionUtils;
43+
import org.apache.cloudstack.storage.datastore.api.Volume;
4344

4445
import com.cloud.agent.api.VMSnapshotTO;
4546
import com.cloud.alert.AlertManager;
@@ -200,11 +201,35 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
200201
if (volumeIds != null && !volumeIds.isEmpty()) {
201202
List<VMSnapshotDetailsVO> vmSnapshotDetails = new ArrayList<VMSnapshotDetailsVO>();
202203
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(), "SnapshotGroupId", snapshotGroupId, false));
204+
Map<String, String> snapshotNameToSrcPathMap = new HashMap<>();
205+
for (Map.Entry<String, String> entry : srcVolumeDestSnapshotMap.entrySet()) {
206+
snapshotNameToSrcPathMap.put(entry.getValue(), entry.getKey());
207+
}
208+
209+
for (String snapshotVolumeId : volumeIds) {
210+
// Use getVolume() to fetch snapshot volume details and get its name
211+
Volume snapshotVolume = client.getVolume(snapshotVolumeId);
212+
if (snapshotVolume == null) {
213+
throw new CloudRuntimeException("Cannot find snapshot volume with id: " + snapshotVolumeId);
214+
}
215+
String snapshotName = snapshotVolume.getName();
216+
217+
// Match back to source volume path
218+
String srcVolumePath = snapshotNameToSrcPathMap.get(snapshotName);
219+
if (srcVolumePath == null) {
220+
throw new CloudRuntimeException("Cannot match snapshot " + snapshotName + " to a source volume");
221+
}
222+
223+
// Find the matching VolumeObjectTO by path
224+
VolumeObjectTO matchedVolume = volumeTOs.stream()
225+
.filter(v -> ScaleIOUtil.getVolumePath(v.getPath()).equals(srcVolumePath))
226+
.findFirst()
227+
.orElseThrow(() -> new CloudRuntimeException("Cannot find source volume for path: " + srcVolumePath));
203228

204-
for (int index = 0; index < volumeIds.size(); index++) {
205-
String volumeSnapshotName = srcVolumeDestSnapshotMap.get(ScaleIOUtil.getVolumePath(volumeTOs.get(index).getPath()));
206-
String pathWithScaleIOVolumeName = ScaleIOUtil.updatedPathWithVolumeName(volumeIds.get(index), volumeSnapshotName);
207-
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(), "Vol_" + volumeTOs.get(index).getId() + "_Snapshot", pathWithScaleIOVolumeName, false));
229+
String pathWithScaleIOVolumeName = ScaleIOUtil.updatedPathWithVolumeName(snapshotVolumeId, snapshotName);
230+
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshot.getId(),
231+
"Vol_" + matchedVolume.getId() + "_Snapshot",
232+
pathWithScaleIOVolumeName, false));
208233
}
209234

210235
vmSnapshotDetailsDao.saveDetails(vmSnapshotDetails);

plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5757
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
5858
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
59+
import org.apache.commons.collections.CollectionUtils;
5960
import org.apache.logging.log4j.Logger;
6061
import org.apache.logging.log4j.LogManager;
6162

@@ -471,6 +472,9 @@ public boolean deleteBackup(Backup backup, boolean forced) {
471472
} else {
472473
host = resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, backup.getZoneId());
473474
}
475+
if (host == null) {
476+
throw new CloudRuntimeException(String.format("Unable to find a running KVM host in zone %d to delete backup %s", backup.getZoneId(), backup.getUuid()));
477+
}
474478

475479
DeleteBackupCommand command = new DeleteBackupCommand(backup.getExternalId(), backupRepository.getType(),
476480
backupRepository.getAddress(), backupRepository.getMountOptions());
@@ -552,7 +556,14 @@ public Pair<Long, Long> getBackupStorageStats(Long zoneId) {
552556
@Override
553557
public void syncBackupStorageStats(Long zoneId) {
554558
final List<BackupRepository> repositories = backupRepositoryDao.listByZoneAndProvider(zoneId, getName());
559+
if (CollectionUtils.isEmpty(repositories)) {
560+
return;
561+
}
555562
final Host host = resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, zoneId);
563+
if (host == null) {
564+
logger.warn("Unable to find a running KVM host in zone {} to sync backup storage stats", zoneId);
565+
return;
566+
}
556567
for (final BackupRepository repository : repositories) {
557568
GetBackupStorageStatsCommand command = new GetBackupStorageStatsCommand(repository.getType(), repository.getAddress(), repository.getMountOptions());
558569
BackupStorageStatsAnswer answer;

0 commit comments

Comments
 (0)