mirror of https://github.com/apache/cloudstack.git
changes for backup job fix
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
894eef1210
commit
aa7d4bc590
|
|
@ -63,6 +63,14 @@ public class FinalizeBackupCmd extends BaseCmd implements AdminCmd {
|
|||
return backupId;
|
||||
}
|
||||
|
||||
public void setVmId(Long vmId) {
|
||||
this.vmId = vmId;
|
||||
}
|
||||
|
||||
public void setBackupId(Long backupId) {
|
||||
this.backupId = backupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
boolean result = incrementalBackupService.finalizeBackup(this);
|
||||
|
|
|
|||
|
|
@ -22,24 +22,33 @@ import javax.inject.Inject;
|
|||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.backup.BackupManager;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
|
||||
@APICommand(name = "startBackup",
|
||||
description = "Start a VM backup session (oVirt-style incremental backup)",
|
||||
responseObject = BackupResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class StartBackupCmd extends BaseCmd implements AdminCmd {
|
||||
public class StartBackupCmd extends BaseAsyncCreateCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Inject
|
||||
private BackupManager backupManager;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = UserVmResponse.class,
|
||||
|
|
@ -47,19 +56,65 @@ public class StartBackupCmd extends BaseCmd implements AdminCmd {
|
|||
description = "ID of the VM")
|
||||
private Long vmId;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME,
|
||||
type = CommandType.STRING,
|
||||
description = "the name of the backup")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION,
|
||||
type = CommandType.STRING,
|
||||
description = "the description for the backup")
|
||||
private String description;
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
BackupResponse response = incrementalBackupService.startBackup(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
try {
|
||||
Backup backup = incrementalBackupService.startBackup(this);
|
||||
BackupResponse response = backupManager.createBackupResponse(backup, null);
|
||||
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (Exception e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
Backup backup = incrementalBackupService.createBackup(this);
|
||||
|
||||
if (backup != null) {
|
||||
setEntityId(backup.getId());
|
||||
setEntityUuid(backup.getUuid());
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Backup");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_VM_BACKUP_CREATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Starting backup for Instance " + vmId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity {
|
|||
Integer getNbdPort();
|
||||
|
||||
enum Status {
|
||||
Allocated, Queued, BackingUp, BackedUp, Error, Failed, Restoring, Removed, Expunged
|
||||
Allocated, Queued, BackingUp, ReadyForTransfer, FinalizingTransfer, BackedUp, Error, Failed, Restoring, Removed, Expunged
|
||||
}
|
||||
|
||||
class Metric {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import org.apache.cloudstack.api.command.admin.backup.FinalizeImageTransferCmd;
|
|||
import org.apache.cloudstack.api.command.admin.backup.ListImageTransfersCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.ListVmCheckpointsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.CheckpointResponse;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
|
@ -44,11 +43,16 @@ public interface IncrementalBackupService extends Configurable, PluggableService
|
|||
"10",
|
||||
"The image transfer progress polling interval in seconds.", true, ConfigKey.Scope.Global);
|
||||
|
||||
/**
|
||||
* Creates a backup session for a VM
|
||||
*/
|
||||
Backup createBackup(StartBackupCmd cmd);
|
||||
|
||||
/**
|
||||
* Start a backup session for a VM
|
||||
* Creates a new checkpoint and starts NBD server for pull-mode backup
|
||||
*/
|
||||
BackupResponse startBackup(StartBackupCmd cmd);
|
||||
Backup startBackup(StartBackupCmd cmd);
|
||||
|
||||
/**
|
||||
* Finalize a backup session
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.cloud.utils.component.Adapter;
|
||||
|
||||
|
|
@ -43,6 +44,12 @@ public interface RouteHandler extends Adapter {
|
|||
return path;
|
||||
}
|
||||
|
||||
static String getRequestData(HttpServletRequest req, Logger logger) {
|
||||
String data = RouteHandler.getRequestData(req);
|
||||
logger.info("Received method: {} request. Request-data: {}", req.getMethod(), data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static String getRequestData(HttpServletRequest req) {
|
||||
String contentType = req.getContentType();
|
||||
if (contentType == null) {
|
||||
|
|
@ -52,6 +59,7 @@ public interface RouteHandler extends Adapter {
|
|||
if (!"application/json".equals(mime) && !"application/x-www-form-urlencoded".equals(mime)) {
|
||||
return null;
|
||||
}
|
||||
String result = null;
|
||||
try {
|
||||
StringBuilder data = new StringBuilder();
|
||||
String line;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
|
@ -37,8 +38,8 @@ import org.apache.cloudstack.api.ApiConstants;
|
|||
import org.apache.cloudstack.api.ApiServerService;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.FinalizeBackupCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin;
|
||||
import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd;
|
||||
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
|
||||
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
|
||||
import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd;
|
||||
|
|
@ -66,6 +67,9 @@ import org.apache.cloudstack.backup.IncrementalBackupService;
|
|||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.ImageTransferDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.jobs.JobInfo;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
|
|
@ -102,6 +106,7 @@ import org.apache.cloudstack.veeam.api.dto.VnicProfile;
|
|||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.cloud.api.query.dao.AsyncJobJoinDao;
|
||||
import com.cloud.api.query.dao.DataCenterJoinDao;
|
||||
|
|
@ -246,6 +251,9 @@ public class ServerAdapter extends ManagerBase {
|
|||
@Inject
|
||||
ApiServerService apiServerService;
|
||||
|
||||
@Inject
|
||||
AsyncJobDao asyncJobDao;
|
||||
|
||||
@Inject
|
||||
AsyncJobJoinDao asyncJobJoinDao;
|
||||
|
||||
|
|
@ -840,7 +848,7 @@ public class ServerAdapter extends ManagerBase {
|
|||
}
|
||||
|
||||
public ImageTransfer getImageTransfer(String uuid) {
|
||||
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
|
||||
ImageTransferVO vo = imageTransferDao.findByUuidIncludingRemoved(uuid);
|
||||
if (vo == null) {
|
||||
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
|
||||
}
|
||||
|
|
@ -863,7 +871,15 @@ public class ServerAdapter extends ManagerBase {
|
|||
throw new InvalidParameterValueException("Invalid or missing direction");
|
||||
}
|
||||
Format format = EnumUtils.fromString(Format.class, request.getFormat());
|
||||
return createImageTransfer(null, volumeVO.getId(), direction, format);
|
||||
Long backupId = null;
|
||||
if (request.getBackup() != null && StringUtils.isNotBlank(request.getBackup().getId())) {
|
||||
BackupVO backupVO = backupDao.findByUuid(request.getBackup().getId());
|
||||
if (backupVO == null) {
|
||||
throw new InvalidParameterValueException("Backup with ID " + request.getBackup().getId() + " not found");
|
||||
}
|
||||
backupId = backupVO.getId();
|
||||
}
|
||||
return createImageTransfer(backupId, volumeVO.getId(), direction, format);
|
||||
}
|
||||
|
||||
public boolean handleCancelImageTransfer(String uuid) {
|
||||
|
|
@ -887,7 +903,7 @@ public class ServerAdapter extends ManagerBase {
|
|||
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
|
||||
try {
|
||||
org.apache.cloudstack.backup.ImageTransfer imageTransfer =
|
||||
incrementalBackupService.createImageTransfer(volumeId, null, direction, format);
|
||||
incrementalBackupService.createImageTransfer(volumeId, backupId, direction, format);
|
||||
ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId());
|
||||
return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, this::getVolumeById);
|
||||
} finally {
|
||||
|
|
@ -1054,7 +1070,7 @@ public class ServerAdapter extends ManagerBase {
|
|||
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
|
||||
}
|
||||
List<BackupVO> backups = backupDao.searchByVmIds(List.of(vo.getId()));
|
||||
return BackupVOToBackupConverter.toBackupList(backups, id -> vo);
|
||||
return BackupVOToBackupConverter.toBackupList(backups, id -> vo, this::getHostById);
|
||||
}
|
||||
|
||||
public Backup createInstanceBackup(final String vmUuid, final Backup request) {
|
||||
|
|
@ -1062,26 +1078,26 @@ public class ServerAdapter extends ManagerBase {
|
|||
if (vmVo == null) {
|
||||
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
|
||||
}
|
||||
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
|
||||
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
|
||||
// Register a context as resource owner
|
||||
Account account = accountService.getAccount(vmVo.getAccountId());
|
||||
CallContext ctx = CallContext.register(vmVo.getUserId(), vmVo.getAccountId());
|
||||
try {
|
||||
CreateBackupCmd cmd = new CreateBackupCmd();
|
||||
StartBackupCmd cmd = new StartBackupCmd();
|
||||
ComponentContext.inject(cmd);
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(ApiConstants.VIRTUAL_MACHINE_ID, vmVo.getUuid());
|
||||
params.put(ApiConstants.NAME, request.getName());
|
||||
params.put(ApiConstants.DESCRIPTION, request.getDescription());
|
||||
ApiServerService.AsyncCmdResult result =
|
||||
apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(),
|
||||
serviceUserAccount.second());
|
||||
apiServerService.processAsyncCmd(cmd, params, ctx, vmVo.getUserId(), account);
|
||||
if (result.objectId == null) {
|
||||
throw new CloudRuntimeException("No backup ID returned");
|
||||
throw new CloudRuntimeException("Unexpected backup ID returned");
|
||||
}
|
||||
BackupVO vo = backupDao.findById(result.objectId);
|
||||
if (vo == null) {
|
||||
throw new CloudRuntimeException("Backup not found");
|
||||
}
|
||||
return BackupVOToBackupConverter.toBackup(vo, id -> vmVo);
|
||||
return BackupVOToBackupConverter.toBackup(vo, id -> vmVo, this::getHostById, this::getBackupDisks);
|
||||
} catch (Exception e) {
|
||||
throw new CloudRuntimeException("Failed to create backup: " + e.getMessage(), e);
|
||||
} finally {
|
||||
|
|
@ -1089,16 +1105,53 @@ public class ServerAdapter extends ManagerBase {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BackupVO getBackupFromJob(ApiServerService.AsyncCmdResult result, UserVmVO vmVo) {
|
||||
AsyncJobVO jobVo = null;
|
||||
// wait for job to complete and get backup ID
|
||||
long timeoutNanos = TimeUnit.MINUTES.toNanos(2);
|
||||
final long deadline = System.nanoTime() + timeoutNanos;
|
||||
long sleepMillis = 1000;
|
||||
while (System.nanoTime() < deadline) {
|
||||
jobVo = asyncJobDao.findByIdIncludingRemoved(result.jobId);
|
||||
if (jobVo == null) {
|
||||
throw new CloudRuntimeException("Failed to find job for backup creation");
|
||||
}
|
||||
if (!JobInfo.Status.IN_PROGRESS.equals(jobVo.getStatus())) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(sleepMillis);
|
||||
// back off gradually to reduce DB pressure
|
||||
sleepMillis = Math.min(5000, sleepMillis + 500);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new CloudRuntimeException("Interrupted while waiting for backup creation job", ie);
|
||||
}
|
||||
}
|
||||
// if still in progress after timeout, fail fast
|
||||
if (jobVo != null && JobInfo.Status.IN_PROGRESS.equals(jobVo.getStatus())) {
|
||||
throw new CloudRuntimeException("Timed out waiting for backup creation job");
|
||||
}
|
||||
BackupVO vo = null;
|
||||
List<BackupVO> backups = backupDao.searchByVmIds(List.of(vmVo.getId()));
|
||||
if (CollectionUtils.isNotEmpty(backups)) {
|
||||
vo = backups.get(0);
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
public Backup getBackup(String uuid) {
|
||||
BackupVO vo = backupDao.findByUuid(uuid);
|
||||
if (vo == null) {
|
||||
throw new InvalidParameterValueException("Backup with ID " + uuid + " not found");
|
||||
}
|
||||
return BackupVOToBackupConverter.toBackup(vo, id -> userVmDao.findById(id));
|
||||
return BackupVOToBackupConverter.toBackup(vo, id -> userVmDao.findById(id), this::getHostById,
|
||||
this::getBackupDisks);
|
||||
}
|
||||
|
||||
public List<Disk> listDisksByBackupUuid(final String uuid) {
|
||||
throw new InvalidParameterValueException("List Backup Disks with ID " + uuid + " not implmenented");
|
||||
throw new InvalidParameterValueException("List Backup Disks with ID " + uuid + " not implemented");
|
||||
// BackupVO vo = backupDao.findByUuid(uuid);
|
||||
// if (vo == null) {
|
||||
// throw new InvalidParameterValueException("Backup with ID " + uuid + " not found");
|
||||
|
|
@ -1106,28 +1159,28 @@ public class ServerAdapter extends ManagerBase {
|
|||
// return VolumeJoinVOToDiskConverter.toDiskList(volumes);
|
||||
}
|
||||
|
||||
public void finalizeBackup(final String vmUuid, final String uuid, String data) {
|
||||
ResourceAction action = null;
|
||||
UserVmVO vmVo = userVmDao.findByUuid(vmUuid);
|
||||
if (vmVo == null) {
|
||||
public Backup finalizeBackup(final String vmUuid, final String backupUuid) {
|
||||
UserVmVO vm = userVmDao.findByUuid(vmUuid);
|
||||
if (vm == null) {
|
||||
throw new InvalidParameterValueException("Instance with ID " + vmUuid + " not found");
|
||||
}
|
||||
BackupVO vo = backupDao.findByUuid(uuid);
|
||||
if (vo == null) {
|
||||
throw new InvalidParameterValueException("Backup with ID " + uuid + " not found");
|
||||
BackupVO backup = backupDao.findByUuid(backupUuid);
|
||||
if (backup == null) {
|
||||
throw new InvalidParameterValueException("Backup with ID " + backupUuid + " not found");
|
||||
}
|
||||
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
|
||||
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
|
||||
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
|
||||
try {
|
||||
FinalizeBackupCmd cmd = new FinalizeBackupCmd();
|
||||
ComponentContext.inject(cmd);
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(ApiConstants.VIRTUAL_MACHINE_ID, vmVo.getUuid());
|
||||
params.put(ApiConstants.BACKUP_ID, vo.getUuid());
|
||||
cmd.setBackupId(backup.getId());
|
||||
cmd.setVmId(vm.getId());
|
||||
boolean result = incrementalBackupService.finalizeBackup(cmd);
|
||||
if (!result) {
|
||||
throw new CloudRuntimeException("Failed to finalize backup");
|
||||
}
|
||||
backup = backupDao.findById(backup.getId());
|
||||
return BackupVOToBackupConverter.toBackup(backup, id -> vm, this::getHostById, this::getBackupDisks);
|
||||
} catch (Exception e) {
|
||||
throw new CloudRuntimeException("Failed to finalize backup: " + e.getMessage(), e);
|
||||
} finally {
|
||||
|
|
@ -1135,6 +1188,14 @@ public class ServerAdapter extends ManagerBase {
|
|||
}
|
||||
}
|
||||
|
||||
protected List<Disk> getBackupDisks(final BackupVO backup) {
|
||||
List<org.apache.cloudstack.backup.Backup.VolumeInfo> volumeInfos = backup.getBackedUpVolumes();
|
||||
if (CollectionUtils.isEmpty(volumeInfos)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return VolumeJoinVOToDiskConverter.toDiskListFromVolumeInfos(volumeInfos);
|
||||
}
|
||||
|
||||
public List<Checkpoint> listCheckpointsByInstanceUuid(final String uuid) {
|
||||
throw new InvalidParameterValueException("Checkpoints for VM with ID " + uuid + " not implemented");
|
||||
// UserVmVO vo = userVmDao.findByUuid(uuid);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler {
|
|||
|
||||
protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp,
|
||||
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
|
||||
String data = RouteHandler.getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
logger.info("Received POST request on /api/disks endpoint. Request-data: {}", data); // ToDo: remove
|
||||
try {
|
||||
Disk request = io.getMapper().jsonMapper().readValue(data, Disk.class);
|
||||
|
|
|
|||
|
|
@ -113,8 +113,7 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand
|
|||
|
||||
protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp,
|
||||
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
|
||||
String data = RouteHandler.getRequestData(req);
|
||||
logger.info("Received POST request on /api/imagetransfers endpoint. Request-data: {}", data);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
try {
|
||||
ImageTransfer request = io.getMapper().jsonMapper().readValue(data, ImageTransfer.class);
|
||||
ImageTransfer response = serverAdapter.handleCreateImageTransfer(request);
|
||||
|
|
|
|||
|
|
@ -246,12 +246,6 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
io.notFound(resp, null, outFormat);
|
||||
}
|
||||
|
||||
protected String getRequestData(final HttpServletRequest req) {
|
||||
String data = RouteHandler.getRequestData(req);
|
||||
logger.info("Received method: {} request. Request-data: {}", req.getMethod(), data);
|
||||
return data;
|
||||
}
|
||||
|
||||
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
|
||||
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
|
||||
final VmListQuery q = fromRequest(req);
|
||||
|
|
@ -310,7 +304,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
|
||||
protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp,
|
||||
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
|
||||
String data = getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
try {
|
||||
Vm request = io.getMapper().jsonMapper().readValue(data, Vm.class);
|
||||
Vm response = serverAdapter.createInstance(request);
|
||||
|
|
@ -332,7 +326,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
|
||||
protected void handleUpdateById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
|
||||
final VeeamControlServlet io) throws IOException {
|
||||
String data = RouteHandler.getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
logger.info("Received PUT request. Request-data: {}", data);
|
||||
try {
|
||||
Vm request = io.getMapper().jsonMapper().readValue(data, Vm.class);
|
||||
|
|
@ -397,7 +391,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
protected void handlePostDiskAttachmentForVmId(final String id, final HttpServletRequest req,
|
||||
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
|
||||
throws IOException {
|
||||
String data = getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
try {
|
||||
DiskAttachment request = io.getMapper().jsonMapper().readValue(data, DiskAttachment.class);
|
||||
DiskAttachment response = serverAdapter.handleInstanceAttachDisk(id, request);
|
||||
|
|
@ -421,7 +415,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
protected void handlePostNicForVmId(final String id, final HttpServletRequest req,
|
||||
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
|
||||
throws IOException {
|
||||
String data = getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
try {
|
||||
Nic request = io.getMapper().jsonMapper().readValue(data, Nic.class);
|
||||
Nic response = serverAdapter.handleAttachInstanceNic(id, request);
|
||||
|
|
@ -445,7 +439,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
protected void handlePostSnapshotForVmId(final String id, final HttpServletRequest req,
|
||||
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
|
||||
throws IOException {
|
||||
String data = getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
try {
|
||||
Snapshot request = io.getMapper().jsonMapper().readValue(data, Snapshot.class);
|
||||
Snapshot response = serverAdapter.handleCreateInstanceSnapshot(id, request);
|
||||
|
|
@ -486,7 +480,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
|
||||
throws IOException {
|
||||
//ToDo: implement
|
||||
String data = getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
io.badRequest(resp, "Not implemented", outFormat);
|
||||
}
|
||||
|
||||
|
|
@ -504,11 +498,11 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
protected void handlePostBackupForVmId(final String id, final HttpServletRequest req,
|
||||
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
|
||||
throws IOException {
|
||||
String data = getRequestData(req);
|
||||
String data = RouteHandler.getRequestData(req, logger);
|
||||
try {
|
||||
Backup request = io.getMapper().jsonMapper().readValue(data, Backup.class);
|
||||
Backup response = serverAdapter.createInstanceBackup(id, request);
|
||||
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, response, outFormat);
|
||||
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
|
||||
} catch (JsonProcessingException | CloudRuntimeException e) {
|
||||
io.badRequest(resp, e.getMessage(), outFormat);
|
||||
}
|
||||
|
|
@ -539,10 +533,9 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
|
|||
protected void handleFinalizeBackupById(final String vmId, final String backupId, final HttpServletRequest req,
|
||||
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
|
||||
throws IOException {
|
||||
String data = getRequestData(req);
|
||||
try {
|
||||
serverAdapter.finalizeBackup(vmId, backupId, data);
|
||||
io.getWriter().write(resp, HttpServletResponse.SC_OK, null, outFormat);
|
||||
Backup backup = serverAdapter.finalizeBackup(vmId, backupId);
|
||||
io.getWriter().write(resp, HttpServletResponse.SC_OK, backup, outFormat);
|
||||
} catch (CloudRuntimeException e) {
|
||||
io.badRequest(resp, e.getMessage(), outFormat);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,16 @@ import org.apache.cloudstack.backup.BackupVO;
|
|||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.api.VmsRouteHandler;
|
||||
import org.apache.cloudstack.veeam.api.dto.Backup;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.apache.cloudstack.veeam.api.dto.Vm;
|
||||
|
||||
import com.cloud.api.query.vo.HostJoinVO;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
|
||||
public class BackupVOToBackupConverter {
|
||||
|
||||
public static Backup toBackup(final BackupVO backupVO, final Function<Long, UserVmVO> vmResolver) {
|
||||
public static Backup toBackup(final BackupVO backupVO, final Function<Long, UserVmVO> vmResolver,
|
||||
final Function<Long, HostJoinVO> hostResolver, final Function<BackupVO, List<Disk>> disksResolver) {
|
||||
Backup backup = new Backup();
|
||||
final String basePath = VeeamControlService.ContextPath.value();
|
||||
backup.setHref(basePath + VmsRouteHandler.BASE_ROUTE + "/backups/" + backupVO.getUuid());
|
||||
|
|
@ -39,13 +42,13 @@ public class BackupVOToBackupConverter {
|
|||
backup.setName(backupVO.getName());
|
||||
backup.setDescription(backupVO.getDescription());
|
||||
backup.setCreationDate(backupVO.getDate().getTime());
|
||||
// backup.setPhase(backupVO.getPhase().name());
|
||||
// if (backupVO.getFromCheckpointId() != null) {
|
||||
// backup.setFromCheckpointId(backupVO.getFromCheckpointId().toString());
|
||||
// }
|
||||
// if (backupVO.getToCheckpointId() != null) {
|
||||
// backup.setToCheckpointId(backupVO.getToCheckpointId().toString());
|
||||
// }
|
||||
backup.setPhase(mapStatusToPhase(backupVO.getStatus()));
|
||||
if (backupVO.getFromCheckpointId() != null) {
|
||||
backup.setFromCheckpointId(backupVO.getFromCheckpointId());
|
||||
}
|
||||
if (backupVO.getToCheckpointId() != null) {
|
||||
backup.setToCheckpointId(backupVO.getToCheckpointId());
|
||||
}
|
||||
if (vmResolver != null) {
|
||||
final UserVmVO vmVO = vmResolver.apply(backupVO.getVmId());
|
||||
if (vmVO != null) {
|
||||
|
|
@ -55,10 +58,29 @@ public class BackupVOToBackupConverter {
|
|||
return backup;
|
||||
}
|
||||
|
||||
public static List<Backup> toBackupList(final List<BackupVO> backupVOs, final Function<Long, UserVmVO> vmResolver) {
|
||||
public static List<Backup> toBackupList(final List<BackupVO> backupVOs, final Function<Long, UserVmVO> vmResolver,
|
||||
final Function<Long, HostJoinVO> hostResolver) {
|
||||
return backupVOs
|
||||
.stream()
|
||||
.map(backupVO -> toBackup(backupVO, vmResolver))
|
||||
.map(backupVO -> toBackup(backupVO, vmResolver, hostResolver, null))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static String mapStatusToPhase(final BackupVO.Status status) {
|
||||
switch (status) {
|
||||
case Allocated:
|
||||
case Queued:
|
||||
return "initializing";
|
||||
case BackingUp:
|
||||
return "starting";
|
||||
case ReadyForTransfer:
|
||||
return "ready";
|
||||
case FinalizingTransfer:
|
||||
return "finalizing";
|
||||
case Restoring:
|
||||
case BackedUp:
|
||||
return "succeeded";
|
||||
}
|
||||
return "failed";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@
|
|||
|
||||
package org.apache.cloudstack.veeam.api.converter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.api.ApiService;
|
||||
import org.apache.cloudstack.veeam.api.DisksRouteHandler;
|
||||
|
|
@ -133,6 +135,21 @@ public class VolumeJoinVOToDiskConverter {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<Disk> toDiskListFromVolumeInfos(final List<Backup.VolumeInfo> volumeInfos) {
|
||||
List<Disk> disks = new ArrayList<>();
|
||||
for (Backup.VolumeInfo volumeInfo : volumeInfos) {
|
||||
Disk disk = new Disk();
|
||||
disk.setId(volumeInfo.getUuid());
|
||||
disk.setName(volumeInfo.getUuid());
|
||||
disk.setProvisionedSize(String.valueOf(volumeInfo.getSize()));
|
||||
disk.setActualSize(String.valueOf(volumeInfo.getSize()));
|
||||
disk.setTotalSize(String.valueOf(volumeInfo.getSize()));
|
||||
disk.setBootable(String.valueOf(Volume.Type.ROOT.equals(volumeInfo.getType())));
|
||||
disks.add(disk);
|
||||
}
|
||||
return disks;
|
||||
}
|
||||
|
||||
public static DiskAttachment toDiskAttachment(final VolumeJoinVO vol) {
|
||||
final DiskAttachment da = new DiskAttachment();
|
||||
final String basePath = VeeamControlService.ContextPath.value();
|
||||
|
|
|
|||
|
|
@ -38,19 +38,19 @@ import org.apache.cloudstack.api.command.admin.backup.FinalizeImageTransferCmd;
|
|||
import org.apache.cloudstack.api.command.admin.backup.ListImageTransfersCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.ListVmCheckpointsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.CheckpointResponse;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.ImageTransferDao;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
|
||||
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;
|
||||
|
||||
|
|
@ -131,7 +131,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
}
|
||||
|
||||
@Override
|
||||
public BackupResponse startBackup(StartBackupCmd cmd) {
|
||||
public Backup createBackup(StartBackupCmd cmd) {
|
||||
//ToDo: add config check, access check, resource count check, etc.
|
||||
Long vmId = cmd.getVmId();
|
||||
|
||||
VMInstanceVO vm = vmInstanceDao.findById(vmId);
|
||||
|
|
@ -148,11 +149,17 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
throw new CloudRuntimeException("Backup already in progress for VM: " + vmId);
|
||||
}
|
||||
|
||||
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
|
||||
|
||||
BackupVO backup = new BackupVO();
|
||||
backup.setVmId(vmId);
|
||||
backup.setName(vmId + "-" + DateTime.now());
|
||||
String name = cmd.getName();
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
name = vmId + "-" + DateTime.now();
|
||||
}
|
||||
backup.setName(name);
|
||||
final String description = cmd.getDescription();
|
||||
if (StringUtils.isNotEmpty(description)) {
|
||||
backup.setDescription(description);
|
||||
}
|
||||
backup.setAccountId(vm.getAccountId());
|
||||
backup.setDomainId(vm.getDomainId());
|
||||
backup.setZoneId(vm.getDataCenterId());
|
||||
|
|
@ -162,7 +169,6 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
|
||||
String toCheckpointId = "ckp-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
String fromCheckpointId = vm.getActiveCheckpointId();
|
||||
Long fromCheckpointCreateTime = vm.getActiveCheckpointCreateTime();
|
||||
|
||||
backup.setToCheckpointId(toCheckpointId);
|
||||
backup.setFromCheckpointId(fromCheckpointId);
|
||||
|
|
@ -174,27 +180,39 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
// Will be changed later if incremental was done
|
||||
backup.setType("FULL");
|
||||
|
||||
backup = backupDao.persist(backup);
|
||||
return backupDao.persist(backup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Backup startBackup(StartBackupCmd cmd) {
|
||||
BackupVO backup = backupDao.findById(cmd.getEntityId());
|
||||
Long vmId = cmd.getVmId();
|
||||
VMInstanceVO vm = vmInstanceDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + vmId);
|
||||
}
|
||||
List<VolumeVO> volumes = volumeDao.findByInstance(vmId);
|
||||
Map<String, String> diskPathUuidMap = new HashMap<>();
|
||||
for (Volume vol : volumes) {
|
||||
String volumePath = getVolumePathForFileBasedBackend(vol);
|
||||
diskPathUuidMap.put(volumePath, vol.getUuid());
|
||||
}
|
||||
long hostId = backup.getHostId();
|
||||
|
||||
Host host = hostDao.findById(hostId);
|
||||
StartBackupCommand startCmd = new StartBackupCommand(
|
||||
vm.getInstanceName(),
|
||||
toCheckpointId,
|
||||
fromCheckpointId,
|
||||
fromCheckpointCreateTime,
|
||||
nbdPort,
|
||||
backup.getToCheckpointId(),
|
||||
backup.getFromCheckpointId(),
|
||||
vm.getActiveCheckpointCreateTime(),
|
||||
backup.getNbdPort(),
|
||||
diskPathUuidMap,
|
||||
host.getPrivateIpAddress(),
|
||||
vm.getState() == State.Stopped
|
||||
);
|
||||
|
||||
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
|
||||
|
||||
StartBackupAnswer answer;
|
||||
try {
|
||||
if (dummyOffering) {
|
||||
|
|
@ -218,13 +236,14 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
// todo: set it in the backend
|
||||
backup.setType("Incremental");
|
||||
}
|
||||
backup.setStatus(Backup.Status.ReadyForTransfer);
|
||||
backupDao.update(backup.getId(), backup);
|
||||
return backup;
|
||||
}
|
||||
|
||||
BackupResponse response = new BackupResponse();
|
||||
response.setId(backup.getUuid());
|
||||
response.setVmId(vm.getUuid());
|
||||
response.setStatus(backup.getStatus());
|
||||
return response;
|
||||
protected void updateBackupState(BackupVO backup, Backup.Status newStatus) {
|
||||
backup.setStatus(newStatus);
|
||||
backupDao.update(backup.getId(), backup);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -249,9 +268,12 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
|
||||
boolean dummyOffering = isDummyOffering(backup.getBackupOfferingId());
|
||||
|
||||
updateBackupState(backup, Backup.Status.FinalizingTransfer);
|
||||
|
||||
List<ImageTransferVO> transfers = imageTransferDao.listByBackupId(backupId);
|
||||
for (ImageTransferVO transfer : transfers) {
|
||||
if (transfer.getPhase() != ImageTransferVO.Phase.finished) {
|
||||
updateBackupState(backup, Backup.Status.Failed);
|
||||
throw new CloudRuntimeException(String.format("Image transfer %s not finalized for backup: %s", transfer.getUuid(), backup.getUuid()));
|
||||
}
|
||||
imageTransferDao.remove(transfer.getId());
|
||||
|
|
@ -269,10 +291,12 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
}
|
||||
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
updateBackupState(backup, Backup.Status.Failed);
|
||||
throw new CloudRuntimeException("Failed to communicate with agent", e);
|
||||
}
|
||||
|
||||
if (!answer.getResult()) {
|
||||
updateBackupState(backup, Backup.Status.Failed);
|
||||
throw new CloudRuntimeException("Failed to stop backup: " + answer.getDetails());
|
||||
}
|
||||
}
|
||||
|
|
@ -290,7 +314,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
}
|
||||
|
||||
// Delete backup session record
|
||||
backupDao.remove(backup.getId());
|
||||
backup.setStatus(Backup.Status.BackedUp);
|
||||
backupDao.update(backupId, backup);
|
||||
|
||||
return true;
|
||||
|
||||
|
|
@ -616,8 +641,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
}
|
||||
imageTransfer.setPhase(ImageTransferVO.Phase.finished);
|
||||
imageTransferDao.update(imageTransfer.getId(), imageTransfer);
|
||||
// ToDo: check this
|
||||
// imageTransferDao.remove(imageTransfer.getId());
|
||||
imageTransferDao.remove(imageTransfer.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue