Remove ImagetransferProgress Command

This commit is contained in:
Abhisar Sinha 2026-04-14 21:20:41 +05:30
parent 07bca60fc4
commit c40b30bc4a
8 changed files with 1 additions and 343 deletions

View File

@ -62,10 +62,6 @@ public class ImageTransferResponse extends BaseResponse {
@Param(description = "the image transfer direction: upload / download")
private String direction;
@SerializedName("progress")
@Param(description = "progress in percentage for the upload image transfer")
private Integer progress;
@SerializedName(ApiConstants.CREATED)
@Param(description = "the date created")
private Date created;
@ -102,10 +98,6 @@ public class ImageTransferResponse extends BaseResponse {
this.direction = direction;
}
public void setProgress(Integer progress) {
this.progress = progress;
}
public void setCreated(Date created) {
this.created = created;
}

View File

@ -38,11 +38,6 @@ import com.cloud.utils.component.PluggableService;
*/
public interface KVMBackupExportService extends Configurable, PluggableService {
ConfigKey<Long> ImageTransferPollingInterval = new ConfigKey<>("Advanced", Long.class,
"image.transfer.polling.interval",
"10",
"The image transfer progress polling interval in seconds.", true, ConfigKey.Scope.Global);
ConfigKey<Integer> ImageTransferIdleTimeoutSeconds = new ConfigKey<>("Advanced", Integer.class,
"image.transfer.idle.timeout.seconds",
"600",

View File

@ -1,47 +0,0 @@
//Licensed to the Apache Software Foundation (ASF) under one
//or more contributor license agreements. See the NOTICE file
//distributed with this work for additional information
//regarding copyright ownership. The ASF licenses this file
//to you under the Apache License, Version 2.0 (the
//"License"); you may not use this file except in compliance
//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 org.apache.cloudstack.backup;
import java.util.Map;
import com.cloud.agent.api.Answer;
public class GetImageTransferProgressAnswer extends Answer {
private Map<String, Long> progressMap; // transferId -> progress percentage (0-100)
public GetImageTransferProgressAnswer() {
}
public GetImageTransferProgressAnswer(GetImageTransferProgressCommand cmd, boolean success, String details) {
super(cmd, success, details);
}
public GetImageTransferProgressAnswer(GetImageTransferProgressCommand cmd, boolean success, String details,
Map<String, Long> progressMap) {
super(cmd, success, details);
this.progressMap = progressMap;
}
public Map<String, Long> getProgressMap() {
return progressMap;
}
public void setProgressMap(Map<String, Long> progressMap) {
this.progressMap = progressMap;
}
}

View File

@ -1,67 +0,0 @@
//Licensed to the Apache Software Foundation (ASF) under one
//or more contributor license agreements. See the NOTICE file
//distributed with this work for additional information
//regarding copyright ownership. The ASF licenses this file
//to you under the Apache License, Version 2.0 (the
//"License"); you may not use this file except in compliance
//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 org.apache.cloudstack.backup;
import java.util.List;
import java.util.Map;
import com.cloud.agent.api.Command;
public class GetImageTransferProgressCommand extends Command {
private List<String> transferIds;
private Map<String, String> volumePaths; // transferId -> volume path
private Map<String, Long> volumeSizes; // transferId -> volume size
public GetImageTransferProgressCommand() {
}
public GetImageTransferProgressCommand(List<String> transferIds, Map<String, String> volumePaths, Map<String, Long> volumeSizes) {
this.transferIds = transferIds;
this.volumePaths = volumePaths;
this.volumeSizes = volumeSizes;
}
public List<String> getTransferIds() {
return transferIds;
}
public void setTransferIds(List<String> transferIds) {
this.transferIds = transferIds;
}
public Map<String, String> getVolumePaths() {
return volumePaths;
}
public void setVolumePaths(Map<String, String> volumePaths) {
this.volumePaths = volumePaths;
}
public Map<String, Long> getVolumeSizes() {
return volumeSizes;
}
public void setVolumeSizes(Map<String, Long> volumeSizes) {
this.volumeSizes = volumeSizes;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -75,9 +75,6 @@ public class ImageTransferVO implements ImageTransfer {
@Column(name = "signed_ticket_id")
private String signedTicketId;
@Column(name = "progress")
private Integer progress;
@Column(name = "account_id")
Long accountId;
@ -210,15 +207,6 @@ public class ImageTransferVO implements ImageTransfer {
this.signedTicketId = signedTicketId;
}
public Integer getProgress() {
return progress;
}
public void setProgress(Integer progress) {
this.progress = progress;
this.updated = new Date();
}
@Override
public Class<?> getEntityType() {
return ImageTransfer.class;

View File

@ -1,95 +0,0 @@
//Licensed to the Apache Software Foundation (ASF) under one
//or more contributor license agreements. See the NOTICE file
//distributed with this work for additional information
//regarding copyright ownership. The ASF licenses this file
//to you under the Apache License, Version 2.0 (the
//"License"); you may not use this file except in compliance
//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.wrapper;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.cloudstack.backup.GetImageTransferProgressAnswer;
import org.apache.cloudstack.backup.GetImageTransferProgressCommand;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
@ResourceWrapper(handles = GetImageTransferProgressCommand.class)
public class LibvirtGetImageTransferProgressCommandWrapper extends CommandWrapper<GetImageTransferProgressCommand, Answer, LibvirtComputingResource> {
protected Logger logger = LogManager.getLogger(getClass());
@Override
public Answer execute(GetImageTransferProgressCommand cmd, LibvirtComputingResource resource) {
try {
List<String> transferIds = cmd.getTransferIds();
Map<String, String> volumePaths = cmd.getVolumePaths();
Map<String, Long> volumeSizes = cmd.getVolumeSizes();
Map<String, Long> progressMap = new HashMap<>();
if (transferIds == null || transferIds.isEmpty()) {
return new GetImageTransferProgressAnswer(cmd, true, "No transfers to check", progressMap);
}
for (String transferId : transferIds) {
String volumePath = volumePaths.get(transferId);
Long volumeSize = volumeSizes.get(transferId);
if (volumePath == null || volumeSize == null || volumeSize == 0) {
logger.warn("Missing volume path or size for transferId: {}", transferId);
progressMap.put(transferId, null);
continue;
}
try {
File file = new File(volumePath);
if (!file.exists()) {
logger.warn("Volume file does not exist: {}", volumePath);
progressMap.put(transferId, null);
continue;
}
long currentSize = file.length();
if (volumePath.endsWith(".qcow2") || volumePath.endsWith(".qcow")) {
try {
currentSize = KVMPhysicalDisk.getVirtualSizeFromFile(volumePath);
} catch (Exception e) {
logger.warn("Failed to get virtual size for qcow2 file: {}, using physical size", volumePath, e);
}
}
progressMap.put(transferId, currentSize);
logger.debug("Transfer {} progress, current: {})", transferId, currentSize, volumeSize);
} catch (Exception e) {
logger.error("Error getting progress for transferId: {}, path: {}", transferId, volumePath, e);
progressMap.put(transferId, null);
}
}
return new GetImageTransferProgressAnswer(cmd, true, "Progress retrieved successfully", progressMap);
} catch (Exception e) {
logger.error("Error executing GetImageTransferProgressCommand", e);
return new GetImageTransferProgressAnswer(cmd, false, "Error getting transfer progress: " + e.getMessage());
}
}
}

View File

@ -42,7 +42,7 @@ public class ImageTransferVOToImageTransferConverter {
final String basePath = VeeamControlService.ContextPath.value();
imageTransfer.setId(vo.getUuid());
imageTransfer.setHref(basePath + ImageTransfersRouteHandler.BASE_ROUTE + "/" + vo.getUuid());
imageTransfer.setActive(Boolean.toString(vo.getProgress() != null && vo.getProgress() > 0 && vo.getProgress() < 100));
imageTransfer.setActive(Boolean.toString(org.apache.cloudstack.backup.ImageTransfer.Phase.transferring.equals(vo.getPhase())));
imageTransfer.setDirection(vo.getDirection().name());
imageTransfer.setFormat("cow");
imageTransfer.setInactivityTimeout(Integer.toString(3600));

View File

@ -28,9 +28,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.inject.Inject;
@ -53,10 +50,8 @@ import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext;
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO;
import org.apache.cloudstack.jobs.JobInfo;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Component;
@ -84,7 +79,6 @@ import com.cloud.user.AccountService;
import com.cloud.user.User;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.ReflectionUse;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.exception.CloudRuntimeException;
@ -142,8 +136,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
@Inject
AsyncJobManager asyncJobManager;
private ScheduledExecutorService imageTransferStatusExecutor;
VmWorkJobHandlerProxy jobHandlerProxy = new VmWorkJobHandlerProxy(this);
private boolean isKVMBackupExportServiceSupported(Long zoneId) {
@ -878,7 +870,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
response.setDiskId(volume.getUuid());
response.setTransferUrl(imageTransferVO.getTransferUrl());
response.setPhase(imageTransferVO.getPhase().toString());
response.setProgress(imageTransferVO.getProgress());
response.setDirection(imageTransferVO.getDirection().toString());
response.setCreated(imageTransferVO.getCreated());
return response;
@ -886,24 +877,11 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
@Override
public boolean start() {
imageTransferStatusExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Image-Transfer-Status-Executor"));
long pollingInterval = ImageTransferPollingInterval.value();
imageTransferStatusExecutor.scheduleAtFixedRate(new ManagedContextRunnable() {
@Override
protected void runInContext() {
try {
pollImageTransferProgress();
} catch (final Throwable t) {
logger.warn("Catch throwable in image transfer poll task ", t);
}
}
}, pollingInterval, pollingInterval, TimeUnit.SECONDS);
return true;
}
@Override
public boolean stop() {
imageTransferStatusExecutor.shutdown();
return true;
}
@ -945,91 +923,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
}
}
private void pollImageTransferProgress() {
try {
List<ImageTransferVO> transferringTransfers = imageTransferDao.listByPhaseAndDirection(
ImageTransfer.Phase.transferring, ImageTransfer.Direction.upload);
if (transferringTransfers == null || transferringTransfers.isEmpty()) {
return;
}
Map<Long, List<ImageTransferVO>> transfersByHost = transferringTransfers.stream()
.collect(Collectors.groupingBy(ImageTransferVO::getHostId));
Map<Long, VolumeVO> transferVolumeMap = new HashMap<>();
for (Map.Entry<Long, List<ImageTransferVO>> entry : transfersByHost.entrySet()) {
Long hostId = entry.getKey();
List<ImageTransferVO> hostTransfers = entry.getValue();
try {
List<String> transferIds = new ArrayList<>();
Map<String, String> volumePaths = new HashMap<>();
Map<String, Long> volumeSizes = new HashMap<>();
for (ImageTransferVO transfer : hostTransfers) {
VolumeVO volume = volumeDao.findById(transfer.getVolumeId());
if (volume == null) {
logger.warn("Volume not found for image transfer: {}", transfer.getUuid());
imageTransferDao.remove(transfer.getId());
continue;
}
transferVolumeMap.put(transfer.getId(), volume);
String transferId = transfer.getUuid();
transferIds.add(transferId);
if (volume.getPath() == null) {
logger.warn("Volume path is null for image transfer: {}", transfer.getUuid());
continue;
}
String volumePath = getVolumePathForFileBasedBackend(volume);
volumePaths.put(transferId, volumePath);
volumeSizes.put(transferId, volume.getSize());
}
if (transferIds.isEmpty()) {
continue;
}
GetImageTransferProgressCommand cmd = new GetImageTransferProgressCommand(transferIds, volumePaths, volumeSizes);
GetImageTransferProgressAnswer answer = (GetImageTransferProgressAnswer) agentManager.send(hostId, cmd);
if (answer == null || !answer.getResult() || MapUtils.isEmpty(answer.getProgressMap())) {
logger.warn("Failed to get progress for transfers on host {}: {}", hostId,
answer != null ? answer.getDetails() : "null answer");
continue;
}
for (ImageTransferVO transfer : hostTransfers) {
String transferId = transfer.getUuid();
Long currentSize = answer.getProgressMap().get(transferId);
if (currentSize == null) {
continue;
}
VolumeVO volume = transferVolumeMap.get(transfer.getId());
long totalSize = getVolumeTotalSize(volume);
int progress = Math.max((int)((currentSize * 100) / totalSize), 100);
transfer.setProgress(progress);
if (currentSize >= 100) {
transfer.setPhase(ImageTransfer.Phase.finished);
logger.debug("Updated phase for image transfer {} to finished", transferId);
}
imageTransferDao.update(transfer.getId(), transfer);
logger.debug("Updated progress for image transfer {}: {}%", transferId, progress);
}
} catch (AgentUnavailableException | OperationTimedoutException e) {
logger.warn("Failed to communicate with host {} for image transfer progress", hostId);
} catch (Exception e) {
logger.error("Error polling image transfer progress for host " + hostId, e);
}
}
} catch (Exception e) {
logger.error("Error in pollImageTransferProgress", e);
}
}
private long getVolumeTotalSize(VolumeVO volume) {
VolumeDetailVO detail = volumeDetailsDao.findDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE);
if (detail != null) {
@ -1063,7 +956,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey[]{
ImageTransferPollingInterval,
ImageTransferIdleTimeoutSeconds,
ExposeKVMBackupExportServiceApis
};