Introduce new backup provider (KBOSS) (#12758)

Co-authored-by: João Jandre <joao@scclouds.com.br>
Co-authored-by: Bernardo De Marco Gonçalves <bernardomg2004@gmail.com>
Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>
Co-authored-by: GaOrtiga <49285692+GaOrtiga@users.noreply.github.com>
This commit is contained in:
João Jandre 2026-07-14 11:19:31 -03:00 committed by GitHub
parent 0e43c6a818
commit 66132f83ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
246 changed files with 18950 additions and 981 deletions

View File

@ -488,3 +488,15 @@ iscsi.session.cleanup.enabled=false
# Optional vCenter SHA1 thumbprint for VMware to KVM conversion via VDDK, passed as
# -io vddk-thumbprint=<value>. If unset, CloudStack computes it on the KVM host via openssl.
#vddk.thumbprint=
# Timeout (in seconds) for QCOW2 delta merge operations, mainly used for classic volume snapshots, disk-only VM snapshots on file-based storage, and the KBOSS plugin.
# If a value of 0 or less is informed, the default will be used.
# qcow2.delta.merge.timeout=259200
# Maximum number of backup validation jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many validations as possible will be done at
# the same time.
# backup.validation.max.concurrent.operations.per.host=
# Maximum number of backup compression jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many compressions as possible will be
# done at the same time.
# backup.compression.max.concurrent.operations.per.host=

View File

@ -170,7 +170,8 @@ public class AgentProperties{
public static final Property<Integer> CMDS_TIMEOUT = new Property<>("cmds.timeout", 7200);
/**
* The timeout (in seconds) for the snapshot merge operation, mainly used for classic volume snapshots and disk-only VM snapshots on file-based storage.<br>
* The timeout (in seconds) for QCOW2 delta merge operations, mainly used for classic volume snapshots, disk-only VM snapshots on file-based storage, and the KBOSS plugin.
* If a value of 0 or less is informed, the default will be used.<br>
* This configuration is only considered if libvirt.events.enabled is also true. <br>
* Data type: Integer.<br>
* Default value: <code>259200</code>
@ -953,6 +954,18 @@ public class AgentProperties{
public static final Property<Boolean> VM_NETWORK_MACIP_STATIC = new Property<>("vm.network.macip.static", false, Boolean.class);
/**
* Maximum number of backup validation jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many validations as possible will be done at
* the same time.
*/
public static final Property<Integer> BACKUP_VALIDATION_MAX_CONCURRENT_OPERATIONS_PER_HOST = new Property<>("backup.validation.max.concurrent.operations.per.host", null, Integer.class);
/**
* Maximum number of backup compression jobs that can be executed at the same time. Values lower than 0 remove the limit, meaning that as many compressions as possible will be
* done at the same time.
*/
public static final Property<Integer> BACKUP_COMPRESSION_MAX_CONCURRENT_OPERATIONS_PER_HOST = new Property<>("backup.compression.max.concurrent.operations.per.host", null, Integer.class);
public static class Property <T>{
private String name;
private T defaultValue;

View File

@ -19,5 +19,5 @@
package com.cloud.agent.api.to;
public enum DataObjectType {
VOLUME, SNAPSHOT, TEMPLATE, ARCHIVE
VOLUME, SNAPSHOT, TEMPLATE, ARCHIVE, BACKUP
}

View File

@ -668,6 +668,7 @@ public class EventTypes {
public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC";
public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT";
public static final String EVENT_VM_CREATE_FROM_BACKUP = "VM.CREATE.FROM.BACKUP";
public static final String EVENT_SCREENSHOT_DOWNLOAD = "BACKUP.VALIDATION.SCREENSHOT.DOWNLOAD";
// external network device events
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";

View File

@ -20,6 +20,7 @@ import java.util.List;
import java.util.Map;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.BackupProvider;
import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.agent.api.Command;
@ -94,10 +95,10 @@ public interface HypervisorGuru extends Adapter {
Map<String, String> getClusterSettings(long vmId);
VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId,
String vmInternalName, Backup backup) throws Exception;
String vmInternalName, Backup backup, BackupProvider backupProvider) throws Exception;
boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo,
VirtualMachine vm, long poolId, Backup backup) throws Exception;
VirtualMachine vm, long poolId, Backup backup, BackupProvider backupProvider) throws Exception;
/**
* Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware.
*

View File

@ -35,7 +35,8 @@ public class Storage {
VDI(true, true, false, "vdi"),
TAR(false, false, false, "tar"),
ZIP(false, false, false, "zip"),
DIR(false, false, false, "dir");
DIR(false, false, false, "dir"),
PNG(false, false, false, "png");
private final boolean supportThinProvisioning;
private final boolean supportSparse;

View File

@ -60,7 +60,9 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
UploadError(false, "Volume upload encountered some error"),
UploadAbandoned(false, "Volume upload is abandoned since the upload was never initiated within a specified time"),
Attaching(true, "The volume is attaching to a VM from Ready state."),
Restoring(true, "The volume is being restored from backup.");
Restoring(true, "The volume is being restored from backup."),
Consolidating(true, "The volume is being flattened."),
RestoreError(false, "The volume restore encountered an error.");
boolean _transitional;
@ -153,6 +155,10 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Destroy, Event.RestoreRequested, Restoring, null));
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Restoring, Event.RestoreSucceeded, Ready, null));
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Restoring, Event.RestoreFailed, Ready, null));
s_fsm.addTransition(new StateMachine2.Transition<>(Ready, Event.ConsolidationRequested, Consolidating, null));
s_fsm.addTransition(new StateMachine2.Transition<>(Consolidating, Event.OperationSucceeded, Ready, null));
s_fsm.addTransition(new StateMachine2.Transition<>(Consolidating, Event.OperationFailed, RestoreError, null));
s_fsm.addTransition(new StateMachine2.Transition<>(RestoreError, Event.RestoreFailed, RestoreError, null));
}
}
@ -179,7 +185,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
OperationTimeout,
RestoreRequested,
RestoreSucceeded,
RestoreFailed;
RestoreFailed,
ConsolidationRequested
}
/**

View File

@ -114,7 +114,7 @@ public interface VolumeApiService {
Volume attachVolumeToVM(AttachVolumeCmd command);
Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS);
Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS, boolean allowAttachOnRestoring);
Volume detachVolumeViaDestroyVM(long vmId, long volumeId);
@ -189,7 +189,7 @@ public interface VolumeApiService {
boolean validateConditionsToReplaceDiskOfferingOfVolume(Volume volume, DiskOffering newDiskOffering, StoragePool destPool);
Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge);
Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge, Boolean countDisplayFalseInResourceCount);
void destroyVolume(long volumeId);

View File

@ -254,14 +254,14 @@ public interface ResourceLimitService {
void updateTaggedResourceLimitsAndCountsForAccounts(List<AccountResponse> responses, String tag);
void updateTaggedResourceLimitsAndCountsForDomains(List<DomainResponse> responses, String tag);
void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List<Reserver> reservations) throws ResourceAllocationException;
List<String> getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering);
List<String> getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering, Boolean enforceResourceLimitOnDisplayFalse);
void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
DiskOffering currentOffering, DiskOffering newOffering, List<Reserver> reservations) throws ResourceAllocationException;
void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List<Reserver> reservations) throws ResourceAllocationException;
void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering, Boolean countDisplayFalseInResourceCount);
void updateVmResourceCountForTemplateChange(long accountId, Boolean display, ServiceOffering offering, VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate);
@ -276,8 +276,8 @@ public interface ResourceLimitService {
void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Reserver> reservations) throws ResourceAllocationException;
void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Boolean countDisplayFalseInResourceLimit);
void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Boolean countDisplayFalseInResourceCount);
void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List<Reserver> reservations) throws ResourceAllocationException;

View File

@ -75,10 +75,11 @@ public interface UserVmService {
* Destroys one virtual machine
*
* @param cmd the API Command Object containg the parameters to use for this service action
* @param checkExpunge
* @throws ConcurrentOperationException
* @throws ResourceUnavailableException
*/
UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException;
UserVm destroyVm(DestroyVMCmd cmd, boolean checkExpunge) throws ResourceUnavailableException, ConcurrentOperationException;
/**
* Destroys one virtual machine

View File

@ -58,7 +58,10 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition,
Error(false, "VM is in error"),
Unknown(false, "VM state is unknown."),
Shutdown(false, "VM state is shutdown from inside"),
Restoring(true, "VM is being restored from backup");
Restoring(true, "VM is being restored from backup"),
BackingUp(true, "VM is being backed up"),
BackupError(false, "VM backup is in an inconsistent state. Operator should analyse the logs and restore the VM"),
RestoreError(false, "VM restore left the VM in an inconsistent state. Operator should analyse the logs and restore the VM");
private final boolean _transitional;
String _description;
@ -134,6 +137,14 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition,
s_fsm.addTransition(new Transition<State, Event>(State.Destroyed, Event.RestoringRequested, State.Restoring, null));
s_fsm.addTransition(new Transition<State, Event>(State.Restoring, Event.RestoringSuccess, State.Stopped, null));
s_fsm.addTransition(new Transition<State, Event>(State.Restoring, Event.RestoringFailed, State.Stopped, null));
s_fsm.addTransition(new Transition<>(State.Running, Event.BackupRequested, State.BackingUp, null));
s_fsm.addTransition(new Transition<>(State.Stopped, Event.BackupRequested, State.BackingUp, null));
s_fsm.addTransition(new Transition<>(State.BackingUp, Event.BackupSucceededRunning, State.Running, null));
s_fsm.addTransition(new Transition<>(State.BackingUp, Event.BackupSucceededStopped, State.Stopped, null));
s_fsm.addTransition(new Transition<>(State.BackingUp, Event.OperationFailedToError, State.BackupError, null));
s_fsm.addTransition(new Transition<>(State.BackingUp, Event.OperationFailedToRunning, State.Running, null));
s_fsm.addTransition(new Transition<>(State.BackingUp, Event.OperationFailedToStopped, State.Stopped, null));
s_fsm.addTransition(new Transition<State, Event>(State.RestoreError, Event.RestoringFailed, State.RestoreError, null));
s_fsm.addTransition(new Transition<State, Event>(State.Starting, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, Arrays.asList(new Impact[]{Impact.USAGE})));
s_fsm.addTransition(new Transition<State, Event>(State.Stopping, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, null));
@ -212,6 +223,8 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition,
ExpungeOperation,
OperationSucceeded,
OperationFailed,
OperationFailedToRunning,
OperationFailedToStopped,
OperationFailedToError,
OperationRetry,
AgentReportShutdowned,
@ -221,6 +234,10 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition,
RestoringRequested,
RestoringFailed,
RestoringSuccess,
BackupRequested,
BackupSucceededStopped,
BackupSucceededRunning,
FinalizedBackupChain,
// added for new VMSync logic
FollowAgentPowerOnReport,

View File

@ -136,4 +136,14 @@ public interface VmDetailConstants {
String ACTIVE_CHECKPOINT_CREATE_TIME = "active.checkpoint.create.time";
String LAST_CHECKPOINT_ID = "last.checkpoint.id";
String LAST_CHECKPOINT_CREATE_TIME = "last.checkpoint.create.time";
// KBOSS specific
String LINKED_VOLUMES_SECONDARY_STORAGE_UUIDS = "linkedVolumesSecondaryStorageUuids";
String VALIDATION_COMMAND = "backupValidationCommand";
String VALIDATION_COMMAND_ARGUMENTS = "backupValidationCommandArguments";
String VALIDATION_COMMAND_EXPECTED_RESULT = "backupValidationCommandExpectedResult";
String VALIDATION_COMMAND_TIMEOUT = "backupValidationCommandTimeout";
String VALIDATION_SCREENSHOT_WAIT = "backupValidationScreenshotWait";
String VALIDATION_BOOT_TIMEOUT = "backupValidationBootTimeout";
String LAST_KNOWN_STATE = "last_known_state";
}

View File

@ -83,6 +83,9 @@ public interface AlertService {
public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true, true);
public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true);
public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true);
public static final AlertType ALERT_TYPE_BACKUP_VALIDATION_FAILED = new AlertType((short)35, "ALERT.BACKUP.VALIDATION.FAILED", true, true);
public static final AlertType ALERT_TYPE_BACKUP_VALIDATION_UNABLE_TO_VALIDATE = new AlertType((short)36, "ALERT.BACKUP.VALIDATION.UNABLE.TO.VALIDATE", true, true);
public static final AlertType ALERT_TYPE_BACKUP_VALIDATION_CLEANUP_FAILED = new AlertType((short)37, "ALERT.BACKUP.VALIDATION.CLEANUP_FAILED", true, true);
public short getType() {
return type;

View File

@ -63,6 +63,7 @@ public class ApiConstants {
public static final String BACKUP_LIMIT = "backuplimit";
public static final String BACKUP_OFFERING_NAME = "backupofferingname";
public static final String BACKUP_OFFERING_ID = "backupofferingid";
public static final String BACKUP_OFFERING_DETAILS = "backupofferingdetails";
public static final String BACKUP_STORAGE_AVAILABLE = "backupstorageavailable";
public static final String BACKUP_STORAGE_LIMIT = "backupstoragelimit";
public static final String BACKUP_STORAGE_TOTAL = "backupstoragetotal";
@ -534,6 +535,7 @@ public class ApiConstants {
public static final String QUALIFIERS = "qualifiers";
public static final String QUERY_FILTER = "queryfilter";
public static final String QUIESCE_VM = "quiescevm";
public static final String QUICK_RESTORE = "quickrestore";
public static final String SCHEDULE = "schedule";
public static final String SCHEDULE_ID = "scheduleid";
public static final String SCOPE = "scope";
@ -584,6 +586,8 @@ public class ApiConstants {
public static final String STATE = "state";
public static final String STATS = "stats";
public static final String STATUS = "status";
public static final String COMPRESSION_STATUS = "compressionstatus";
public static final String VALIDATION_STATUS = "validationstatus";
public static final String STORAGE_TYPE = "storagetype";
public static final String STORAGE_POLICY = "storagepolicy";
public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled";
@ -682,6 +686,7 @@ public class ApiConstants {
public static final String ETCD_SERVICE_OFFERING_NAME = "etcdofferingname";
public static final String REMOVE_VLAN = "removevlan";
public static final String VLAN_ID = "vlanid";
public static final String ISOLATED = "isolated";
public static final String ISOLATED_PVLAN = "isolatedpvlan";
public static final String ISOLATED_PVLAN_TYPE = "isolatedpvlantype";
public static final String ISOLATION_URI = "isolationuri";
@ -1206,6 +1211,7 @@ public class ApiConstants {
public static final String CLEAN_UP_EXTRA_CONFIG = "cleanupextraconfig";
public static final String CLEAN_UP_PARAMETERS = "cleanupparameters";
public static final String VIRTUAL_SIZE = "virtualsize";
public static final String UNCOMPRESSED_SIZE = "uncompressedsize";
public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid";
public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid";
public static final String FETCH_ROUTER_HEALTH_CHECK_RESULTS = "fetchhealthcheckresults";
@ -1342,7 +1348,7 @@ public class ApiConstants {
public static final String IMPORT_SOURCE = "importsource";
public static final String TEMP_PATH = "temppath";
public static final String HEURISTIC_RULE = "heuristicrule";
public static final String HEURISTIC_TYPE_VALID_OPTIONS = "Valid options are: ISO, SNAPSHOT, TEMPLATE and VOLUME.";
public static final String HEURISTIC_TYPE_VALID_OPTIONS = "Valid options are: ISO, SNAPSHOT, BACKUP, TEMPLATE and VOLUME.";
public static final String MANAGEMENT = "management";
public static final String IS_VNF = "isvnf";
public static final String VNF_NICS = "vnfnics";
@ -1438,6 +1444,10 @@ public class ApiConstants {
public static final String VMWARE_DC = "vmwaredc";
public static final String PARAMETER_DESCRIPTION_ISOLATED_BACKUPS = "Whether the backup will be isolated, defaults to false. " +
"Isolated backups are always created as full backups in independent chains. Therefore, they will never depend on any existing backup chain " +
"and no backup chain will depend on them. Currently only supported for the KBOSS provider.";
public static final String CSS = "css";
public static final String JSON_CONFIGURATION = "jsonconfiguration";
@ -1459,6 +1469,27 @@ public class ApiConstants {
public static final String OBSOLETE_PARAMETERS = "obsoleteparameters";
public static final String EXCLUDED_PARAMETERS = "excludedparameters";
public static final String COMPRESS = "compress";
public static final String VALIDATE = "validate";
public static final String VALIDATION_STEPS = "validationsteps";
public static final String ALLOW_QUICK_RESTORE = "allowquickrestore";
public static final String ALLOW_EXTRACT_FILE = "allowextractfile";
public static final String BACKUP_CHAIN_SIZE = "backupchainsize";
public static final String COMPRESSION_LIBRARY = "compressionlibrary";
public static final String ATTEMPTS = "attempts";
public static final String EXECUTING = "executing";
public static final String SCHEDULED = "scheduled";
public static final String SCHEDULED_DATE = "scheduleddate";
public static final String BACKUP_PROVIDER = "backupprovider";
/**
* This enum specifies IO Drivers, each option controls specific policies on I/O.
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).

View File

@ -45,6 +45,8 @@ public class CreateVMFromBackupCmdByAdmin extends CreateVMFromBackupCmd implemen
@Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "destination Cluster ID to deploy the VM to - parameter available for root admin only", since = "4.21")
private Long clusterId;
private String instanceType;
public Long getPodId() {
return podId;
}
@ -52,4 +54,17 @@ public class CreateVMFromBackupCmdByAdmin extends CreateVMFromBackupCmd implemen
public Long getClusterId() {
return clusterId;
}
@Override
public String getInstanceType() {
return instanceType;
}
public CreateVMFromBackupCmdByAdmin(){}
public CreateVMFromBackupCmdByAdmin(String hypervisor, String instanceType) {
this.displayVm = false;
this.hypervisor = hypervisor;
this.instanceType = instanceType;
}
}

View File

@ -40,7 +40,7 @@ public class DestroyVolumeCmdByAdmin extends DestroyVolumeCmd implements AdminCm
@Override
public void execute() {
CallContext.current().setEventDetails("Volume Id: " + getId());
Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false);
Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false, null);
if (result != null) {
VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result);
response.setResponseName(getCommandName());

View File

@ -81,6 +81,12 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd {
since = "4.21.0")
private Boolean quiesceVM;
@Parameter(name = ApiConstants.ISOLATED,
type = CommandType.BOOLEAN,
description = ApiConstants.PARAMETER_DESCRIPTION_ISOLATED_BACKUPS,
since = "4.23.0")
private boolean isolated;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -101,6 +107,10 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd {
return quiesceVM;
}
public boolean isIsolated() {
return isolated;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -0,0 +1,185 @@
// 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
// with 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.api.command.user.backup;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
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.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.BackupOfferingResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.BackupManager;
import org.apache.cloudstack.backup.BackupOffering;
import javax.inject.Inject;
import java.util.List;
@APICommand(name = "createBackupOffering", description = "Creates a backup offering", responseObject = BackupOfferingResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}, since = "4.23.0")
public class CreateBackupOfferingCmd extends BaseCmd {
@Inject
protected BackupManager backupManager;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Backup offering name.", required = true)
private String name;
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = true,
description = "The description of the backup offering")
private String description;
@Parameter(name = ApiConstants.COMPRESS, type = CommandType.BOOLEAN, description = "Whether the backups should be compressed or not.")
private Boolean compress;
@Parameter(name = ApiConstants.VALIDATE, type = CommandType.BOOLEAN, description = "Whether the backups should be validated or not.")
private Boolean validate;
@Parameter(name = ApiConstants.VALIDATION_STEPS, type = CommandType.STRING, description = "Which validation steps should be performed. Accepts a comma-separated list of " +
"steps. Accepted values are: wait_for_boot, screenshot and execute_command.")
private String validationSteps;
@Parameter(name = ApiConstants.ALLOW_QUICK_RESTORE, type = CommandType.BOOLEAN, description = "Whether quick restore is enabled for the backups or not.")
private Boolean allowQuickRestore;
@Parameter(name = ApiConstants.ALLOW_EXTRACT_FILE, type = CommandType.BOOLEAN, description = "Whether files may be extracted from backups or not.")
private Boolean allowExtractFile;
@Parameter(name = ApiConstants.BACKUP_CHAIN_SIZE, type = CommandType.INTEGER, description = "Backup chain size for backups created with this offering.")
private Integer backupChainSize;
@Parameter(name = ApiConstants.COMPRESSION_LIBRARY, type = CommandType.STRING, description = "Compression library, for offerings that support compression. Accepted values " +
"are zstd and zlib. By default, zstd is used for images that support it. If the image only supports zlib, it will be used regardless of this parameter.")
private String compressionLibrary;
@Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
description = "Restrict the backup offering to the Zone identified by this ID.", required = true)
private Long zoneId;
@Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = CommandType.BOOLEAN,
description = "Whether users are allowed to create ad-hoc backups and backup schedules when using this offering.", required = true)
private Boolean userDrivenBackups;
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = DomainResponse.class,
description = "Restrict the backup offering to the Domains identified by these IDs.")
private List<Long> domainIds;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getName() {
return name;
}
public boolean isCompress() {
return Boolean.TRUE.equals(compress);
}
public boolean isValidate() {
return Boolean.TRUE.equals(validate);
}
public boolean isAllowQuickRestore() {
return Boolean.TRUE.equals(allowQuickRestore);
}
public boolean isAllowExtractFile() {
return Boolean.TRUE.equals(allowExtractFile);
}
public Integer getBackupChainSize() {
return backupChainSize;
}
public Backup.CompressionLibrary getCompressionLibrary() {
if (compressionLibrary == null) {
return null;
}
try {
return Backup.CompressionLibrary.valueOf(compressionLibrary);
} catch (IllegalArgumentException e) {
throw new InvalidParameterValueException(String.format("Invalid compression library, accepted values are zstd and zlib, received [%s].", compressionLibrary));
}
}
public String getValidationSteps() {
if (validationSteps == null) {
return Backup.ValidationSteps.screenshot.name();
}
StringBuilder sb = new StringBuilder();
for (String step : validationSteps.strip().split(",")) {
try {
Backup.ValidationSteps enumStep = Backup.ValidationSteps.valueOf(step);
sb.append(enumStep.name());
sb.append(",");
} catch (IllegalArgumentException ex) {
logger.error("Invalid validation step informed [{}].", step, ex);
throw new InvalidParameterValueException(String.format("Invalid validation step [%s] informed. Accepted values are: wait_for_boot, screenshot and execute_command.", step));
}
}
sb.deleteCharAt(sb.lastIndexOf(","));
return sb.toString();
}
public String getDescription() {
return description;
}
public Long getZoneId() {
return zoneId;
}
public List<Long> getDomainIds() {
return domainIds;
}
public Boolean getUserDrivenBackups() {
return userDrivenBackups;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
NetworkRuleConflictException {
BackupOffering offering = backupManager.createBackupOffering(this);
BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(offering);
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
@Override
public long getEntityOwnerId() {
return 0;
}
}

View File

@ -88,6 +88,12 @@ public class CreateBackupScheduleCmd extends BaseCmd {
since = "4.21.0")
private Boolean quiesceVM;
@Parameter(name = ApiConstants.ISOLATED,
type = CommandType.BOOLEAN,
description = ApiConstants.PARAMETER_DESCRIPTION_ISOLATED_BACKUPS,
since = "4.23.0")
private boolean isolated;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -116,6 +122,10 @@ public class CreateBackupScheduleCmd extends BaseCmd {
return quiesceVM;
}
public boolean isIsolated() {
return isolated;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -0,0 +1,94 @@
// 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
// with 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.api.command.user.backup;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.BackupResponse;
import org.apache.cloudstack.api.response.ExtractResponse;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.InternalBackupService;
import javax.inject.Inject;
@APICommand(name = "downloadValidationScreenshot", description = "Download validation screenshot of given backup.",
responseObject = ExtractResponse.class, since = "4.23.0", requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false)
public class DownloadValidationScreenshotCmd extends BaseAsyncCmd {
@Inject
private InternalBackupService internalBackupService;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@ACL
@Parameter(name = ApiConstants.BACKUP_ID, type = CommandType.UUID, entityType = BackupResponse.class, required = true,
description = "ID of the backup.")
private Long backupId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getBackupId() {
return backupId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public String getEventType() {
return EventTypes.EVENT_SCREENSHOT_DOWNLOAD;
}
@Override
public String getEventDescription() {
Backup backup = _entityMgr.findById(Backup.class, getBackupId());
if (backup == null) {
throw new InvalidParameterValueException(String.format("Unable to find backup with ID [%s].", getBackupId()));
}
return "Downloading validation screenshot of backup " + backup.getUuid();
}
@Override
public void execute() {
ExtractResponse response = internalBackupService.downloadScreenshot(getBackupId());
response.setResponseName(getCommandName());
response.setObjectName(getCommandName());
this.setResponseObject(response);
}
@Override
public long getEntityOwnerId() {
Backup backup = _entityMgr.findById(Backup.class, getBackupId());
if (backup != null) {
return backup.getAccountId();
}
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -0,0 +1,86 @@
// 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
// with 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.api.command.user.backup;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.VirtualMachineResponse;
import org.apache.cloudstack.backup.InternalBackupService;
import javax.inject.Inject;
@APICommand(name = "finishBackupChain", description = "Finish the backup chain of VM. Currently only has effect on VMs with KBOSS backup offerings.",
responseObject = SuccessResponse.class, since = "4.23.0", requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false)
public class FinishBackupChainCmd extends BaseCmd {
@Inject
private InternalBackupService internalBackupService;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@ACL
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = VirtualMachineResponse.class, required = true,
description = "ID of the VM to finish the chain.")
private Long vmId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getVmId() {
return vmId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
NetworkRuleConflictException {
boolean result = internalBackupService.finishBackupChain(getVmId());
SuccessResponse response = new SuccessResponse();
response.setSuccess(result);
response.setResponseName(getCommandName());
response.setObjectName(getCommandName());
this.setResponseObject(response);
}
@Override
public long getEntityOwnerId() {
VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, getVmId());
if (vm != null) {
return vm.getAccountId();
}
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -0,0 +1,105 @@
// 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
// with 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.api.command.user.backup;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.BackupServiceJobResponse;
import org.apache.cloudstack.api.response.BackupResponse;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
@APICommand(name = "listBackupServiceJobs", description = "List backup service jobs", responseObject = BackupServiceJobResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}, since = "4.23.0")
public class ListBackupServiceJobsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.LONG, entityType = BackupServiceJobResponse.class, description = "List only job with given ID.")
private Long id;
@Parameter(name = ApiConstants.BACKUP_ID, type = CommandType.UUID, entityType = BackupResponse.class, description = "List jobs for the given backup.")
private Long backupId;
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "List jobs in the given host. When passing this parameter, only jobs that are currently executing will be returned.")
private Long hostId;
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "List jobs in the given zone.")
private Long zoneId;
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "List jobs with the given type. Accepted values are StartCompression, FinalizeCompression and " +
"BackupValidation.")
private String type;
@Parameter(name = ApiConstants.EXECUTING, type = CommandType.BOOLEAN, description = "List executing jobs.")
private Boolean executing;
@Parameter(name = ApiConstants.SCHEDULED, type = CommandType.BOOLEAN, description = "List scheduled jobs.")
private Boolean scheduled;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getId() {
return id;
}
public Long getBackupId() {
return backupId;
}
public Long getHostId() {
return hostId;
}
public Long getZoneId() {
return zoneId;
}
public String getType() {
return type;
}
public boolean getExecuting() {
return Boolean.TRUE.equals(executing);
}
public boolean getScheduled() {
return Boolean.TRUE.equals(scheduled);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException,
NetworkRuleConflictException {
ListResponse<BackupServiceJobResponse> response = _queryService.listBackupServiceJobs(this);
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
}

View File

@ -26,6 +26,7 @@ import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.BackupResponse;
import org.apache.cloudstack.backup.BackupManager;
@ -38,6 +39,7 @@ import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.commons.lang3.BooleanUtils;
@APICommand(name = "restoreBackup",
description = "Restores an existing stopped or deleted Instance using an Instance backup",
@ -59,6 +61,14 @@ public class RestoreBackupCmd extends BaseAsyncCmd {
description = "ID of the backup")
private Long backupId;
@Parameter(name = ApiConstants.QUICK_RESTORE, type = CommandType.BOOLEAN, entityType = BackupResponse.class, description = "Whether to use the quick restore process or not. " +
"Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0")
private Boolean quickRestore;
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "If quickrestore is true, which host to start the VM on;" +
" otherwise, ignored. Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0", authorized = {RoleType.Admin})
private Long hostId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -67,6 +77,14 @@ public class RestoreBackupCmd extends BaseAsyncCmd {
return backupId;
}
public boolean isQuickRestore() {
return BooleanUtils.isTrue(quickRestore);
}
public Long getHostId() {
return hostId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -74,7 +92,7 @@ public class RestoreBackupCmd extends BaseAsyncCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = backupManager.restoreBackup(backupId);
boolean result = backupManager.restoreBackup(backupId, isQuickRestore(), getHostId());
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
response.setResponseName(getCommandName());

View File

@ -28,6 +28,7 @@ import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.BackupResponse;
@ -78,6 +79,14 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
description = "ID of the Instance where to attach the restored volume")
private Long vmId;
@Parameter(name = ApiConstants.QUICK_RESTORE, type = CommandType.BOOLEAN, description = "Whether to use the quick restore process or not. " +
"Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0")
private Boolean quickRestore;
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "If quickrestore is true, which host to start the VM on;" +
" otherwise, ignored. Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0", authorized = {RoleType.Admin})
private Long hostId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -94,6 +103,14 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
return backupId;
}
public boolean isQuickRestore() {
return org.apache.commons.lang3.BooleanUtils.isTrue(quickRestore);
}
public Long getHostId() {
return hostId;
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
@ -106,7 +123,7 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = backupManager.restoreBackupVolumeAndAttachToVM(volumeUuid, backupId, vmId);
boolean result = backupManager.restoreBackupVolumeAndAttachToVM(volumeUuid, backupId, vmId, isQuickRestore(), getHostId());
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
response.setResponseName(getCommandName());

View File

@ -419,6 +419,27 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd {
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
public CreateNetworkCmd() {
}
public CreateNetworkCmd(long networkOfferingId, String name, String displayText, String gateway, String netmask, String startIp, String endIp, long domainId,
String accountName, long zoneId, String aclType, boolean subdomainAccess, boolean displayNetwork) {
this.networkOfferingId = networkOfferingId;
this.name = name;
this.displayText = displayText;
this.gateway = gateway;
this.netmask = netmask;
this.startIp = startIp;
this.endIp = endIp;
this.domainId = domainId;
this.accountName = accountName;
this.zoneId = zoneId;
this.aclType = aclType;
this.subdomainAccess = subdomainAccess;
this.displayNetwork = displayNetwork;
}
@Override
public String getCommandName() {
return s_name;

View File

@ -36,6 +36,7 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine;
import org.apache.commons.lang3.ObjectUtils;
@APICommand(name = "createVMFromBackup",
description = "Creates and automatically starts a VM from a backup.",
@ -70,6 +71,10 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd {
@Parameter(name = ApiConstants.PRESERVE_IP, type = CommandType.BOOLEAN, description = "Use the same IP/MAC addresses as stored in the backup metadata. Works only if the original Instance is deleted and the IP/MAC address is available.")
private Boolean preserveIp;
@Parameter(name = ApiConstants.QUICK_RESTORE, type = CommandType.BOOLEAN, entityType = BackupResponse.class, description = "Whether to use the quick restore process or not. " +
"Currently this parameter is only supported by the KBOSS provider.", since = "4.23.0")
private Boolean quickRestore;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -90,6 +95,10 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd {
return (preserveIp != null) ? preserveIp : false;
}
public Boolean getQuickRestore() {
return ObjectUtils.defaultIfNull(this.quickRestore, false);
}
@Override
public void create() {
UserVm vm;

View File

@ -98,6 +98,14 @@ public class DestroyVMCmd extends BaseAsyncCmd implements UserCmd {
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
public DestroyVMCmd() {
}
public DestroyVMCmd(Long id, Boolean expunge) {
this.id = id;
this.expunge = expunge;
}
@Override
public String getCommandName() {
return s_name;
@ -136,7 +144,7 @@ public class DestroyVMCmd extends BaseAsyncCmd implements UserCmd {
@Override
public void execute() throws ResourceUnavailableException, ConcurrentOperationException {
CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID));
UserVm result = _userVmService.destroyVm(this);
UserVm result = _userVmService.destroyVm(this, true);
UserVmResponse response = new UserVmResponse();
if (result != null) {

View File

@ -85,7 +85,7 @@ public class DeleteVolumeCmd extends BaseCmd {
@Override
public void execute() throws ConcurrentOperationException {
CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID));
Volume result = _volumeService.destroyVolume(id, CallContext.current().getCallingAccount(), true, false);
Volume result = _volumeService.destroyVolume(id, CallContext.current().getCallingAccount(), true, false, null);
if (result != null) {
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);

View File

@ -116,7 +116,7 @@ public class DestroyVolumeCmd extends BaseAsyncCmd {
@Override
public void execute() {
CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID));
Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false);
Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false, null);
if (result != null) {
VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Restricted, result);
response.setResponseName(getCommandName());

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.api.response;
import java.util.Date;
import java.util.Map;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
@ -79,6 +80,10 @@ public class BackupOfferingResponse extends BaseResponse {
@Param(description = "The date this backup offering was created")
private Date created;
@SerializedName(ApiConstants.BACKUP_OFFERING_DETAILS)
@Param(description = "Details for the backup offering", since = "4.23.0")
private Map<String, String> details;
public void setId(String id) {
this.id = id;
}
@ -127,4 +132,7 @@ public class BackupOfferingResponse extends BaseResponse {
this.domain = domain;
}
public void setDetails(Map<String, String> details) {
this.details = details;
}
}

View File

@ -71,10 +71,22 @@ public class BackupResponse extends BaseResponse {
@Param(description = "Backup protected (virtual) size in bytes")
private Long protectedSize;
@SerializedName(ApiConstants.UNCOMPRESSED_SIZE)
@Param(description = "Backup uncompressed size in bytes. Only defined if backup is compressed.")
private Long uncompressedSize;
@SerializedName(ApiConstants.STATUS)
@Param(description = "Backup status")
private Backup.Status status;
@SerializedName(ApiConstants.COMPRESSION_STATUS)
@Param(description = "Backup compression status.")
private Backup.CompressionStatus compressionStatus;
@SerializedName(ApiConstants.VALIDATION_STATUS)
@Param(description = "Backup validation status.")
private Backup.ValidationStatus validationStatus;
@SerializedName(ApiConstants.VOLUMES)
@Param(description = "Backed up volumes")
private String volumes;
@ -219,6 +231,14 @@ public class BackupResponse extends BaseResponse {
this.protectedSize = protectedSize;
}
public Long getUncompressedSize() {
return uncompressedSize;
}
public void setUncompressedSize(Long uncompressedSize) {
this.uncompressedSize = uncompressedSize;
}
public Backup.Status getStatus() {
return status;
}
@ -227,6 +247,22 @@ public class BackupResponse extends BaseResponse {
this.status = status;
}
public Backup.CompressionStatus getCompressionStatus() {
return compressionStatus;
}
public void setCompressionStatus(Backup.CompressionStatus compressionStatus) {
this.compressionStatus = compressionStatus;
}
public Backup.ValidationStatus getValidationStatus() {
return validationStatus;
}
public void setValidationStatus(Backup.ValidationStatus validationStatus) {
this.validationStatus = validationStatus;
}
public String getVolumes() {
return volumes;
}

View File

@ -56,6 +56,10 @@ public class BackupScheduleResponse extends BaseResponse {
@Param(description = "maximum number of backups retained")
private Integer maxBackups;
@SerializedName(ApiConstants.ISOLATED)
@Param(description = ApiConstants.PARAMETER_DESCRIPTION_ISOLATED_BACKUPS)
private boolean isolated;
public void setId(String id) {
this.id = id;
}
@ -111,4 +115,8 @@ public class BackupScheduleResponse extends BaseResponse {
public void setQuiesceVM(Boolean quiesceVM) {
this.quiesceVM = quiesceVM;
}
public void setIsolated(boolean isolated) {
this.isolated = isolated;
}
}

View File

@ -0,0 +1,79 @@
// 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
// with 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.api.response;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import java.util.Date;
public class BackupServiceJobResponse extends BaseResponse {
@SerializedName(ApiConstants.ID)
@Param(description = "Compression job ID.")
private Long id;
@SerializedName(ApiConstants.BACKUP_ID)
@Param(description = "Backup ID.")
private String backupId;
@SerializedName(ApiConstants.HOST_ID)
@Param(description = "Host where the job is being executed.")
private String hostId;
@SerializedName(ApiConstants.ZONE_ID)
@Param(description = "Zone where the job is being executed.")
private String zoneId;
@SerializedName(ApiConstants.ATTEMPTS)
@Param(description = "Number of attempts already made to complete this job.")
private Integer attempts;
@SerializedName(ApiConstants.TYPE)
@Param(description = "Compression job type.")
private String type;
@SerializedName(ApiConstants.START_DATE)
@Param(description = "Compression job start date.")
private Date startDate;
@SerializedName(ApiConstants.SCHEDULED_DATE)
@Param(description = "Compression job scheduled start date.")
private Date scheduledDate;
@SerializedName(ApiConstants.REMOVED)
@Param(description = "Compression job scheduled removed date.")
private Date removed;
public BackupServiceJobResponse(Long id, String backupId, String zoneId, Integer attempts, String type, Date startDate, Date scheduledDate, Date removed) {
super("backupservicejob");
this.id = id;
this.backupId = backupId;
this.zoneId = zoneId;
this.attempts = attempts;
this.type = type;
this.startDate = startDate;
this.scheduledDate = scheduledDate;
this.removed = removed;
}
public void setHostId(String hostId) {
this.hostId = hostId;
}
}

View File

@ -234,6 +234,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
@Param(description = "The name of the backup offering of the Instance", since = "4.14")
private String backupOfferingName;
@SerializedName(ApiConstants.BACKUP_PROVIDER)
@Param(description = "The name of the backup provider of the offering attached to the Instance", since = "4.23.0")
private String backupProvider;
@SerializedName("forvirtualnetwork")
@Param(description = "The virtual Network for the service offering")
private Boolean forVirtualNetwork;
@ -1362,4 +1366,11 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
this.leaseExpiryDate = leaseExpiryDate;
}
public String getBackupProvider() {
return backupProvider;
}
public void setBackupProvider(String backupProvider) {
this.backupProvider = backupProvider;
}
}

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.backup;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -46,6 +47,22 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity {
Hidden
}
enum CompressionStatus {
Uncompressed, Compressing, FinalizingCompression, Compressed, CompressionError
}
enum ValidationStatus {
NotValidated, Validating, Valid, UnableToValidate, NotValid
}
enum ValidationSteps {
wait_for_boot, screenshot, execute_command
}
enum CompressionLibrary {
zstd, zlib
}
class Metric {
private Long backupSize = 0L;
private Long dataSize = 0L;
@ -132,7 +149,7 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity {
}
}
class VolumeInfo {
class VolumeInfo implements Serializable {
private String uuid;
private Volume.Type type;
private Long size;
@ -201,11 +218,14 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity {
String getType();
Date getDate();
Backup.Status getStatus();
Backup.CompressionStatus getCompressionStatus();
Backup.ValidationStatus getValidationStatus();
Long getSize();
Long getProtectedSize();
void setName(String name);
String getDescription();
void setDescription(String description);
Long getUncompressedSize();
List<VolumeInfo> getBackedUpVolumes();
long getZoneId();
Map<String, String> getDetails();

View File

@ -20,6 +20,8 @@ package org.apache.cloudstack.backup;
import java.util.List;
import java.util.Map;
import com.cloud.storage.Volume;
import com.cloud.vm.VirtualMachine;
import com.cloud.capacity.Capacity;
import com.cloud.exception.ResourceAllocationException;
import org.apache.cloudstack.api.command.admin.backup.CloneBackupOfferingCmd;
@ -31,17 +33,16 @@ import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupScheduleCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
import org.apache.cloudstack.api.command.user.backup.CreateBackupOfferingCmd;
import org.apache.cloudstack.api.response.BackupResponse;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
import com.cloud.storage.Volume;
import com.cloud.utils.Pair;
import com.cloud.utils.component.Manager;
import com.cloud.utils.component.PluggableService;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VmDiskInfo;
/**
@ -138,6 +139,12 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
*/
BackupOffering importBackupOffering(final ImportBackupOfferingCmd cmd);
/**
* Create a new Backup and Recovery policy to CloudStack. Currently only supported for KBOSS.
* @param cmd create backup offering cmd
*/
BackupOffering createBackupOffering(final CreateBackupOfferingCmd cmd);
List<Long> getBackupOfferingDomains(final Long offeringId);
/**
@ -210,7 +217,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
/**
* Restore a full VM from backup
*/
boolean restoreBackup(final Long backupId);
boolean restoreBackup(final Long backupId, boolean quickRestore, Long hostId);
Map<Long, Network.IpAddresses> getIpToNetworkMapFromBackup(Backup backup, boolean preserveIps, List<Long> networkIds);
@ -221,12 +228,12 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
/**
* Restore a backup to a new Instance
*/
boolean restoreBackupToVM(Long backupId, Long vmId) throws ResourceUnavailableException;
boolean restoreBackupToVM(Long backupId, Long vmId, boolean quickrestore) throws ResourceUnavailableException;
/**
* Restore a backed up volume and attach it to a VM
*/
boolean restoreBackupVolumeAndAttachToVM(final String backedUpVolumeUuid, final Long backupId, final Long vmId) throws Exception;
boolean restoreBackupVolumeAndAttachToVM(final String backedUpVolumeUuid, final Long backupId, final Long vmId, boolean isQuickRestore, Long hostId) throws Exception;
/**
* Deletes a backup

View File

@ -63,6 +63,17 @@ public interface BackupProvider {
*/
boolean removeVMFromBackupOffering(VirtualMachine vm);
/**
* Removes the specified backup schedule from a virtual machine.
*
* @param vm the virtual machine from which the schedule will be removed.
* @param backupSchedule the backup schedule to be removed.
* @return {@code true} if the operation was successful; {@code false} otherwise.
*/
default boolean removeVMBackupSchedule(VirtualMachine vm, BackupSchedule backupSchedule) {
return true;
}
/**
* Whether the provider will delete backups on removal of VM from the offering
* @return boolean result
@ -73,11 +84,14 @@ public interface BackupProvider {
* Starts and creates an adhoc backup process
* for a previously registered VM backup
*
* @param vm the machine to make a backup of
* @param quiesceVM instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin.
* @param vm
* the machine to make a backup of
* @param quiesceVM
* instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin.
* @param isolated
* @return the result and {code}Backup{code} {code}Object{code}
*/
Pair<Boolean, Backup> takeBackup(VirtualMachine vm, Boolean quiesceVM);
Pair<Boolean, Backup> takeBackup(VirtualMachine vm, Boolean quiesceVM, boolean isolated, Long backupScheduleId);
/**
* Delete an existing backup
@ -99,17 +113,18 @@ public interface BackupProvider {
return false;
}
Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);
Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid, boolean quickrestore);
/**
* Restore VM from backup
*/
boolean restoreVMFromBackup(VirtualMachine vm, Backup backup);
boolean restoreVMFromBackup(VirtualMachine vm, Backup backup, boolean quickRestore, Long hostId);
/**
* Restore a volume from a backup
*/
Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair<String, VirtualMachine.State> vmNameAndState);
Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid,
Pair<String, VirtualMachine.State> vmNameAndState, VirtualMachine vm, boolean quickRestore);
/**
* Syncs backup metrics (backup size, protected size) from the plugin and stores it within the provider
@ -152,5 +167,4 @@ public interface BackupProvider {
* @param zoneId the zone for which to return metrics
*/
void syncBackupStorageStats(Long zoneId);
}

