Skip to content
9 changes: 9 additions & 0 deletions api/src/main/java/com/cloud/event/EventTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.backup.BackupRepositoryService;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet;
import org.apache.cloudstack.extension.Extension;
Expand Down Expand Up @@ -852,6 +853,10 @@ public class EventTypes {
// Custom Action
public static final String EVENT_CUSTOM_ACTION = "CUSTOM.ACTION";

// Backup Repository
public static final String EVENT_BACKUP_REPOSITORY_ADD = "BACKUP.REPOSITORY.ADD";
public static final String EVENT_BACKUP_REPOSITORY_UPDATE = "BACKUP.REPOSITORY.UPDATE";

static {

// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
Expand Down Expand Up @@ -1385,6 +1390,10 @@ public class EventTypes {
entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_ADD, ExtensionCustomAction.class);
entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_UPDATE, ExtensionCustomAction.class);
entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_DELETE, ExtensionCustomAction.class);

// Backup Repository
entityEventDetails.put(EVENT_BACKUP_REPOSITORY_ADD, BackupRepositoryService.class);
entityEventDetails.put(EVENT_BACKUP_REPOSITORY_UPDATE, BackupRepositoryService.class);
}

public static boolean isNetworkEvent(String eventType) {
Expand Down
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/vm/VirtualMachineProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public static class Param {
public static final Param BootIntoSetup = new Param("enterHardwareSetup");
public static final Param PreserveNics = new Param("PreserveNics");
public static final Param ConsiderLastHost = new Param("ConsiderLastHost");
public static final Param ReturnAfterVolumePrepare = new Param("ReturnAfterVolumePrepare");

private String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public class ApiConstants {
public static final String DOMAIN_ID = "domainid";
public static final String DOMAIN__ID = "domainId";
public static final String DURATION = "duration";
public static final String DRAAS_ENABLED = "draasenabled";
public static final String ELIGIBLE = "eligible";
public static final String EMAIL = "email";
public static final String END_ASN = "endasn";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public class AddBackupRepositoryCmd extends BaseCmd {
@Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, description = "capacity of this backup repository")
private Long capacityBytes;

@Parameter(name = ApiConstants.DRAAS_ENABLED, type = CommandType.BOOLEAN, description = "the backup repository is configured to be used for disaster recovery on other Zones", since = "4.22.0")
private Boolean draasEnabled;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
Expand Down Expand Up @@ -109,6 +111,10 @@ public Long getCapacityBytes() {
return capacityBytes;
}

public Boolean isDraasEnabled() {
return draasEnabled;
}

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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.repository;

import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.BackupRepositoryResponse;
import org.apache.cloudstack.backup.BackupRepository;
import org.apache.cloudstack.backup.BackupRepositoryService;
import org.apache.cloudstack.context.CallContext;

import javax.inject.Inject;

@APICommand(name = "updateBackupRepository",
description = "Update a backup repository",
responseObject = BackupRepositoryResponse.class, since = "4.22.0",
authorized = {RoleType.Admin})
public class UpdateBackupRepositoryCmd extends BaseCmd {

@Inject
private BackupRepositoryService backupRepositoryService;

/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////

@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupRepositoryResponse.class, description = "ID of the backup repository")
private Long id;

@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the backup repository")
private String name;

@Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, required = true, description = "address of the backup repository")
private String address;

@Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options")
private String mountOptions;

@Parameter(name = ApiConstants.DRAAS_ENABLED, type = CommandType.BOOLEAN, description = "the backup repository is configured to be used for disaster recovery on other Zones")
private Boolean draasEnabled;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////

public BackupRepositoryService getBackupRepositoryService() {
return backupRepositoryService;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getAddress() {
return address;
}

public String getMountOptions() {
return mountOptions == null ? "" : mountOptions;
}

public Boolean isDraasEnabled() {
return draasEnabled;
}

/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////

@Override
public void execute() {
try {
BackupRepository result = backupRepositoryService.updateBackupRepository(this);
if (result != null) {
BackupRepositoryResponse response = _responseGenerator.createBackupRepositoryResponse(result);
response.setResponseName(getCommandName());
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the backup repository");
}
} catch (Exception ex4) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex4.getMessage());
}

}

@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ public class BackupRepositoryResponse extends BaseResponse {
@Param(description = "capacity of the backup repository")
private Long capacityBytes;

@SerializedName(ApiConstants.DRAAS_ENABLED)
@Param(description = "is the backup repository configured to be used for disaster recovery on other Zones")
private Boolean draasEnabled;

@SerializedName("created")
@Param(description = "the date and time the backup repository was added")
private Date created;
Expand Down Expand Up @@ -132,6 +136,14 @@ public void setCapacityBytes(Long capacityBytes) {
this.capacityBytes = capacityBytes;
}

public Boolean getDraasEnabled() {
return draasEnabled;
}

public void setDraasEnabled(Boolean draasEnabled) {
this.draasEnabled = draasEnabled;
}

public Date getCreated() {
return created;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer

Boolean canCreateInstanceFromBackup(Long backupId);

Boolean canCreateInstanceFromBackupAcrossZones(Long backupId);

/**
* Restore a backup to a new Instance
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

public interface BackupProvider {

Boolean isDraasEnabled(BackupOffering backupOffering);

/**
* Returns the unique name of the provider
* @return returns provider name
Expand Down Expand Up @@ -85,7 +87,7 @@ public interface BackupProvider {
*/
boolean deleteBackup(Backup backup, boolean forced);

boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);
Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid);

/**
* Restore VM from backup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ public interface BackupRepository extends InternalIdentity, Identity {
String getType();
String getAddress();
String getMountOptions();
void setMountOptions(String mountOptions);
void setUsedBytes(Long usedBytes);
Long getCapacityBytes();
Long getUsedBytes();
void setCapacityBytes(Long capacityBytes);
Boolean isDraasEnabled();
void setDraasEnabled(Boolean draasEnabled);
Date getCreated();
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
import org.apache.cloudstack.api.command.user.backup.repository.AddBackupRepositoryCmd;
import org.apache.cloudstack.api.command.user.backup.repository.DeleteBackupRepositoryCmd;
import org.apache.cloudstack.api.command.user.backup.repository.ListBackupRepositoriesCmd;
import org.apache.cloudstack.api.command.user.backup.repository.UpdateBackupRepositoryCmd;

import java.util.List;

public interface BackupRepositoryService {
BackupRepository addBackupRepository(AddBackupRepositoryCmd cmd);
BackupRepository updateBackupRepository(UpdateBackupRepositoryCmd cmd);
boolean deleteBackupRepository(DeleteBackupRepositoryCmd cmd);
Pair<List<BackupRepository>, Integer> listBackupRepositories(ListBackupRepositoriesCmd cmd);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class RestoreBackupCommand extends Command {
private Boolean vmExists;
private String restoreVolumeUUID;
private VirtualMachine.State vmState;
private Integer mountTimeout;

protected RestoreBackupCommand() {
super();
Expand Down Expand Up @@ -136,4 +137,12 @@ public List<String> getBackupVolumesUUIDs() {
public void setBackupVolumesUUIDs(List<String> backupVolumesUUIDs) {
this.backupVolumesUUIDs = backupVolumesUUIDs;
}

public Integer getMountTimeout() {
return this.mountTimeout;
}

public void setMountTimeout(Integer mountTimeout) {
this.mountTimeout = mountTimeout;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,21 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
volumeMgr.prepare(vmProfile, dest);
}

if (params != null) {
Boolean returnAfterVolumePrepare = (Boolean) params.get(VirtualMachineProfile.Param.ReturnAfterVolumePrepare);
if (Boolean.TRUE.equals(returnAfterVolumePrepare)) {
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)) {
logger.error("Unable to transition to a new state. VM uuid: {}, VM oldstate: {}, Event: {}", vm, vm.getState(), Event.AgentReportStopped);
throw new ConcurrentOperationException(String.format("Failed to deploy VM %s", vm));
}

logger.debug("Volume preparation completed for VM {} (VM state set to Stopped)", vm);
return;
}
}

if (!reuseVolume) {
reuseVolume = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public class BackupRepositoryVO implements BackupRepository {
@Column(name = "capacity_bytes", nullable = true)
private Long capacityBytes;

@Column(name = "draas_enabled")
private Boolean draasEnabled;

@Column(name = "created")
@Temporal(value = TemporalType.TIMESTAMP)
private Date created;
Expand All @@ -79,7 +82,7 @@ public BackupRepositoryVO() {
this.uuid = UUID.randomUUID().toString();
}

public BackupRepositoryVO(final long zoneId, final String provider, final String name, final String type, final String address, final String mountOptions, final Long capacityBytes) {
public BackupRepositoryVO(final long zoneId, final String provider, final String name, final String type, final String address, final String mountOptions, final Long capacityBytes, final Boolean draasEnabled) {
this();
this.zoneId = zoneId;
this.provider = provider;
Expand All @@ -88,6 +91,7 @@ public BackupRepositoryVO(final long zoneId, final String provider, final String
this.address = address;
this.mountOptions = mountOptions;
this.capacityBytes = capacityBytes;
this.draasEnabled = draasEnabled;
this.created = new Date();
}

Expand Down Expand Up @@ -139,6 +143,11 @@ public String getMountOptions() {
return mountOptions;
}

@Override
public void setMountOptions(String mountOptions) {
this.mountOptions = mountOptions;
}

@Override
public Long getUsedBytes() {
return usedBytes;
Expand All @@ -154,6 +163,16 @@ public Long getCapacityBytes() {
return capacityBytes;
}

@Override
public Boolean isDraasEnabled() {
return draasEnabled;
}

@Override
public void setDraasEnabled(Boolean draasEnabled) {
this.draasEnabled = draasEnabled;
}

@Override
public void setCapacityBytes(Long capacityBytes) {
this.capacityBytes = capacityBytes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@

-- Increase length of scripts_version column to 128 due to md5sum to sha512sum change
CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.domain_router', 'scripts_version', 'scripts_version', 'VARCHAR(128)');

-- Add the column draas_enabled to cloud.backup_repository. if enabled it means that new Instance can be created on another Zones from Backups on this Repository.
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_repository', 'draas_enabled', 'TINYINT(1) DEFAULT NULL COMMENT ''Backup Repository can be used for disaster recovery on another zone''');
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public class DummyBackupProvider extends AdapterBase implements BackupProvider {
@Inject
private DiskOfferingDao diskOfferingDao;

@Override
public Boolean isDraasEnabled(BackupOffering backupOffering) {
return true;
}

@Override
public String getName() {
return "dummy";
Expand Down Expand Up @@ -199,7 +204,7 @@ public void syncBackupStorageStats(Long zoneId) {
}

@Override
public boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
return true;
public Pair<Boolean, String> restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid) {
return new Pair<>(true, null);
}
}
Loading
Loading