View File

@ -34,4 +34,5 @@ public interface BackupSchedule extends ControlledEntity, InternalIdentity {
Boolean getQuiesceVM();
int getMaxBackups();
String getUuid();
boolean isIsolated();
}

View File

@ -0,0 +1,142 @@
// 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
// with 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 com.cloud.storage.Volume;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
import org.apache.cloudstack.framework.config.ConfigKey;
import java.util.Set;
public interface InternalBackupProvider extends BackupProvider {
String VM_WORK_JOB_HANDLER = InternalBackupService.class.getSimpleName();
ConfigKey<Integer> backupCompressionTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.compression.timeout", "28800", "Backup compression timeout (in " +
"seconds). Will only start counting once the backup compression async job actually starts. This setting is currently only applicable to KBOSS.", true,
ConfigKey.Scope.Cluster);
ConfigKey<Double> backupCompressionMinimumFreeStorage = new ConfigKey<>("Advanced", Double.class, "backup.compression.minimum.free.storage", "1", "The minimum " +
"amount of free storage that should be available to start the compression. This configuration uses a multiplier on the backup size, by default, it needs the same " +
"amount of free storage as the backup uses while uncompressed. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Zone);
ConfigKey<Integer> backupCompressionCoroutines = new ConfigKey<>("Advanced", Integer.class, "backup.compression.coroutines", "1", "Number of parallel coroutines " +
"for the compression process. This is translated to qemu-img '-m' parameter. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Cluster);
ConfigKey<Integer> backupCompressionRateLimit = new ConfigKey<>("Advanced", Integer.class, "backup.compression.rate.limit", "0", "Limit the compression rate to " +
"this configuration's value (in MB/s). Values lower than 1 disable the limit. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Cluster);
ConfigKey<Integer> backupValidationTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.validation.timeout", "3600", "Backup validation job timeout (in " +
"seconds). Will only start counting once the backup validation async job actually starts. This setting is currently only applicable to KBOSS.", true, ConfigKey.Scope.Cluster);
/**
* Actually execute the backup after being queued.
* */
default Pair<Boolean, Long> orchestrateTakeBackup(Backup backup, boolean quiesceVm, boolean isolated) {
return null;
}
/**
* Actually delete the backup after being queued.
* */
default Boolean orchestrateDeleteBackup(Backup backup, boolean forced) {
return null;
}
/**
* Actually restore the backup after being queued.
* */
default Boolean orchestrateRestoreVMFromBackup(Backup backup, VirtualMachine vm, boolean quickRestore, Long hostId, boolean sameVmAsBackup) {
return null;
}
/**
* This method should be overwritten by any backup providers that want to schedule their backup restore jobs in the same queue as the VM jobs.
* Otherwise, just use the restoreBackedUpVolume method.
* */
default Pair<Boolean, String> orchestrateRestoreBackedUpVolume(Backup backup, VirtualMachine vm, Backup.VolumeInfo backupVolumeInfo, String hostIp, boolean quickRestore) {
return null;
}
/**
* This method should be overwritten by any native backup providers that want to allow backup compression through ACS.<br/>
* The compression is done in two steps:<br/>
* 1) Compress the backup to a different file;<br/>
* 2) Switch the old file for the newly compressed one.<br/>
* <br/> <br/>
* This method is supposed to execute step 1.
*
* @return
*/
default boolean startBackupCompression(long backupId, long hostId) {
return false;
}
/**
* This method should be overwritten by any native backup providers that want to allow backup compression through ACS.<br/>
* The compression is done in two steps:<br/>
* 1) Compress the backup to a different file;<br/>
* 2) Switch the old file for the newly compressed one.<br/>
* <br/> <br/>
* This method is supposed to execute step 2.
*
* @return
*/
default boolean finalizeBackupCompression(long backupId, long hostId) {
return false;
}
default boolean validateBackup(long backupId, long hostId) {
return false;
}
/**
* This method should be overwritten by any native backup providers that allow volume detach but need to prepare it beforehand.
* */
default void prepareVolumeForDetach(Volume volume, VirtualMachine virtualMachine) {
}
/**
* This method should be overwritten by any native backup providers that allow volume migration but need to prepare it beforehand.
* */
default void prepareVolumeForMigration(Volume volume, VirtualMachine virtualMachine) {
}
/**
* This method should be overwritten by any native backup providers that must update metadata regarding a volume after certain operations (such as after a volume migration).
* */
default void updateVolumeId(VirtualMachine virtualMachine, long oldVolumeId, long newVolumeId) {
}
default Set<String> getSecondaryStorageUrls(UserVm userVm) {
return Set.of();
}
/**
* This method should be overwritten by any native backup providers that are compatible with VM Snapshots but need to prepare the VM to be reverted.
* Currently, the only strategy that calls this method is the {@code KvmFileBasedStorageVmSnapshotStrategy}.
* */
default void prepareVmForSnapshotRevert(VMSnapshot vmSnapshot, VirtualMachine virtualMachine) {
}
default boolean finishBackupChains(VirtualMachine virtualMachine) {
return false;
}
}

View File

@ -0,0 +1,54 @@
// 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
// with 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 com.cloud.agent.api.Command;
import com.cloud.agent.api.to.DataTO;
import com.cloud.storage.Volume;
import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
import org.apache.cloudstack.api.response.ExtractResponse;
import java.util.Set;
public interface InternalBackupService {
void configureChainInfo(DataTO volumeTo, Command cmd);
void cleanupBackupMetadata(long volumeId);
void prepareVolumeForDetach(Volume volume, VirtualMachine virtualMachine);
void prepareVolumeForMigration(Volume volume);
void prepareVmForSnapshotRevert(VMSnapshot vmSnapshot);
void updateVolumeId(long oldVolumeId, long newVolumeId);
Set<String> getSecondaryStorageUrls(UserVm userVm);
boolean startBackupCompression(long backupId, long hostId, long zoneId);
boolean finalizeBackupCompression(long backupId, long hostId, long zoneId);
boolean validateBackup(long backupId, long hostId, long zoneId);
ExtractResponse downloadScreenshot(long backupId);
boolean finishBackupChain(long vmId);
}

View File

@ -41,6 +41,7 @@ import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupServiceJobsCmd;
import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.api.command.user.iso.ListIsosCmd;
@ -63,6 +64,7 @@ import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.AsyncJobResponse;
import org.apache.cloudstack.api.response.BackupServiceJobResponse;
import org.apache.cloudstack.api.response.BucketResponse;
import org.apache.cloudstack.api.response.DetailOptionsResponse;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
@ -119,7 +121,7 @@ public interface QueryService {
"Determines whether users can view certain VM settings. When set to empty, default value used is: rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null);
ConfigKey<String> UserVMReadOnlyDetails = new ConfigKey<>(String.class,
"user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController",
"user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController, backupValidationCommandTimeout, backupValidationScreenshotWait, backupValidationBootTimeout",
"List of read-only VM settings/details as comma separated string", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null, "");
ConfigKey<Boolean> SortKeyAscending = new ConfigKey<>("Advanced", Boolean.class, "sortkey.algorithm", "true",
@ -224,4 +226,6 @@ public interface QueryService {
ListResponse<ObjectStoreResponse> searchForObjectStores(ListObjectStoragePoolsCmd listObjectStoragePoolsCmd);
ListResponse<BucketResponse> searchForBuckets(ListBucketsCmd listBucketsCmd);
ListResponse<BackupServiceJobResponse> listBackupServiceJobs(ListBackupServiceJobsCmd cmd);
}

View File

@ -18,8 +18,8 @@ package org.apache.cloudstack.secstorage.heuristics;
/**
* The type of the heuristic used in the allocation process of secondary storage resources.
* Valid options are: {@link #ISO}, {@link #SNAPSHOT}, {@link #TEMPLATE} and {@link #VOLUME}
* Valid options are: {@link #ISO}, {@link #SNAPSHOT}, {@link #TEMPLATE}, {@link #VOLUME} and {@link #BACKUP}
*/
public enum HeuristicType {
ISO, SNAPSHOT, TEMPLATE, VOLUME
ISO, SNAPSHOT, TEMPLATE, VOLUME, BACKUP
}

View File

@ -627,6 +627,11 @@
<artifactId>cloud-plugin-integrations-veeam-control-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-backup-kvm-backup-on-secondary-storage</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-integrations-kubernetes-service</artifactId>

View File

@ -0,0 +1,41 @@
// 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
// with 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.agent.api;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import java.util.List;
public class MigrateBackupsBetweenSecondaryStoragesCommand extends MigrateBetweenSecondaryStoragesCommand {
List<List<DataTO>> backupChain;
public MigrateBackupsBetweenSecondaryStoragesCommand() {
}
public MigrateBackupsBetweenSecondaryStoragesCommand(List<List<DataTO>> backupChain, DataStoreTO srcDataStore, DataStoreTO destDataStore) {
super(srcDataStore, destDataStore);
this.backupChain = backupChain;
}
public List<List<DataTO>> getBackupChain() {
return backupChain;
}
}

View File

@ -0,0 +1,48 @@
// 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
// with 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.agent.api;
import com.cloud.agent.api.to.DataStoreTO;
public abstract class MigrateBetweenSecondaryStoragesCommand extends Command {
DataStoreTO srcDataStore;
DataStoreTO destDataStore;
public MigrateBetweenSecondaryStoragesCommand() {
}
public MigrateBetweenSecondaryStoragesCommand(DataStoreTO srcDataStore, DataStoreTO destDataStore) {
this.srcDataStore = srcDataStore;
this.destDataStore = destDataStore;
}
@Override
public boolean executeInSequence() {
return false;
}
public DataStoreTO getSrcDataStore() {
return srcDataStore;
}
public DataStoreTO getDestDataStore() {
return destDataStore;
}
}

View File

@ -0,0 +1,41 @@
//
// 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
// with 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.agent.api;
import com.cloud.utils.Pair;
import java.util.List;
public class MigrateBetweenSecondaryStoragesCommandAnswer extends Answer {
List<Pair<Long, String>> migratedResourcesIdAndCheckpointPath;
public MigrateBetweenSecondaryStoragesCommandAnswer() {
}
public MigrateBetweenSecondaryStoragesCommandAnswer(MigrateBetweenSecondaryStoragesCommand cmd, boolean success, String result, List<Pair<Long, String>> migratedResourcesIdAndCheckpointPath) {
super(cmd, success, result);
this.migratedResourcesIdAndCheckpointPath = migratedResourcesIdAndCheckpointPath;
}
public List<Pair<Long, String>> getMigratedResources() {
return migratedResourcesIdAndCheckpointPath;
}
}

View File

@ -22,13 +22,15 @@ package com.cloud.agent.api;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
import java.util.List;
/**
*/
public class StartCommand extends Command {
VirtualMachineTO vm;
String hostIp;
boolean executeInSequence = false;
String secondaryStorage;
private List<String> secondaryStorages;
public VirtualMachineTO getVirtualMachine() {
return vm;
@ -50,18 +52,18 @@ public class StartCommand extends Command {
this.vm = vm;
this.hostIp = host.getPrivateIpAddress();
this.executeInSequence = executeInSequence;
this.secondaryStorage = null;
this.secondaryStorages = null;
}
public String getHostIp() {
return this.hostIp;
}
public String getSecondaryStorage() {
return this.secondaryStorage;
public List<String> getSecondaryStorages() {
return this.secondaryStorages;
}
public void setSecondaryStorage(String secondary) {
this.secondaryStorage = secondary;
public void setSecondaryStorages(List<String> secondary) {
this.secondaryStorages = secondary;
}
}

View File

@ -20,20 +20,19 @@ package com.cloud.agent.api.storage;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.utils.Pair;
import java.util.Map;
public class CreateDiskOnlyVmSnapshotAnswer extends Answer {
protected Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath;
protected Map<String, Long> mapVolumeToSnapshotSize;
public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath) {
public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map<String, Long> mapVolumeToSnapshotSize) {
super(command, success, details);
this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath;
this.mapVolumeToSnapshotSize = mapVolumeToSnapshotSize;
}
public Map<String, Pair<Long, String>> getMapVolumeToSnapshotSizeAndNewVolumePath() {
return mapVolumeToSnapshotSizeAndNewVolumePath;
public Map<String, Long> getMapVolumeToSnapshotSize() {
return mapVolumeToSnapshotSize;
}
}

View File

@ -21,6 +21,7 @@ package com.cloud.agent.api.storage;
import com.cloud.agent.api.VMSnapshotBaseCommand;
import com.cloud.agent.api.VMSnapshotTO;
import com.cloud.utils.Pair;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
@ -30,12 +31,19 @@ public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand {
protected VirtualMachine.State vmState;
public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<VolumeObjectTO> volumeTOs, String guestOSType, VirtualMachine.State vmState) {
super(vmName, snapshot, volumeTOs, guestOSType);
List<Pair<VolumeObjectTO, String>> volumeTosAndNewPaths;
public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<Pair<VolumeObjectTO, String>> volumeTosAndNewPaths, String guestOSType, VirtualMachine.State vmState) {
super(vmName, snapshot, null, guestOSType);
this.vmState = vmState;
this.volumeTosAndNewPaths = volumeTosAndNewPaths;
}
public VirtualMachine.State getVmState() {
return vmState;
}
public List<Pair<VolumeObjectTO, String>> getVolumeTosAndNewPaths() {
return volumeTosAndNewPaths;
}
}

View File

@ -19,28 +19,28 @@
package com.cloud.agent.api.storage;
import com.cloud.agent.api.Command;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.storage.to.DeltaMergeTreeTO;
import java.util.List;
public class MergeDiskOnlyVmSnapshotCommand extends Command {
private List<SnapshotMergeTreeTO> snapshotMergeTreeToList;
private VirtualMachine.State vmState;
private List<DeltaMergeTreeTO> snapshotMergeTreeToList;
private boolean isVmRunning;
private String vmName;
public MergeDiskOnlyVmSnapshotCommand(List<SnapshotMergeTreeTO> snapshotMergeTreeToList, VirtualMachine.State vmState, String vmName) {
public MergeDiskOnlyVmSnapshotCommand(List<DeltaMergeTreeTO> snapshotMergeTreeToList, boolean isVmRunning, String vmName) {
this.snapshotMergeTreeToList = snapshotMergeTreeToList;
this.vmState = vmState;
this.isVmRunning = isVmRunning;
this.vmName = vmName;
}
public List<SnapshotMergeTreeTO> getSnapshotMergeTreeToList() {
public List<DeltaMergeTreeTO> getDeltaMergeTreeToList() {
return snapshotMergeTreeToList;
}
public VirtualMachine.State getVmState() {
return vmState;
public boolean isVmRunning() {
return isVmRunning;
}
public String getVmName() {

View File

@ -85,4 +85,8 @@ public interface StorageProcessor {
public Answer checkDataStoreStoragePolicyCompliance(CheckDataStoreStoragePolicyComplianceCommand cmd);
public Answer syncVolumePath(SyncVolumePathCommand cmd);
default Answer deleteBackup(DeleteCommand cmd) {
return new Answer(cmd, false, "Operation not implemented");
}
}

View File

@ -154,6 +154,8 @@ public class StorageSubsystemCommandHandlerBase implements StorageSubsystemComma
answer = processor.deleteVolume(cmd);
} else if (data.getObjectType() == DataObjectType.SNAPSHOT) {
answer = processor.deleteSnapshot(cmd);
} else if (data.getObjectType() == DataObjectType.BACKUP) {
answer = processor.deleteBackup(cmd);
} else {
answer = new Answer(cmd, false, "unsupported type");
}

View File

@ -0,0 +1,46 @@
/*
* 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
* with 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 org.apache.commons.collections4.MapUtils;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.utils.Pair;
public class CleanupKbossBackupErrorAnswer extends Answer {
private Map<String, Pair<String, Boolean>> volumeIdToPathAndChainEnded;
private boolean vmRunning;
public CleanupKbossBackupErrorAnswer(Command cmd, Map<String, Pair<String, Boolean>> volumeIdToPathAndChainEnded, boolean vmRunning) {
super(cmd, MapUtils.isNotEmpty(volumeIdToPathAndChainEnded), null);
this.volumeIdToPathAndChainEnded = volumeIdToPathAndChainEnded;
this.vmRunning = vmRunning;
}
public Map<String, Pair<String, Boolean>> getVolumeIdToPathAndChainEnded() {
return volumeIdToPathAndChainEnded;
}
public boolean isVmRunning() {
return vmRunning;
}
}

View File

@ -0,0 +1,82 @@
// 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
// with 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 com.cloud.agent.api.Command;
import org.apache.cloudstack.storage.to.KbossTO;
import java.util.List;
public class CleanupKbossBackupErrorCommand extends Command {
private boolean runningVM;
private boolean errorOnCreate;
private boolean endOfChain;
private boolean isTopDelta;
private String vmName;
private String imageStoreUrl;
private List<KbossTO> kbossTOS;
public CleanupKbossBackupErrorCommand(boolean runningVM, boolean errorOnCreate, boolean endOfChain, boolean isTopDelta, String vmName, String imageStoreUrl, List<KbossTO> kbossTOS) {
this.errorOnCreate = errorOnCreate;
this.runningVM = runningVM;
this.endOfChain = endOfChain;
this.isTopDelta = isTopDelta;
this.vmName = vmName;
this.imageStoreUrl = imageStoreUrl;
this.kbossTOS = kbossTOS;
}
public boolean isErrorOnCreate() {
return errorOnCreate;
}
public boolean isEndOfChain() {
return endOfChain;
}
public boolean isTopDelta() {
return isTopDelta;
}
public boolean isRunningVM() {
return runningVM;
}
public String getVmName() {
return vmName;
}
public String getImageStoreUrl() {
return imageStoreUrl;
}
public List<KbossTO> getKbossTOs() {
return kbossTOS;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,49 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import java.util.Set;
public class CleanupKbossValidationCommand extends Command {
private String vmName;
private Set<String> secondaryStorages;
public CleanupKbossValidationCommand(String vmName, Set<String> secondaryStorages) {
this.vmName = vmName;
this.secondaryStorages = secondaryStorages;
}
public String getVmName() {
return vmName;
}
public Set<String> getSecondaryStorages() {
return secondaryStorages;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,78 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import org.apache.cloudstack.storage.to.DeltaMergeTreeTO;
import java.util.List;
public class CompressBackupCommand extends Command {
private List<DeltaMergeTreeTO> backupDeltasToCompress;
private List<String> backupChainImageStoreUrls;
private long minFreeStorage;
private Backup.CompressionLibrary compressionLib;
private int coroutines;
private int rateLimit;
public CompressBackupCommand(List<DeltaMergeTreeTO> backupDeltasToCompress, List<String> backupChainImageStoreUrls, long minFreeStorage, Backup.CompressionLibrary compressionLib, int coroutines, int rateLimit) {
this.backupChainImageStoreUrls = backupChainImageStoreUrls;
this.backupDeltasToCompress = backupDeltasToCompress;
this.minFreeStorage = minFreeStorage;
this.compressionLib = compressionLib;
this.coroutines = coroutines;
this.rateLimit = rateLimit;
}
public List<DeltaMergeTreeTO> getBackupDeltasToCompress() {
return backupDeltasToCompress;
}
public List<String> getBackupChainImageStoreUrls() {
return backupChainImageStoreUrls;
}
public long getMinFreeStorage() {
return minFreeStorage;
}
public Backup.CompressionLibrary getCompressionLib() {
return compressionLib;
}
public int getCoroutines() {
return coroutines;
}
public int getRateLimit() {
return rateLimit;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,37 @@
// 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
// with 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 com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import java.util.List;
public class ConsolidateVolumesAnswer extends Answer {
private List<VolumeObjectTO> successfullyConsolidatedVolumes;
public ConsolidateVolumesAnswer(Command command, boolean success, String details, List<VolumeObjectTO> successfullyConsolidatedVolumes) {
super(command, success, details);
this.successfullyConsolidatedVolumes = successfullyConsolidatedVolumes;
}
public List<VolumeObjectTO> getSuccessfullyConsolidatedVolumes() {
return successfullyConsolidatedVolumes;
}
}

View File

@ -0,0 +1,56 @@
// 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
// with 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 com.cloud.agent.api.Command;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import java.util.List;
import java.util.stream.Collectors;
public class ConsolidateVolumesCommand extends Command {
private List<VolumeObjectTO> volumesToConsolidate;
private List<String> secondaryStorageUuids;
private String vmName;
public ConsolidateVolumesCommand(List<VolumeInfo> volumesToConsolidate, List<String> secondaryStorageUuids, String vmName) {
this.volumesToConsolidate = volumesToConsolidate.stream().map(vol -> (VolumeObjectTO)vol.getTO()).collect(Collectors.toList());
this.secondaryStorageUuids = secondaryStorageUuids;
this.vmName = vmName;
}
public List<VolumeObjectTO> getVolumesToConsolidate() {
return volumesToConsolidate;
}
public List<String> getSecondaryStorageUuids() {
return secondaryStorageUuids;
}
public String getVmName() {
return vmName;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,49 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import org.apache.cloudstack.storage.to.BackupDeltaTO;
import java.util.List;
public class FinalizeBackupCompressionCommand extends Command {
private boolean cleanup;
private List<BackupDeltaTO> backupDeltaTO;
public FinalizeBackupCompressionCommand(boolean cleanup, List<BackupDeltaTO> backupDeltaTO) {
this.cleanup = cleanup;
this.backupDeltaTO = backupDeltaTO;
}
public boolean isCleanup() {
return cleanup;
}
public List<BackupDeltaTO> getBackupDeltaTOList() {
return backupDeltaTO;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,52 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import com.cloud.utils.Pair;
import org.apache.cloudstack.storage.to.BackupDeltaTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import java.util.List;
import java.util.Set;
public class PrepareValidationCommand extends Command {
private List<Pair<BackupDeltaTO, VolumeObjectTO>> backupToVolumeList;
private Set<String> imageStoreSet;
public PrepareValidationCommand(List<Pair<BackupDeltaTO, VolumeObjectTO>> backupToVolumeList, Set<String> imageStoreSet) {
this.backupToVolumeList = backupToVolumeList;
this.imageStoreSet = imageStoreSet;
}
public List<Pair<BackupDeltaTO, VolumeObjectTO>> getBackupToVolumeList() {
return backupToVolumeList;
}
public Set<String> getImageStoreSet() {
return imageStoreSet;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,41 @@
// 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
// with 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 com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import java.util.Set;
public class RestoreKbossBackupAnswer extends Answer {
private Set<String> secondaryStorageUuids;
public RestoreKbossBackupAnswer(Command command, Set<String> secondaryStorageUuids) {
super(command);
this.secondaryStorageUuids = secondaryStorageUuids;
}
public RestoreKbossBackupAnswer(Command command, Exception e, Set<String> secondaryStorageUuids) {
super(command, e);
this.secondaryStorageUuids = secondaryStorageUuids;
}
public Set<String> getSecondaryStorageUuids() {
return secondaryStorageUuids;
}
}

View File

@ -0,0 +1,66 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import com.cloud.utils.Pair;
import org.apache.cloudstack.storage.to.BackupDeltaTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import java.util.Set;
public class RestoreKbossBackupCommand extends Command {
private Set<BackupDeltaTO> deltasToRemove;
private Set<Pair<BackupDeltaTO, VolumeObjectTO>> backupAndVolumePairs;
private Set<String> secondaryStorageUrls;
private boolean quickRestore;
public RestoreKbossBackupCommand(Set<BackupDeltaTO> deltasToRemove, Set<Pair<BackupDeltaTO, VolumeObjectTO>> backupAndVolumePairs, Set<String> secondaryStorageUrls,
boolean quickRestore) {
this.deltasToRemove = deltasToRemove;
this.backupAndVolumePairs = backupAndVolumePairs;
this.secondaryStorageUrls = secondaryStorageUrls;
this.quickRestore = quickRestore;
}
@Override
public boolean executeInSequence() {
return false;
}
public Set<BackupDeltaTO> getDeltasToRemove() {
return deltasToRemove;
}
public Set<Pair<BackupDeltaTO, VolumeObjectTO>> getBackupAndVolumePairs() {
return backupAndVolumePairs;
}
public Set<String> getSecondaryStorageUrls() {
return secondaryStorageUrls;
}
public boolean isQuickRestore() {
return quickRestore;
}
}

View File

@ -0,0 +1,47 @@
// 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
// with 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 com.cloud.agent.api.Command;
import org.apache.cloudstack.storage.to.BackupDeltaTO;
import java.util.List;
public class TakeBackupHashCommand extends Command {
private List<BackupDeltaTO> backupDeltaTOList;
private String backupUuid;
public TakeBackupHashCommand(List<BackupDeltaTO> backupDeltaTOList, String backupUuid) {
this.backupDeltaTOList = backupDeltaTOList;
this.backupUuid = backupUuid;
}
public List<BackupDeltaTO> getBackupDeltaTOList() {
return backupDeltaTOList;
}
public String getBackupUuid() {
return backupUuid;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,59 @@
//
// 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
// with 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 com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.BackupException;
import java.util.Map;
public class TakeKbossBackupAnswer extends Answer {
private Map<String, String> mapVolumeUuidToNewVolumePath;
private Map<String, Pair<String, Long>> mapVolumeUuidToDeltaPathOnSecondaryAndSize;
private boolean isVmConsistent = true;
public TakeKbossBackupAnswer(Command command, boolean success, Map<String, String> mapVolumeUuidToNewVolumePath,
Map<String, Pair<String, Long>> mapVolumeUuidToDeltaPathOnSecondaryAndSize) {
super(command, success, null);
this.mapVolumeUuidToNewVolumePath = mapVolumeUuidToNewVolumePath;
this.mapVolumeUuidToDeltaPathOnSecondaryAndSize = mapVolumeUuidToDeltaPathOnSecondaryAndSize;
}
public TakeKbossBackupAnswer(Command command, Exception e) {
super(command, e);
if (e instanceof BackupException) {
this.isVmConsistent = ((BackupException)e).isVmConsistent();
}
}
public Map<String, String> getMapVolumeUuidToNewVolumePath() {
return mapVolumeUuidToNewVolumePath;
}
public Map<String, Pair<String, Long>> getMapVolumeUuidToDeltaPathOnSecondaryAndSize() {
return mapVolumeUuidToDeltaPathOnSecondaryAndSize;
}
public boolean isVmConsistent() {
return isVmConsistent;
}
}

View File

@ -0,0 +1,92 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import org.apache.cloudstack.storage.to.KbossTO;
import java.util.List;
public class TakeKbossBackupCommand extends Command {
private boolean quiesceVm;
private boolean runningVM;
private boolean endChain;
private String vmName;
private String imageStoreUrl;
private List<String> backupChainImageStoreUrls;
private List<KbossTO> kbossTOS;
private boolean isolated;
public TakeKbossBackupCommand(boolean quiesceVm, boolean runningVM, boolean endChain, String vmName, String imageStoreUrl, List<String> backupChainImageStoreUrls, List<KbossTO> kbossTOS, boolean isolated) {
this.quiesceVm = quiesceVm;
this.runningVM = runningVM;
this.endChain = endChain;
this.vmName = vmName;
this.imageStoreUrl = imageStoreUrl;
this.backupChainImageStoreUrls = backupChainImageStoreUrls;
this.kbossTOS = kbossTOS;
this.isolated = isolated;
}
public boolean isQuiesceVm() {
return quiesceVm;
}
public boolean isRunningVM() {
return runningVM;
}
public boolean isEndChain() {
return endChain;
}
public String getVmName() {
return vmName;
}
public String getImageStoreUrl() {
return imageStoreUrl;
}
public List<String> getBackupChainImageStoreUrls() {
return backupChainImageStoreUrls;
}
public List<KbossTO> getKbossTOs() {
return kbossTOS;
}
public boolean isIsolated() {
return isolated;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,46 @@
// 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
// with 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 com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
public class ValidateKbossVmAnswer extends Answer {
private boolean bootValidated;
private String screenshotPath;
private String scriptResult;
public ValidateKbossVmAnswer(Command command, boolean bootValidated, String screenshotPath, String scriptResult) {
super(command);
this.bootValidated = bootValidated;
this.screenshotPath = screenshotPath;
this.scriptResult = scriptResult;
}
public boolean isBootValidated() {
return bootValidated;
}
public String getScreenshotPath() {
return screenshotPath;
}
public String getScriptResult() {
return scriptResult;
}
}

View File

@ -0,0 +1,133 @@
//
// 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
// with 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 com.cloud.agent.api.Command;
import com.cloud.agent.api.to.VirtualMachineTO;
import org.apache.cloudstack.storage.to.BackupDeltaTO;
public class ValidateKbossVmCommand extends Command {
private VirtualMachineTO vm;
private BackupDeltaTO backupDeltaTO;
private String scriptToExecute;
private String scriptArguments;
private String expectedResult;
private Integer scriptTimeout;
private Integer bootTimeout;
private Integer screenshotWait;
private boolean takeScreenshot;
private boolean waitForBoot;
private boolean executeScript;
public ValidateKbossVmCommand(VirtualMachineTO vm, BackupDeltaTO backupDeltaTO) {
this.vm = vm;
this.backupDeltaTO = backupDeltaTO;
}
public void setScriptToExecute(String scriptToExecute) {
this.scriptToExecute = scriptToExecute;
}
public void setScriptArguments(String scriptArguments) {
this.scriptArguments = scriptArguments;
}
public void setExpectedResult(String expectedResult) {
this.expectedResult = expectedResult;
}
public void setScriptTimeout(Integer scriptTimeout) {
this.scriptTimeout = scriptTimeout;
}
public void setTakeScreenshot(boolean takeScreenshot) {
this.takeScreenshot = takeScreenshot;
}
public void setWaitForBoot(boolean waitForBoot) {
this.waitForBoot = waitForBoot;
}
public void setExecuteScript(boolean executeScript) {
this.executeScript = executeScript;
}
public void setBootTimeout(Integer bootTimeout) {
this.bootTimeout = bootTimeout;
}
public void setScreenshotWait(Integer screenshotWait) {
this.screenshotWait = screenshotWait;
}
public VirtualMachineTO getVm() {
return vm;
}
public BackupDeltaTO getBackupDeltaTO() {
return backupDeltaTO;
}
public String getScriptToExecute() {
return scriptToExecute;
}
public String getScriptArguments() {
return scriptArguments;
}
public String getExpectedResult() {
return expectedResult;
}
public Integer getScriptTimeout() {
return scriptTimeout;
}
public Integer getBootTimeout() {
return bootTimeout;
}
public Integer getScreenshotWait() {
return screenshotWait;
}
public boolean isTakeScreenshot() {
return takeScreenshot;
}
public boolean isWaitForBoot() {
return waitForBoot;
}
public boolean isExecuteScript() {
return executeScript;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,36 @@
//
// 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
// with 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.storage.command;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
public class BackupDeleteAnswer extends Answer {
private long backupId;
public BackupDeleteAnswer(Command command, boolean success, String details) {
super(command, success, details);
backupId = ((DeleteCommand) command).getData().getId();
}
public long getBackupId() {
return backupId;
}
}

View File

@ -24,6 +24,8 @@ import com.cloud.agent.api.to.DataTO;
public final class DeleteCommand extends StorageSubSystemCommand {
private DataTO data;
private boolean deleteChain;
public DeleteCommand(final DataTO data) {
super();
this.data = data;
@ -42,6 +44,14 @@ public final class DeleteCommand extends StorageSubSystemCommand {
return data;
}
public void setDeleteChain(boolean deleteChain) {
this.deleteChain = deleteChain;
}
public boolean isDeleteChain() {
return deleteChain;
}
@Override
public void setExecuteInSequence(final boolean inSeq) {

View File

@ -25,6 +25,8 @@ public final class RevertSnapshotCommand extends StorageSubSystemCommand {
private SnapshotObjectTO dataOnPrimaryStorage;
private boolean _executeInSequence = false;
private boolean deleteChain;
public RevertSnapshotCommand(SnapshotObjectTO data, SnapshotObjectTO dataOnPrimaryStorage) {
super();
this.data = data;
@ -43,6 +45,14 @@ public final class RevertSnapshotCommand extends StorageSubSystemCommand {
return dataOnPrimaryStorage;
}
public boolean isDeleteChain() {
return deleteChain;
}
public void setDeleteChain(boolean deleteChain) {
this.deleteChain = deleteChain;
}
@Override
public void setExecuteInSequence(final boolean executeInSequence) {
_executeInSequence = executeInSequence;

View File

@ -0,0 +1,102 @@
// 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
// with 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.storage.to;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.Storage;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class BackupDeltaTO implements DataTO {
private DataStoreTO dataStoreTO;
private Hypervisor.HypervisorType hypervisorType;
private String path;
private String screenshotPath;
private Storage.ImageFormat format;
// When set, represents the Backup ID, not the delta ID.
private long id = 0;
public BackupDeltaTO(DataStoreTO dataStoreTO, Hypervisor.HypervisorType hypervisorType, String path) {
this.dataStoreTO = dataStoreTO;
this.hypervisorType = hypervisorType;
this.path = path;
this.format = Storage.ImageFormat.QCOW2;
}
public BackupDeltaTO(long id, DataStoreTO dataStoreTO, Hypervisor.HypervisorType hypervisorType, String path) {
this(dataStoreTO, hypervisorType, path);
this.id = id;
}
@Override
public DataObjectType getObjectType() {
return DataObjectType.BACKUP;
}
@Override
public DataStoreTO getDataStore() {
return dataStoreTO;
}
@Override
public Hypervisor.HypervisorType getHypervisorType() {
return hypervisorType;
}
@Override
public String getPath() {
return path;
}
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Storage.ImageFormat getFormat() {
return this.format;
}
public void setScreenshotPath(String screenshotPath) {
this.screenshotPath = screenshotPath;
}
public String getScreenshotPath() {
return screenshotPath;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.JSON_STYLE).setExcludeFieldNames("id").toString();
}
}

View File

@ -16,28 +16,40 @@
* specific language governing permissions and limitations
* under the License.
*/
package com.cloud.agent.api.storage;
package org.apache.cloudstack.storage.to;
import com.cloud.agent.api.to.DataTO;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
public class SnapshotMergeTreeTO {
public class DeltaMergeTreeTO {
VolumeObjectTO volumeObjectTO;
DataTO parent;
DataTO child;
List<DataTO> grandChildren;
public SnapshotMergeTreeTO(DataTO parent, DataTO child, List<DataTO> grandChildren) {
public DeltaMergeTreeTO(VolumeObjectTO volumeObjectTO, DataTO parent, DataTO child, List<DataTO> grandChildren) {
this.volumeObjectTO = volumeObjectTO;
this.parent = parent;
this.child = child;
this.grandChildren = grandChildren;
}
public VolumeObjectTO getVolumeObjectTO() {
return volumeObjectTO;
}
public DataTO getParent() {
return parent;
}
public void setParent(DataTO parent) {
this.parent = parent;
}
public DataTO getChild() {
return child;
}
@ -52,6 +64,6 @@ public class SnapshotMergeTreeTO {
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
}
}

View File

@ -0,0 +1,110 @@
package org.apache.cloudstack.storage.to;
// 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
// with 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.
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class KbossTO {
private String pathBackupParentOnSecondary;
private VolumeObjectTO volumeObjectTO;
private String deltaPathOnPrimary;
private String parentDeltaPathOnPrimary;
private String deltaPathOnSecondary;
private String oldVolumePath;
private DeltaMergeTreeTO deltaMergeTreeTO;
private List<String> deltaPaths;
public KbossTO(VolumeObjectTO volumeObjectTO, LinkedList<String> deltaPaths) {
this.volumeObjectTO = volumeObjectTO;
this.deltaPaths = deltaPaths;
}
public KbossTO(VolumeObjectTO volumeObjectTO, String deltaPathOnPrimary, String deltaPathOnSecondary, LinkedList<String> deltaPaths) {
this.volumeObjectTO = volumeObjectTO;
this.deltaPathOnPrimary = deltaPathOnPrimary;
this.deltaPathOnSecondary = deltaPathOnSecondary;
this.deltaPaths = deltaPaths;
}
public String getPathBackupParentOnSecondary() {
return pathBackupParentOnSecondary;
}
public VolumeObjectTO getVolumeObjectTO() {
return volumeObjectTO;
}
public DeltaMergeTreeTO getDeltaMergeTreeTO() {
return deltaMergeTreeTO;
}
public List<String> getDeltaPaths() {
return deltaPaths;
}
public String getDeltaPathOnPrimary() {
return deltaPathOnPrimary;
}
public String getDeltaPathOnSecondary() {
return deltaPathOnSecondary;
}
public String getParentDeltaPathOnPrimary() {
return parentDeltaPathOnPrimary;
}
public void setParentDeltaPathOnPrimary(String parentDeltaPathOnPrimary) {
this.parentDeltaPathOnPrimary = parentDeltaPathOnPrimary;
}
public void setPathBackupParentOnSecondary(String pathBackupParentOnSecondary) {
this.pathBackupParentOnSecondary = pathBackupParentOnSecondary;
}
public void setDeltaMergeTreeTO(DeltaMergeTreeTO deltaMergeTreeTO) {
this.deltaMergeTreeTO = deltaMergeTreeTO;
}
public void setDeltaPathOnPrimary(String deltaPathOnPrimary) {
this.deltaPathOnPrimary = deltaPathOnPrimary;
}
public void setDeltaPathOnSecondary(String deltaPathOnSecondary) {
this.deltaPathOnSecondary = deltaPathOnSecondary;
}
public String getOldVolumePath() {
return oldVolumePath;
}
public void setOldVolumePath(String oldVolumePath) {
this.oldVolumePath = oldVolumePath;
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
}
}

View File

@ -32,11 +32,12 @@ import com.cloud.storage.Storage;
import com.cloud.storage.Volume;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class VolumeObjectTO extends DownloadableObjectTO implements DataTO {
public class VolumeObjectTO extends DownloadableObjectTO implements DataTO, Serializable {
private String uuid;
private Volume.Type volumeType;
private DataStoreTO dataStore;
@ -80,6 +81,7 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO {
private String encryptFormat;
private List<String> checkpointPaths;
private Set<String> checkpointImageStoreUrls;
private Set<String> deltasToRemove;
public VolumeObjectTO() {
@ -424,4 +426,12 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO {
public void setCheckpointImageStoreUrls(Set<String> checkpointImageStoreUrls) {
this.checkpointImageStoreUrls = checkpointImageStoreUrls;
}
public Set<String> getDeltasToRemove() {
return deltasToRemove;
}
public void setDeltasToRemove(Set<String> deltasToRemove) {
this.deltasToRemove = deltasToRemove;
}
}

View File

@ -29,4 +29,9 @@
<property name="typeClass" value="org.apache.cloudstack.backup.BackupProvider" />
</bean>
<bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
<property name="registry" ref="internalBackupProvidersRegistry" />
<property name="typeClass" value="org.apache.cloudstack.backup.InternalBackupProvider" />
</bean>
</beans>

View File

@ -339,6 +339,10 @@
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
</bean>
<bean id="internalBackupProvidersRegistry"
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
</bean>
<bean id="kubernetesServiceHelperRegistry"
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
</bean>

View File

@ -122,7 +122,7 @@ public interface VolumeOrchestrationService {
DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template,
Account owner, Long deviceId, Long kmsKeyId, boolean incrementResourceCount);
VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException;
VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool, Long clusterId, Long podId) throws NoTransitionException;
void release(VirtualMachineProfile profile);

View File

@ -241,6 +241,9 @@ public interface StorageManager extends StorageService {
"while adding a new Secondary Storage. If the copy operation fails, the system falls back to downloading the template from the source URL.",
true, ConfigKey.Scope.Zone, null);
ConfigKey<Integer> AgentMaxDataMigrationWaitTime = new ConfigKey<>("Advanced", Integer.class, "agent.max.data.migration.wait.time", "3600",
"The maximum time (in seconds) that the secondary storage data migration command sent to the KVM Agent will be executed before a timeout occurs.", true, ConfigKey.Scope.Cluster);
/**
* should we execute in sequence not involving any storages?
* @return true if commands should execute in sequence

View File

@ -0,0 +1,38 @@
// 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
// with 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.vm;
public class VmWorkDeleteBackup extends VmWork {
private long backupId;
private boolean forced;
public VmWorkDeleteBackup(long userId, long accountId, long vmId, String handlerName, long backupId, boolean forced) {
super(userId, accountId, vmId, handlerName);
this.backupId = backupId;
this.forced = forced;
}
public long getBackupId() {
return backupId;
}
public boolean isForced() {
return forced;
}
}

View File

@ -0,0 +1,45 @@
// 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
// with 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.vm;
public class VmWorkRestoreBackup extends VmWork {
private long backupId;
private boolean quickRestore;
private Long hostId;
public VmWorkRestoreBackup(long userId, long accountId, long vmId, String handlerName, long backupId, boolean quickRestore, Long hostId) {
super(userId, accountId, vmId, handlerName);
this.backupId = backupId;
this.quickRestore = quickRestore;
this.hostId = hostId;
}
public long getBackupId() {
return backupId;
}
public boolean isQuickRestore() {
return quickRestore;
}
public Long getHostId() {
return hostId;
}
}

View File

@ -0,0 +1,55 @@
// 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
// with 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.vm;
import org.apache.cloudstack.backup.Backup;
public class VmWorkRestoreVolumeBackupAndAttach extends VmWork {
private long backupId;
private Backup.VolumeInfo backupVolumeInfo;
private String hostIp;
private boolean quickRestore;
public VmWorkRestoreVolumeBackupAndAttach(long userId, long accountId, long vmId, String handlerName, long backupId, Backup.VolumeInfo backupVolumeInfo,
String hostIp, boolean quickRestore) {
super(userId, accountId, vmId, handlerName);
this.backupId = backupId;
this.backupVolumeInfo = backupVolumeInfo;
this.hostIp = hostIp;
this.quickRestore = quickRestore;
}
public long getBackupId() {
return backupId;
}
public Backup.VolumeInfo getBackupVolumeInfo() {
return backupVolumeInfo;
}
public String getHostIp() {
return hostIp;
}
public boolean isQuickRestore() {
return quickRestore;
}
}

View File

@ -0,0 +1,50 @@
// 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
// with 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.vm;
public class VmWorkTakeBackup extends VmWork {
private long backupId;
private boolean quiesceVm;
private boolean isolated;
public VmWorkTakeBackup(long userId, long accountId, long vmId, long backupId, String handlerName, boolean quiesceVm, boolean isolated) {
super(userId, accountId, vmId, handlerName);
this.quiesceVm = quiesceVm;
this.backupId = backupId;
this.isolated = isolated;
}
public boolean isQuiesceVm() {
return quiesceVm;
}
public long getBackupId() {
return backupId;
}
public boolean isIsolated() {
return isolated;
}
@Override
public String toString() {
return super.toStringAfterRemoveParams(null, null);
}
}

View File

@ -499,7 +499,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
static final ConfigKey<Integer> ClusterVMMetaDataSyncInterval = new ConfigKey<Integer>("Advanced", Integer.class, "vmmetadata.sync.interval", "180", "Cluster VM metadata sync interval in seconds",
false);
static final ConfigKey<Long> VmJobCheckInterval = new ConfigKey<Long>("Advanced",
public static final ConfigKey<Long> VmJobCheckInterval = new ConfigKey<Long>("Advanced",
Long.class, "vm.job.check.interval", "3000",
"Interval in milliseconds to check if the job is complete", false);
static final ConfigKey<Long> VmJobTimeout = new ConfigKey<Long>("Advanced",
@ -1029,7 +1029,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (stateTransitTo(vm, Event.StartRequested, null, work.getId())) {
logger.debug("Successfully transitioned to start state for {} reservation id = {}", vm, work.getId());
if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
_resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template);
_resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template, null);
}
return new Ternary<>(vm, context, work);
}
@ -1396,6 +1396,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
Throwable lastKnownError = null;
boolean canRetry = true;
ExcludeList avoids = null;
long deployedHostId = -1;
try {
final Journal journal = start.second().getJournal();
@ -1527,6 +1528,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (params != null) {
Boolean returnAfterVolumePrepare = (Boolean) params.get(VirtualMachineProfile.Param.ReturnAfterVolumePrepare);
if (Boolean.TRUE.equals(returnAfterVolumePrepare)) {
deployedHostId = vm.getHostId();
logger.info("Returning from VM start command execution for VM {} as requested. Volumes are prepared and ready.", vm.getUuid());
if (!changeState(vm, Event.AgentReportStopped, destHostId, work, Step.Done)) {
@ -1715,7 +1717,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} finally {
if (startedVm == null) {
if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
_resourceLimitMgr.decrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template);
_resourceLimitMgr.decrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template, null);
}
if (canRetry) {
try {
@ -1730,6 +1732,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (planToDeploy != null) {
planToDeploy.setAvoids(avoids);
}
if (params != null && Boolean.TRUE.equals(params.get(VirtualMachineProfile.Param.ReturnAfterVolumePrepare))) {
vm.setHostId(null);
vm.setLastHostId(deployedHostId);
_vmDao.update(vm.getId(), vm);
}
}
if (startedVm == null) {
@ -2646,7 +2654,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (result && VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) {
ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
_resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template);
_resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template, null);
}
return result;
}

View File

@ -28,9 +28,13 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.backup.InternalBackupJoinVO;
import org.apache.cloudstack.backup.dao.InternalBackupJoinDao;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
@ -42,6 +46,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.storage.ImageStoreService;
import org.apache.cloudstack.storage.backup.BackupObject;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
@ -89,12 +94,15 @@ public class DataMigrationUtility {
HostDao hostDao;
@Inject
SnapshotDao snapshotDao;
@Inject
InternalBackupJoinDao internalBackupJoinDao;
/**
* This function verifies if the given image store contains data objects that are not in any of the following states:
* "Ready" "Allocated", "Destroying", "Destroyed", "Failed". If this is the case, and if the migration policy is complete,
* the migration is terminated.
*/
public boolean filesReadyToMigrate(Long srcDataStoreId, List<TemplateDataStoreVO> templates, List<SnapshotDataStoreVO> snapshots, List<VolumeDataStoreVO> volumes) {
public boolean filesReadyToMigrate(Long srcDataStoreId, List<TemplateDataStoreVO> templates, List<SnapshotDataStoreVO> snapshots, List<VolumeDataStoreVO> volumes, List<InternalBackupJoinVO> backups) {
State[] validStates = {State.Ready, State.Allocated, State.Destroying, State.Destroyed, State.Failed};
boolean isReady = true;
for (TemplateDataStoreVO template : templates) {
@ -109,14 +117,48 @@ public class DataMigrationUtility {
isReady &= (Arrays.asList(validStates).contains(volume.getState()));
logger.trace("volume state: {}", volume.getState());
}
isReady &= checkIfBackupsMigrationIsPossible(backups);
return isReady;
}
private boolean checkIfBackupsMigrationIsPossible(List<InternalBackupJoinVO> backups) {
List<Backup.Status> invalidBackupStates = Arrays.asList(Backup.Status.BackingUp, Backup.Status.Restoring);
List<Backup.CompressionStatus> invalidBackupCompressionStatus = Arrays.asList(Backup.CompressionStatus.Compressing, Backup.CompressionStatus.FinalizingCompression);
List<List<BackupObject>> backupChains;
Set<Long> backupIdsAlreadyInChain = new HashSet<>();
for (InternalBackupJoinVO backup : backups) {
if (backup.getStatus() == Backup.Status.BackedUp && !backupIdsAlreadyInChain.contains(backup.getId())) {
backupChains = createBackupChain(backup);
backupChains.forEach(list -> backupIdsAlreadyInChain.add(list.stream().map(BackupObject::getId).findFirst().get()));
for (List<BackupObject> backupVolumeChain : backupChains) {
BackupObject backupObject = backupVolumeChain.get(0);
if (invalidBackupStates.contains(backupObject.getStatus())) {
logger.debug("Migration is not possible because backup {} is in {} state.", backupObject.getUuid(), backupObject.getStatus());
return false;
}
if (invalidBackupCompressionStatus.contains(backupObject.getCompressionStatus())) {
logger.debug("Migration is not possible because backup {} is currently being compressed. Current compression status: {}.", backupObject.getUuid(), backupObject.getCompressionStatus());
return false;
}
}
}
}
return true;
}
private boolean filesReadyToMigrate(Long srcDataStoreId) {
List<TemplateDataStoreVO> templates = templateDataStoreDao.listByStoreId(srcDataStoreId);
List<SnapshotDataStoreVO> snapshots = snapshotDataStoreDao.listByStoreId(srcDataStoreId, DataStoreRole.Image);
List<VolumeDataStoreVO> volumes = volumeDataStoreDao.listByStoreId(srcDataStoreId);
return filesReadyToMigrate(srcDataStoreId, templates, snapshots, volumes);
List<InternalBackupJoinVO> backups = internalBackupJoinDao.listByImageStoreId(srcDataStoreId);
return filesReadyToMigrate(srcDataStoreId, templates, snapshots, volumes, backups);
}
protected void checkIfCompleteMigrationPossible(ImageStoreService.MigrationPolicy policy, Long srcDataStoreId) {
@ -175,19 +217,58 @@ public class DataMigrationUtility {
return files;
}
protected List<DataObject> getSortedValidSourcesList(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains,
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates) {
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates, Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains) {
List<DataObject> files = new ArrayList<>();
files.addAll(getAllReadyTemplates(srcDataStore, childTemplates));
files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains));
files.addAll(getAllReadyVolumes(srcDataStore));
files.addAll(getAllReadyBackupsAndChains(srcDataStore, backupChains));
files = sortFilesOnSize(files, snapshotChains);
return files;
}
protected List<DataObject> getAllReadyBackupsAndChains(DataStore srcDataStore, Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains) {
List<InternalBackupJoinVO> backups = internalBackupJoinDao.listByImageStoreId(srcDataStore.getId());
return getAllReadyBackupsAndChains(backupChains, backups);
}
private List<DataObject> getAllReadyBackupsAndChains(Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupsChains, List<InternalBackupJoinVO> backups) {
Set<Long> backupIdsToMigrate = backups.stream().map(InternalBackupJoinVO::getId).collect(Collectors.toSet());
List<List<BackupObject>> backupChains;
Set<Long> backupIdsAlreadyInChain = new HashSet<>();
List<BackupObject> files = new LinkedList<>();
for (InternalBackupJoinVO backup : backups) {
long backupId = backup.getId();
if (backup.getStatus() == Backup.Status.BackedUp && !backupIdsAlreadyInChain.contains(backupId)) {
backupChains = createBackupChain(backup);
backupChains.forEach(list -> backupIdsAlreadyInChain.add(list.stream().map(BackupObject::getId).findFirst().get()));
BackupObject parent = backupChains.get(0).get(0);
files.add(parent);
backupsChains.put(parent, new Pair<>(backupChains, backupChains.stream().map(list -> getTotalChainSize(list.stream()
.filter(back -> backupIdsToMigrate.contains(parent.getId())).collect(Collectors.toList()))
).reduce(Long::sum).get()));
}
}
return (List<DataObject>) (List<?>) files;
}
private List<List<BackupObject>> createBackupChain(InternalBackupJoinVO backup) {
List<List<BackupObject>> chain = new LinkedList<>();
BackupObject backupObject = BackupObject.getBackupObject(backup);
chain.addAll(backupObject.getParents(backup.getParentId()));
chain.add(internalBackupJoinDao.listById(backup.getId()).stream().map(BackupObject::getBackupObject).collect(Collectors.toList()));
chain.addAll(backupObject.getChildren());
return chain;
}
protected List<DataObject> sortFilesOnSize(List<DataObject> files, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains) {
Collections.sort(files, new Comparator<DataObject>() {
@Override

View File

@ -25,9 +25,13 @@ import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@ -36,10 +40,22 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.MigrateBackupsBetweenSecondaryStoragesCommand;
import com.cloud.agent.api.MigrateBetweenSecondaryStoragesCommandAnswer;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.template.TemplateManager;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import org.apache.cloudstack.api.response.MigrationResponse;
import org.apache.cloudstack.backup.BackupDetailVO;
import org.apache.cloudstack.backup.dao.BackupDetailsDao;
import org.apache.cloudstack.engine.orchestration.service.StorageOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@ -57,6 +73,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.ImageStoreService.MigrationPolicy;
import org.apache.cloudstack.storage.backup.BackupObject;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
@ -115,6 +132,12 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
TemplateDataFactory templateDataFactory;
@Inject
DataCenterDao dcDao;
@Inject
AgentManager agentManager;
@Inject
HostDao hostDao;
@Inject
BackupDetailsDao backupDetailDao;
ConfigKey<Double> ImageStoreImbalanceThreshold = new ConfigKey<>("Advanced", Double.class,
@ -128,6 +151,7 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
private final Map<Long, ThreadPoolExecutor> zoneExecutorMap = new HashMap<>();
private final Map<Long, Integer> zonePendingWorkCountMap = new HashMap<>();
private final Map<Long, ExecutorService> zoneKvmIncrementalExecutorMap = new ConcurrentHashMap<>();
@Override
public String getConfigComponentName() {
@ -171,7 +195,9 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
DataStore srcDatastore = dataStoreManager.getDataStore(srcDataStoreId, DataStoreRole.Image);
Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains = new HashMap<>();
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates = new HashMap<>();
files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates);
Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains = new HashMap<>();
files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates, backupChains);
if (files.isEmpty()) {
return new MigrationResponse(String.format("No files in Image store: %s to migrate", srcDatastore), migrationPolicy.toString(), true);
@ -227,7 +253,7 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
}
if (shouldMigrate(chosenFileForMigration, srcDatastore.getId(), destDatastoreId, storageCapacities, snapshotChains, childTemplates, migrationPolicy)) {
storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, srcDatastore, destDatastoreId, futures);
storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, backupChains, srcDatastore, destDatastoreId, futures);
} else {
if (migrationPolicy == MigrationPolicy.BALANCE) {
continue;
@ -256,7 +282,7 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
List<TemplateDataStoreVO> templates = templateDataStoreDao.listByStoreIdAndTemplateIds(srcImgStoreId, templateIdList);
List<SnapshotDataStoreVO> snapshots = snapshotDataStoreDao.listByStoreAndSnapshotIds(srcImgStoreId, DataStoreRole.Image, snapshotIdList);
if (!migrationHelper.filesReadyToMigrate(srcImgStoreId, templates, snapshots, Collections.emptyList())) {
if (!migrationHelper.filesReadyToMigrate(srcImgStoreId, templates, snapshots, Collections.emptyList(), Collections.emptyList())) {
throw new CloudRuntimeException("Migration failed as there are data objects which are not Ready - i.e, they may be in Migrating, creating, copying, etc. states");
}
files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates, templates, snapshots);
@ -291,7 +317,7 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
}
if (storageCapacityBelowThreshold(storageCapacities, destImgStoreId)) {
storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, srcDatastore, destImgStoreId, futures);
storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, null, srcDatastore, destImgStoreId, futures);
} else {
message = "Migration failed. Destination store doesn't have enough capacity for migration";
success = false;
@ -355,15 +381,89 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
Map<DataObject,
Pair<List<SnapshotInfo>, Long>> snapshotChains,
Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChains,
Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains,
DataStore srcDatastore,
Long destDatastoreId,
List<Future<DataObjectResult>> futures) {
Long fileSize = migrationHelper.getFileSize(chosenFileForMigration, snapshotChains, templateChains);
storageCapacities = assumeMigrate(storageCapacities, srcDatastore.getId(), destDatastoreId, fileSize);
DataStore destDataStore = dataStoreManager.getDataStore(destDatastoreId, DataStoreRole.Image);
MigrateDataTask task = new MigrateDataTask(chosenFileForMigration, srcDatastore, dataStoreManager.getDataStore(destDatastoreId, DataStoreRole.Image));
if (chosenFileForMigration instanceof SnapshotInfo ) {
boolean isKvmIncrementalBackup = backupChains != null && chosenFileForMigration instanceof BackupObject && backupChains.containsKey(chosenFileForMigration);
if (isKvmIncrementalBackup) {
MigrateKvmIncrementalBackupTask task = new MigrateKvmIncrementalBackupTask(chosenFileForMigration, backupChains, srcDatastore, destDataStore);
futures.add(submitKvmIncrementalMigration(srcDatastore.getScope().getScopeId(), task));
logger.debug("Incremental backup migration {} submitted to incremental pool.", chosenFileForMigration.getUuid());
} else {
createMigrateDataTask(chosenFileForMigration, snapshotChains, templateChains, srcDatastore, destDataStore, futures);
}
return storageCapacities;
}
private void migrateKvmIncrementalBackupChain(DataObject chosenFileForMigration, Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains, DataStore srcDatastore, DataStore destDataStore) {
Transaction.execute((TransactionCallback<Boolean>) status -> {
MigrateBetweenSecondaryStoragesCommandAnswer answer = null;
try {
List<List<BackupObject>> backupChain = backupChains.get(chosenFileForMigration).first();
MigrateBackupsBetweenSecondaryStoragesCommand migrateBetweenSecondaryStoragesCmd = new MigrateBackupsBetweenSecondaryStoragesCommand(backupChain.stream().map(list -> list.stream().map(BackupObject::getTO).collect(Collectors.toList()))
.collect(Collectors.toList()), srcDatastore.getTO(), destDataStore.getTO());
HostVO host = getAvailableHost(((BackupObject) chosenFileForMigration).getZoneId());
if (host == null) {
throw new CloudRuntimeException("No hosts found to send migrate command.");
}
migrateBetweenSecondaryStoragesCmd.setWait(StorageManager.AgentMaxDataMigrationWaitTime.valueIn(host.getClusterId()));
answer = (MigrateBetweenSecondaryStoragesCommandAnswer) agentManager.send(host.getId(), migrateBetweenSecondaryStoragesCmd);
if (answer == null || !answer.getResult()) {
logger.warn("Unable to migrate backups [{}].", backupChain);
throw new CloudRuntimeException("Unable to migrate KVM incremental backups to another secondary storage");
}
} catch (final OperationTimedoutException | AgentUnavailableException e) {
throw new CloudRuntimeException("Error while migrating KVM incremental backup chain. Check the logs for more information.", e);
} finally {
if (answer != null) {
updateBackupsReference(destDataStore, answer);
}
}
return answer.getResult();
});
}
private void updateBackupsReference(DataStore destDataStore, MigrateBetweenSecondaryStoragesCommandAnswer answer) {
for (Pair<Long, String> backupIdAndUpdatedCheckpointPath : answer.getMigratedResources()) {
Long backupId = backupIdAndUpdatedCheckpointPath.first();
BackupDetailVO backupDetail = backupDetailDao.findDetail(backupId, BackupDetailsDao.IMAGE_STORE_ID);
String destDataStoreId = String.valueOf(destDataStore.getId());
if (backupDetail == null) {
logger.warn("No details found for backup [{}]. Creating new entry with image store ID [{}].", backupId, destDataStoreId);
backupDetailDao.addDetail(backupId, BackupDetailsDao.IMAGE_STORE_ID, destDataStoreId, false);
continue;
}
backupDetail.setValue(destDataStoreId);
backupDetailDao.update(backupDetail.getId(), backupDetail);
}
}
private HostVO getAvailableHost(long zoneId) throws AgentUnavailableException, OperationTimedoutException {
List<HostVO> hosts = hostDao.listByDataCenterIdAndHypervisorType(zoneId, Hypervisor.HypervisorType.KVM);
if (CollectionUtils.isNotEmpty(hosts)) {
return hosts.get(new Random().nextInt(hosts.size()));
}
return null;
}
private void createMigrateDataTask(DataObject chosenFileForMigration, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains, Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChains, DataStore srcDatastore, DataStore destDataStore, List<Future<DataObjectResult>> futures) {
MigrateDataTask task = new MigrateDataTask(chosenFileForMigration, srcDatastore, destDataStore);
if (chosenFileForMigration instanceof SnapshotInfo) {
task.setSnapshotChains(snapshotChains);
}
if (chosenFileForMigration instanceof TemplateInfo) {
@ -371,7 +471,6 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
}
futures.add(submit(srcDatastore.getScope().getScopeId(), task));
logger.debug("Migration of {}: {} is initiated.", chosenFileForMigration.getType().name(), chosenFileForMigration.getUuid());
return storageCapacities;
}
protected <T> Future<T> submit(Long zoneId, Callable<T> task) {
@ -390,6 +489,13 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
}
protected synchronized <T> Future<T> submitKvmIncrementalMigration(Long zoneId, Callable<T> task) {
if (!zoneKvmIncrementalExecutorMap.containsKey(zoneId)) {
zoneKvmIncrementalExecutorMap.put(zoneId, Executors.newSingleThreadExecutor());
}
return zoneKvmIncrementalExecutorMap.get(zoneId).submit(task);
}
protected void scaleExecutorIfNecessary(Long zoneId) {
long activeSsvms = migrationHelper.activeSSVMCount(zoneId);
long totalJobs = activeSsvms * numConcurrentCopyTasksPerSSVM;
@ -666,4 +772,32 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
return result;
}
}
private class MigrateKvmIncrementalBackupTask implements Callable<DataObjectResult> {
private final DataObject chosenFile;
private final Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains;
private final DataStore srcDataStore;
private final DataStore destDataStore;
public MigrateKvmIncrementalBackupTask(DataObject chosenFile, Map<DataObject, Pair<List<List<BackupObject>>, Long>> backupChains, DataStore srcDataStore, DataStore destDataStore) {
this.chosenFile = chosenFile;
this.backupChains = backupChains;
this.srcDataStore = srcDataStore;
this.destDataStore = destDataStore;
}
@Override
public DataObjectResult call() {
try {
migrateKvmIncrementalBackupChain(chosenFile, backupChains, srcDataStore, destDataStore);
return new DataObjectResult(chosenFile);
} catch (Exception e) {
logger.warn("Failed migrating incremental backup {} due to {}.", chosenFile.getUuid(), e);
DataObjectResult result = new DataObjectResult(chosenFile);
result.setResult(e.toString());
return result;
}
}
}
}

View File

@ -53,6 +53,7 @@ import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin;
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
import org.apache.cloudstack.backup.InternalBackupService;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
@ -301,6 +302,9 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
@Inject
private KMSWrappedKeyDao kmsWrappedKeyDao;
@Inject
private InternalBackupService internalBackupService;
private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
protected List<StoragePoolAllocator> _storagePoolAllocators;
@ -1358,21 +1362,27 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
}
@Override
public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volumeInfo, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException {
public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volumeInfo, HypervisorType rootDiskHyperType, StoragePool storagePool, Long clusterId, Long podId)
throws NoTransitionException {
String volumeToString = getReflectOnlySelectedFields(volumeInfo.getVolume());
VirtualMachineTemplate rootDiskTmplt = _entityMgr.findById(VirtualMachineTemplate.class, vm.getTemplateId());
DataCenter dcVO = _entityMgr.findById(DataCenter.class, vm.getDataCenterId());
logger.trace("storage-pool {}/{} is associated with pod {}",storagePool.getName(), storagePool.getUuid(), storagePool.getPodId());
Long podId = storagePool.getPodId() != null ? storagePool.getPodId() : vm.getPodIdToDeployIn();
if (storagePool != null) {
logger.trace("storage-pool {}/{} is associated with pod {}", storagePool.getName(), storagePool.getUuid(), storagePool.getPodId());
podId = storagePool.getPodId() != null ? storagePool.getPodId() : vm.getPodIdToDeployIn();
clusterId = storagePool.getClusterId();
logger.trace("storage-pool {}/{} is associated with cluster {}",storagePool.getName(), storagePool.getUuid(), clusterId);
}
Pod pod = _entityMgr.findById(Pod.class, podId);
ServiceOffering svo = _entityMgr.findById(ServiceOffering.class, vm.getServiceOfferingId());
DiskOffering diskVO = _entityMgr.findById(DiskOffering.class, volumeInfo.getDiskOfferingId());
Long clusterId = storagePool.getClusterId();
logger.trace("storage-pool {}/{} is associated with cluster {}",storagePool.getName(), storagePool.getUuid(), clusterId);
Long hostId = vm.getHostId();
if (hostId == null && (storagePool.isLocal() || ClvmPoolManager.isClvmPoolType(storagePool.getPoolType()))) {
if (hostId == null && storagePool != null && (storagePool.isLocal() || ClvmPoolManager.isClvmPoolType(storagePool.getPoolType()))) {
if (ClvmPoolManager.isClvmPoolType(storagePool.getPoolType())) {
hostId = getClvmLockHostFromVmVolumes(vm.getId());
if (hostId != null) {
@ -1632,6 +1642,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
_snapshotDao.updateVolumeIds(vol.getId(), result.getVolume().getId());
_snapshotDataStoreDao.updateVolumeIds(vol.getId(), result.getVolume().getId());
}
internalBackupService.updateVolumeId(vol.getId(), result.getVolume().getId());
// For CLVM volumes attached to a VM, update the CLVM_LOCK_HOST_ID after migration
updateClvmLockHostAfterMigration(result.getVolume(), destPool, "migrated");
@ -1695,6 +1706,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
throw new CloudRuntimeException(String.format("Failed to find the destination storage pool [%s] to migrate the volume [%s] to.", storagePoolToString, volumeToString));
}
internalBackupService.prepareVolumeForMigration(volume);
volumeMap.put(volFactory.getVolume(volume.getId()), (DataStore)destPool);
}
@ -2531,7 +2544,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
if (volume.getState() == Volume.State.Allocated) {
_volsDao.remove(volume.getId());
stateTransitTo(volume, Volume.Event.DestroyRequested);
_resourceLimitMgr.decrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(), volume.getSize(), diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()));
_resourceLimitMgr.decrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(), volume.getSize(), diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()),
null);
} else {
destroyVolumeInContext(volume);
}

View File

@ -91,6 +91,7 @@
<entry key="VolumeApiServiceImpl" value-ref="volumeApiServiceImpl" />
<entry key="VMSnapshotManagerImpl" value-ref="vMSnapshotManagerImpl" />
<entry key="KVMBackupExportServiceImpl" value-ref="kvmBackupExportService" />
<entry key="InternalBackupService" value-ref="internalBackupServiceImpl" />
</map>
</property>
</bean>

View File

@ -121,6 +121,8 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
List<Long> listIdsForUpEnabledByZoneAndHypervisor(Long zoneId, HypervisorType hypervisorType);
List<HostVO> findRoutingByClusterId(Long clusterId);
List<HostVO> findByClusterIdAndEncryptionSupport(Long clusterId);
/**
@ -139,6 +141,8 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
List<HostVO> listAllHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType);
List<HostVO> listAllRoutingHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType);
List<HostVO> listAllHostsThatHaveNoRuleTag(Host.Type type, Long clusterId, Long podId, Long dcId);
HostVO findByPublicIp(String publicIp);

View File

@ -1341,6 +1341,14 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
return listIdsBy(null, Status.Up, ResourceState.Enabled, hypervisorType, zoneId, null, null);
}
@Override
public List<HostVO> findRoutingByClusterId(Long clusterId) {
SearchCriteria<HostVO> sc = ClusterSearch.create();
sc.setParameters("clusterId", clusterId);
sc.setParameters("type", Type.Routing);
return listBy(sc);
}
@Override
public List<HostVO> findByClusterIdAndEncryptionSupport(Long clusterId) {
SearchBuilder<DetailVO> hostCapabilitySearch = _detailsDao.createSearchBuilder();
@ -1456,6 +1464,16 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
return listBy(sc);
}
@Override
public List<HostVO> listAllRoutingHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType) {
SearchCriteria<HostVO> sc = DcSearch.create();
sc.setParameters("dc", zoneId);
sc.setParameters("hypervisorType", hypervisorType.toString());
sc.setParameters("type", Type.Routing);
return listBy(sc);
}
@Override
public List<HostVO> listAllHostsThatHaveNoRuleTag(Type type, Long clusterId, Long podId, Long dcId) {
SearchCriteria<HostVO> sc = searchBuilderFindByIdTypeClusterIdPodIdDcIdAndWithoutRuleTag.create();

View File

@ -141,4 +141,6 @@ public interface NetworkDao extends GenericDao<NetworkVO, Long>, StateDao<State,
List<NetworkVO> listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri);
List<NetworkVO> getAllPersistentNetworksFromZone(long dataCenterId);
NetworkVO findByZoneIdAndAccountIdAndGuestTypeAndName(long zoneId, long accountId, GuestType guestType, String name);
}

View File

@ -943,4 +943,16 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
return overlappingNetworks;
}
@Override
public NetworkVO findByZoneIdAndAccountIdAndGuestTypeAndName(long zoneId, long accountId, GuestType guestType, String name) {
SearchCriteria<NetworkVO> sc = AllFieldsSearch.create();
sc.setParameters("datacenter", zoneId);
sc.setParameters("account", accountId);
sc.setParameters("guestType", guestType);
sc.setParameters("name", name);
return findOneBy(sc);
}
}

View File

@ -248,6 +248,10 @@ public class SnapshotVO implements Snapshot {
return removed;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
@Override
public State getState() {
return state;

View File

@ -56,10 +56,18 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
// TODO: we should remove these direct sqls
private static final String GET_LAST_SNAPSHOT =
"SELECT snapshots.id FROM snapshot_store_ref, snapshots where snapshots.id = snapshot_store_ref.snapshot_id AND snapshosts.volume_id = ? AND snapshot_store_ref.role = ? ORDER BY created DESC";
private static final String VOLUME_ID = "volumeId";
private static final String NOT_TYPE = "notType";
private static final String TYPE = "type";
private static final String STATUS = "status";
private static final String VERSION = "version";
private static final String ACCOUNT_ID = "accountId";
private static final String REMOVED = "removed";
private static final String NOT_TYPE = "notType";
private static final String ID = "id";
private static final String INSTANCE_ID = "instanceId";
private static final String STATE = "state";
private static final String INSTANCE_VOLUMES = "instanceVolumes";
private static final String INSTANCE_SNAPSHOTS = "instanceSnapshots";
private SearchBuilder<SnapshotVO> snapshotIdsSearch;
private SearchBuilder<SnapshotVO> VolumeIdSearch;
@ -83,9 +91,9 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public List<SnapshotVO> listByVolumeIdTypeNotDestroyed(long volumeId, Type type) {
SearchCriteria<SnapshotVO> sc = VolumeIdTypeNotDestroyedSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("type", type.ordinal());
sc.setParameters("status", State.Destroyed);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(TYPE, type.ordinal());
sc.setParameters(STATUS, State.Destroyed);
return listBy(sc, null);
}
@ -102,28 +110,28 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public List<SnapshotVO> listByVolumeId(Filter filter, long volumeId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters(VOLUME_ID, volumeId);
return listBy(sc, filter);
}
@Override
public List<SnapshotVO> listByVolumeIdIncludingRemoved(long volumeId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters(VOLUME_ID, volumeId);
return listIncludingRemovedBy(sc, null);
}
public List<SnapshotVO> listByVolumeIdType(Filter filter, long volumeId, Type type) {
SearchCriteria<SnapshotVO> sc = VolumeIdTypeSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("type", type.ordinal());
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(TYPE, type.ordinal());
return listBy(sc, filter);
}
public List<SnapshotVO> listByVolumeIdVersion(Filter filter, long volumeId, String version) {
SearchCriteria<SnapshotVO> sc = VolumeIdVersionSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("version", version);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(VERSION, version);
return listBy(sc, filter);
}
@ -133,61 +141,61 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@PostConstruct
protected void init() {
VolumeIdSearch = createSearchBuilder();
VolumeIdSearch.and("volumeId", VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdSearch.and(VOLUME_ID, VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdSearch.done();
VolumeIdTypeSearch = createSearchBuilder();
VolumeIdTypeSearch.and("volumeId", VolumeIdTypeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.and("type", VolumeIdTypeSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.and(VOLUME_ID, VolumeIdTypeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.and(TYPE, VolumeIdTypeSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.done();
VolumeIdTypeNotDestroyedSearch = createSearchBuilder();
VolumeIdTypeNotDestroyedSearch.and("volumeId", VolumeIdTypeNotDestroyedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and("type", VolumeIdTypeNotDestroyedSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and("status", VolumeIdTypeNotDestroyedSearch.entity().getState(), SearchCriteria.Op.NEQ);
VolumeIdTypeNotDestroyedSearch.and(VOLUME_ID, VolumeIdTypeNotDestroyedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and(TYPE, VolumeIdTypeNotDestroyedSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and(STATUS, VolumeIdTypeNotDestroyedSearch.entity().getState(), SearchCriteria.Op.NEQ);
VolumeIdTypeNotDestroyedSearch.done();
VolumeIdVersionSearch = createSearchBuilder();
VolumeIdVersionSearch.and("volumeId", VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and("version", VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and(VOLUME_ID, VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and(VERSION, VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.done();
AccountIdSearch = createSearchBuilder();
AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountIdSearch.and(ACCOUNT_ID, AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountIdSearch.done();
StatusSearch = createSearchBuilder();
StatusSearch.and("volumeId", StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
StatusSearch.and("status", StatusSearch.entity().getState(), SearchCriteria.Op.IN);
StatusSearch.and(VOLUME_ID, StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
StatusSearch.and(STATUS, StatusSearch.entity().getState(), SearchCriteria.Op.IN);
StatusSearch.done();
notInStatusSearch = createSearchBuilder();
notInStatusSearch.and("volumeId", notInStatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
notInStatusSearch.and("status", notInStatusSearch.entity().getState(), SearchCriteria.Op.NOTIN);
notInStatusSearch.and(VOLUME_ID, notInStatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
notInStatusSearch.and(STATUS, notInStatusSearch.entity().getState(), SearchCriteria.Op.NOTIN);
notInStatusSearch.done();
CountSnapshotsByAccount = createSearchBuilder(Long.class);
CountSnapshotsByAccount.select(null, Func.COUNT, null);
CountSnapshotsByAccount.and("account", CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountSnapshotsByAccount.and("status", CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountSnapshotsByAccount.and(ACCOUNT_ID, CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountSnapshotsByAccount.and(STATUS, CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountSnapshotsByAccount.and("snapshotTypeNEQ", CountSnapshotsByAccount.entity().getSnapshotType(), SearchCriteria.Op.NIN);
CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL);
CountSnapshotsByAccount.and(REMOVED, CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL);
CountSnapshotsByAccount.done();
InstanceIdSearch = createSearchBuilder();
InstanceIdSearch.and("status", InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN);
InstanceIdSearch.and(STATUS, InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN);
snapshotIdsSearch = createSearchBuilder();
snapshotIdsSearch.and("id", snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN);
snapshotIdsSearch.and(ID, snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN);
SearchBuilder<VMInstanceVO> instanceSearch = _instanceDao.createSearchBuilder();
instanceSearch.and("instanceId", instanceSearch.entity().getId(), SearchCriteria.Op.EQ);
instanceSearch.and(INSTANCE_ID, instanceSearch.entity().getId(), SearchCriteria.Op.EQ);
SearchBuilder<VolumeVO> volumeSearch = _volumeDao.createSearchBuilder();
volumeSearch.and("state", volumeSearch.entity().getState(), SearchCriteria.Op.EQ);
volumeSearch.join("instanceVolumes", instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER);
volumeSearch.and(STATE, volumeSearch.entity().getState(), SearchCriteria.Op.EQ);
volumeSearch.join(INSTANCE_VOLUMES, instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER);
InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER);
InstanceIdSearch.join(INSTANCE_SNAPSHOTS, volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER);
InstanceIdSearch.done();
volumeIdAndTypeNotInSearch = createSearchBuilder();
@ -219,8 +227,8 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public Long countSnapshotsForAccount(long accountId) {
SearchCriteria<Long> sc = CountSnapshotsByAccount.create();
sc.setParameters("account", accountId);
sc.setParameters("status", State.Error, State.Destroyed);
sc.setParameters(ACCOUNT_ID, accountId);
sc.setParameters(STATUS, State.Error, State.Destroyed);
sc.setParameters("snapshotTypeNEQ", Snapshot.Type.GROUP.ordinal());
return customSearch(sc, null).get(0);
}
@ -230,19 +238,19 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
SearchCriteria<SnapshotVO> sc = InstanceIdSearch.create();
if (status != null && status.length != 0) {
sc.setParameters("status", (Object[])status);
sc.setParameters(STATUS, (Object[])status);
}
sc.setJoinParameters("instanceSnapshots", "state", Volume.State.Ready);
sc.setJoinParameters("instanceVolumes", "instanceId", instanceId);
sc.setJoinParameters(INSTANCE_SNAPSHOTS, STATE, Volume.State.Ready);
sc.setJoinParameters(INSTANCE_VOLUMES, INSTANCE_ID, instanceId);
return listBy(sc, null);
}
@Override
public List<SnapshotVO> listByStatus(long volumeId, Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("status", (Object[])status);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(STATUS, (Object[])status);
return listBy(sc, null);
}
@ -263,7 +271,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public List<SnapshotVO> listAllByStatus(Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
sc.setParameters("status", (Object[])status);
sc.setParameters(STATUS, (Object[])status);
return listBy(sc, null);
}
@ -277,7 +285,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public List<SnapshotVO> listByIds(Object... ids) {
SearchCriteria<SnapshotVO> sc = snapshotIdsSearch.create();
sc.setParameters("id", ids);
sc.setParameters(ID, ids);
return listBy(sc, null);
}
@ -295,7 +303,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public void updateVolumeIds(long oldVolId, long newVolId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", oldVolId);
sc.setParameters(VOLUME_ID, oldVolId);
SnapshotVO snapshot = createForUpdate();
snapshot.setVolumeId(newVolId);
UpdateBuilder ub = getUpdateBuilder(snapshot);
@ -305,8 +313,8 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public List<SnapshotVO> listByStatusNotIn(long volumeId, Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = this.notInStatusSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("status", (Object[]) status);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(STATUS, (Object[]) status);
return listBy(sc, null);
}

View File

@ -83,6 +83,11 @@ public class BackupOfferingVO implements BackupOffering {
this.created = new Date();
}
public BackupOfferingVO(final long zoneId, final String provider, final String name, final String description, final boolean userDrivenBackupAllowed) {
this(zoneId, null, provider, name, description, userDrivenBackupAllowed);
this.externalId = this.uuid;
}
public String getUuid() {
return uuid;
}

View File

@ -74,10 +74,14 @@ public class BackupScheduleVO implements BackupSchedule {
@Column(name = "domain_id")
Long domainId;
@Column(name = "isolated")
private boolean isolated;
public BackupScheduleVO() {
}
public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, int maxBackups, Boolean quiesceVM, Long accountId, Long domainId) {
public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, int maxBackups, Boolean quiesceVM,
Long accountId, Long domainId, boolean isolated) {
this.vmId = vmId;
this.scheduleType = (short) scheduleType.ordinal();
this.schedule = schedule;
@ -87,6 +91,7 @@ public class BackupScheduleVO implements BackupSchedule {
this.quiesceVM = quiesceVM;
this.accountId = accountId;
this.domainId = domainId;
this.isolated = isolated;
}
@Override
@ -197,4 +202,13 @@ public class BackupScheduleVO implements BackupSchedule {
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
@Override
public boolean isIsolated() {
return isolated;
}
public void setIsolated(boolean isolated) {
this.isolated = isolated;
}
}

View File

@ -19,7 +19,6 @@ package org.apache.cloudstack.backup;
import com.cloud.utils.db.GenericDao;
import com.google.gson.Gson;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang3.StringUtils;
@ -81,6 +80,9 @@ public class BackupVO implements Backup {
@Column(name = "protected_size")
private Long protectedSize;
@Column(name = "uncompressed_size")
private Long uncompressedSize;
@Enumerated(value = EnumType.STRING)
@Column(name = "status")
private Backup.Status status;
@ -103,6 +105,12 @@ public class BackupVO implements Backup {
@Column(name = "backup_schedule_id")
private Long backupScheduleId;
@Column(name = "compression_status")
private CompressionStatus compressionStatus;
@Column(name = "validation_status")
private ValidationStatus validationStatus;
@Column(name = "from_checkpoint_id")
private String fromCheckpointId;
@ -120,6 +128,24 @@ public class BackupVO implements Backup {
public BackupVO() {
this.uuid = UUID.randomUUID().toString();
this.compressionStatus = CompressionStatus.Uncompressed;
this.validationStatus = ValidationStatus.NotValidated;
}
public BackupVO(String name, long vmId, long backupOfferingId, long accountId, long domainId, long zoneId, long virtualSize, Status status, Long backupScheduleId) {
this.name = name;
this.vmId = vmId;
this.backupOfferingId = backupOfferingId;
this.accountId = accountId;
this.domainId = domainId;
this.zoneId = zoneId;
this.protectedSize = virtualSize;
this.status = status;
this.setType("FULL");
this.uuid = UUID.randomUUID().toString();
this.backupScheduleId = backupScheduleId;
this.compressionStatus = CompressionStatus.Uncompressed;
this.validationStatus = ValidationStatus.NotValidated;
}
@Override
@ -156,6 +182,7 @@ public class BackupVO implements Backup {
this.externalId = externalId;
}
@Override
public String getType() {
return type;
}
@ -301,6 +328,32 @@ public class BackupVO implements Backup {
this.backupScheduleId = backupScheduleId;
}
@Override
public CompressionStatus getCompressionStatus() {
return compressionStatus;
}
public void setCompressionStatus(CompressionStatus compressionStatus) {
this.compressionStatus = compressionStatus;
}
@Override
public ValidationStatus getValidationStatus() {
return validationStatus;
}
public void setValidationStatus(ValidationStatus validationStatus) {
this.validationStatus = validationStatus;
}
public Long getUncompressedSize() {
return uncompressedSize;
}
public void setUncompressedSize(Long uncompressedSize) {
this.uncompressedSize = uncompressedSize;
}
@Override
public String getFromCheckpointId() {
return fromCheckpointId;

View File

@ -0,0 +1,95 @@
//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 org.apache.cloudstack.api.InternalIdentity;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "internal_backup_store_ref")
public class InternalBackupDataStoreVO implements InternalIdentity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "backup_id")
private long backupId;
@Column(name = "volume_id")
private long volumeId;
@Column (name = "device_id")
private long deviceId;
@Column(name = "path")
private String backupPath;
public InternalBackupDataStoreVO() {
}
public InternalBackupDataStoreVO(long backupId, long volumeId, long deviceId, String backupPath) {
this.backupId = backupId;
this.volumeId = volumeId;
this.deviceId = deviceId;
this.backupPath = backupPath;
}
@Override
public long getId() {
return id;
}
public long getBackupId() {
return backupId;
}
public long getVolumeId() {
return volumeId;
}
public long getDeviceId() {
return deviceId;
}
public String getBackupPath() {
return backupPath;
}
public void setVolumeId(long volumeId) {
this.volumeId = volumeId;
}
public void setBackupPath(String backupPath) {
this.backupPath = backupPath;
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
}
}

View File

@ -0,0 +1,211 @@
// 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
// with 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 com.google.gson.Gson;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.commons.lang3.StringUtils;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "internal_backup_view")
public class InternalBackupJoinVO {
@Id
@Column(name="id")
private long id;
@Column(name = "uuid")
private String uuid;
@Column(name = "vm_id")
private long vmId;
@Column(name = "backed_volumes", length = 65535)
private String backedUpVolumes;
@Column(name = "backup_offering_id")
private long backupOfferingId;
@Column(name = "image_store_id")
private long imageStoreId;
@Column(name = "parent_id")
private long parentId;
@Column(name = "type")
private String type;
@Column(name = "date")
@Temporal(value = TemporalType.DATE)
private Date date;
@Enumerated(value = EnumType.STRING)
@Column(name = "status")
private Backup.Status status;
@Enumerated(value = EnumType.STRING)
@Column(name = "compression_status")
private Backup.CompressionStatus compressionStatus;
@Column(name = "end_of_chain")
private Boolean endOfChain;
@Column(name = "current")
private Boolean current;
@Column(name = "image_store_path")
private String imageStorePath;
@Column(name = "zone_id")
private long zoneId;
@Column(name = "size")
private long size;
@Column(name = "protected_size")
private long protectedSize;
@Column(name = "volume_id")
private long volumeId;
@Column(name = "isolated")
private Boolean isolated;
@Column(name = "storage_pool_delta_path")
private String storagePoolDeltaPath;
@Column(name = "storage_pool_parent_path")
private String storagePoolParentPath;
@Column(name = "schedule_id")
private Long scheduleId;
public InternalBackupJoinVO() {
}
public long getId() {
return id;
}
public String getUuid() {
return uuid;
}
public long getVmId() {
return vmId;
}
public List<Backup.VolumeInfo> getBackedUpVolumes() {
if (StringUtils.isEmpty(this.backedUpVolumes)) {
return Collections.emptyList();
}
return Arrays.asList(new Gson().fromJson(this.backedUpVolumes, Backup.VolumeInfo[].class));
}
public long getBackupOfferingId() {
return backupOfferingId;
}
public long getImageStoreId() {
return imageStoreId;
}
public long getParentId() {
return parentId;
}
public String getType() {
return type;
}
public Date getDate() {
return date;
}
public Backup.Status getStatus() {
return status;
}
public Boolean getEndOfChain() {
return BooleanUtils.isTrue(endOfChain);
}
public Boolean getCurrent() {
return BooleanUtils.isTrue(current);
}
public String getImageStorePath() {
return imageStorePath;
}
public long getZoneId() {
return zoneId;
}
public long getSize() {
return size;
}
public long getProtectedSize() {
return protectedSize;
}
public long getVolumeId() {
return volumeId;
}
public Boolean getIsolated() {
return BooleanUtils.isTrue(isolated);
}
public Backup.CompressionStatus getCompressionStatus() {
return compressionStatus;
}
public String getStoragePoolDeltaPath() {
return storagePoolDeltaPath;
}
public String getStoragePoolParentPath() {
return storagePoolParentPath;
}
public Long getScheduleId() {
return scheduleId;
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
}
}

View File

@ -0,0 +1,21 @@
// 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
// with 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;
public enum InternalBackupServiceJobType {
StartCompression, FinalizeCompression, BackupValidation
}

View File

@ -0,0 +1,179 @@
// 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
// with 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 com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
@Entity
@Table(name = "internal_backup_service_job")
public class InternalBackupServiceJobVO implements InternalIdentity, Comparable<InternalBackupServiceJobVO> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "backup_id")
private long backupId;
@Column(name = "instance_id")
private long instanceId;
@Column(name = "account_id")
private long accountId;
@Column(name = "host_id")
private Long hostId;
@Column(name = "zone_id")
private long zoneId;
@Column(name = "attempts")
private int attempts;
@Column (name = "type")
private InternalBackupServiceJobType type;
@Column(name = GenericDao.CREATED_COLUMN)
private Date created;
@Column(name = "scheduled_start_time")
@Temporal(value = TemporalType.TIMESTAMP)
private Date scheduledStartTime;
@Column(name = "start_time")
@Temporal(value = TemporalType.TIMESTAMP)
private Date startTime;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
public InternalBackupServiceJobVO() {
}
public InternalBackupServiceJobVO(long backupId, long zoneId, long instanceId, long accountId, InternalBackupServiceJobType type) {
this.created = new Date();
this.backupId = backupId;
this.zoneId = zoneId;
this.instanceId = instanceId;
this.accountId = accountId;
this.type = type;
this.scheduledStartTime = this.created;
}
public InternalBackupServiceJobVO(long backupId, long zoneId, long instanceId, long accountId, InternalBackupServiceJobType type, Date scheduledStartTime) {
this(backupId, zoneId, instanceId, accountId, type);
this.scheduledStartTime = scheduledStartTime;
}
@Override
public long getId() {
return id;
}
public long getBackupId() {
return backupId;
}
public long getInstanceId() {
return instanceId;
}
public long getAccountId() {
return accountId;
}
public Long getHostId() {
return hostId;
}
public void setHostId(Long hostId) {
this.hostId = hostId;
}
public long getZoneId() {
return zoneId;
}
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
public int getAttempts() {
return attempts;
}
public void setAttempts(int attempts) {
this.attempts = attempts;
}
public InternalBackupServiceJobType getType() {
return type;
}
public Date getCreated() {
return created;
}
public Date getScheduledStartTime() {
return scheduledStartTime;
}
public void setScheduledStartTime(Date scheduledStartTime) {
this.scheduledStartTime = scheduledStartTime;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getRemoved() {
return removed;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
@Override
public int compareTo(InternalBackupServiceJobVO that) {
return this.created.compareTo(that.created);
}
@Override
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "backupId", "zoneId", "hostId", "created", "scheduledStartTime", "startTime", "attempts",
"type");
}
}

Some files were not shown because too many files have changed in this diff Show More