Skip to content

Commit

Permalink
This feature separates the snapshot creation on primary and its backi…
Browse files Browse the repository at this point in the history
…ng up on secondary.

As part of this, a new parameter, which is optional, is added to CreateSnapshotCmd, which seperates the creation and backup.

More details in the FS-
https://cwiki.apache.org/confluence/display/CLOUDSTACK/Separate+creation+and+backup+operations+for+a+volume+snapshot
  • Loading branch information
Harika Punna committed Oct 4, 2017
1 parent 0dfdbe0 commit 6bb0ca2
Show file tree
Hide file tree
Showing 33 changed files with 593 additions and 118 deletions.
2 changes: 2 additions & 0 deletions api/src/com/cloud/storage/Snapshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ enum LocationType {

String getName();

long getSnapshotId();

Date getCreated();

Type getRecurringType();
Expand Down
2 changes: 1 addition & 1 deletion api/src/com/cloud/storage/VolumeApiService.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public interface VolumeApiService {

Volume detachVolumeFromVM(DetachVolumeCmd cmd);

Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType) throws ResourceAllocationException;
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup) throws ResourceAllocationException;

Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType) throws ResourceAllocationException;

Expand Down
1 change: 1 addition & 0 deletions api/src/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class ApiConstants {
public static final String ALGORITHM = "algorithm";
public static final String ALLOCATED_ONLY = "allocatedonly";
public static final String API_KEY = "apikey";
public static final String ASYNC_BACKUP = "asyncbackup";
public static final String USER_API_KEY = "userapikey";
public static final String APPLIED = "applied";
public static final String LIST_LB_VMIPS = "lbvmips";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the snapshot")
private String snapshotName;

@Parameter(name = ApiConstants.ASYNC_BACKUP, type = CommandType.BOOLEAN, required = false, description = "asynchronous backup if true")
private Boolean asyncBackup;

private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject;

// ///////////////////////////////////////////////////
Expand Down Expand Up @@ -200,7 +203,7 @@ public void execute() {
Snapshot snapshot;
try {
snapshot =
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType());
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup());

if (snapshot != null) {
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
Expand Down Expand Up @@ -246,4 +249,12 @@ public Long getSyncObjId() {
}
return null;
}

public Boolean getAsyncBackup() {
if (asyncBackup == null) {
return false;
} else {
return asyncBackup;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void testCreateSuccess() {
try {

Mockito.when(volumeApiService.takeSnapshot(anyLong(), anyLong(), anyLong(),
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class))).thenReturn(snapshot);
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class), anyBoolean())).thenReturn(snapshot);

} catch (Exception e) {
Assert.fail("Received exception when success expected " + e.getMessage());
Expand Down Expand Up @@ -115,7 +115,7 @@ public void testCreateFailure() {

try {
Mockito.when(volumeApiService.takeSnapshot(anyLong(), anyLong(), anyLong(),
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class))).thenReturn(null);
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class), anyBoolean())).thenReturn(null);
} catch (Exception e) {
Assert.fail("Received exception when success expected " + e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public interface SnapshotInfo extends DataObject, Snapshot {

Object getPayload();

void setFullBackup(Boolean fullBackup);

Boolean getFullBackup();

Long getDataCenterId();

ObjectInDataStoreStateMachine.State getStatus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.apache.cloudstack.engine.subsystem.api.storage;

import com.cloud.storage.Snapshot.Event;

public interface SnapshotService {
SnapshotResult takeSnapshot(SnapshotInfo snapshot);

Expand All @@ -29,4 +31,8 @@ public interface SnapshotService {
void syncVolumeSnapshotsToRegionStore(long volumeId, DataStore store);

void cleanupVolumeDuringSnapshotFailure(Long volumeId, Long snapshotId);

void processEventOnSnapshotObject(SnapshotInfo snapshot, Event event);

void cleanupOnSnapshotBackupFailure(SnapshotInfo snapshot);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.cloud.storage.Snapshot;

public interface SnapshotStrategy {

enum SnapshotOperation {
TAKE, BACKUP, DELETE, REVERT
}
Expand All @@ -32,4 +33,6 @@ enum SnapshotOperation {
boolean revertSnapshot(SnapshotInfo snapshot);

StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op);

void postSnapshotCreation(SnapshotInfo snapshot);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ public class VmWorkTakeVolumeSnapshot extends VmWork {
private Long snapshotId;
private boolean quiesceVm;
private Snapshot.LocationType locationType;
private boolean asyncBackup;

public VmWorkTakeVolumeSnapshot(long userId, long accountId, long vmId, String handlerName,
Long volumeId, Long policyId, Long snapshotId, boolean quiesceVm, Snapshot.LocationType locationType) {
Long volumeId, Long policyId, Long snapshotId, boolean quiesceVm, Snapshot.LocationType locationType, boolean asyncBackup) {
super(userId, accountId, vmId, handlerName);
this.volumeId = volumeId;
this.policyId = policyId;
this.snapshotId = snapshotId;
this.quiesceVm = quiesceVm;
this.locationType = locationType;
this.asyncBackup = asyncBackup;
}

public Long getVolumeId() {
Expand All @@ -55,4 +57,8 @@ public boolean isQuiesceVm() {
}

public Snapshot.LocationType getLocationType() { return locationType; }

public boolean isAsyncBackup() {
return asyncBackup;
}
}
5 changes: 5 additions & 0 deletions engine/schema/src/com/cloud/storage/SnapshotVO.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ public String getName() {
return name;
}

@Override
public long getSnapshotId() {
return id;
}

@Override
public short getsnapshotType() {
return snapshotType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,10 @@ protected Answer copySnapshot(DataObject srcData, DataObject destData) {

DataObject cacheData = null;
SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
Object payload = snapshotInfo.getPayload();
Boolean snapshotFullBackup = snapshotInfo.getFullBackup();
Boolean fullSnapshot = true;
if (payload != null) {
fullSnapshot = (Boolean)payload;
if (snapshotFullBackup != null) {
fullSnapshot = snapshotFullBackup;
}
Map<String, String> options = new HashMap<String, String>();
options.put("fullSnapshot", fullSnapshot.toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.snapshot;

import com.cloud.utils.SerialVersionUID;

public class SnapshotBackupException extends Exception {
private static final long serialVersionUID = SerialVersionUID.SnapshotBackupException;

public SnapshotBackupException(String msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public class SnapshotObject implements SnapshotInfo {
private SnapshotVO snapshot;
private DataStore store;
private Object payload;
private Boolean fullBackup;
@Inject
protected SnapshotDao snapshotDao;
@Inject
Expand Down Expand Up @@ -230,6 +231,11 @@ public String getName() {
return snapshot.getName();
}

@Override
public long getSnapshotId() {
return snapshot.getSnapshotId();
}

@Override
public Date getCreated() {
return snapshot.getCreated();
Expand Down Expand Up @@ -388,6 +394,16 @@ public Object getPayload() {
return payload;
}

@Override
public void setFullBackup(Boolean data) {
fullBackup = data;
}

@Override
public Boolean getFullBackup() {
return fullBackup;
}

@Override
public boolean delete() {
if (store != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
Expand All @@ -44,16 +43,22 @@
import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.framework.async.AsyncRpcContext;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;

import com.cloud.storage.CreateSnapshotPayload;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotDetailsDao;
import com.cloud.storage.template.TemplateConstants;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;

Expand All @@ -72,6 +77,8 @@ public class SnapshotServiceImpl implements SnapshotService {
DataMotionService motionSrv;
@Inject
StorageCacheManager _cacheMgr;
@Inject
private SnapshotDetailsDao _snapshotDetailsDao;

static private class CreateSnapshotContext<T> extends AsyncRpcContext<T> {
final SnapshotInfo snapshot;
Expand Down Expand Up @@ -226,9 +233,9 @@ public SnapshotResult takeSnapshot(SnapshotInfo snap) {
// we are taking delta snapshot
private DataStore findSnapshotImageStore(SnapshotInfo snapshot) {
Boolean fullSnapshot = true;
Object payload = snapshot.getPayload();
if (payload != null) {
fullSnapshot = (Boolean)payload;
Boolean snapshotFullBackup = snapshot.getFullBackup();
if (snapshotFullBackup != null) {
fullSnapshot = snapshotFullBackup;
}
if (fullSnapshot) {
return dataStoreMgr.getImageStore(snapshot.getDataCenterId());
Expand Down Expand Up @@ -300,19 +307,22 @@ protected Void copySnapshotAsyncCallback(AsyncCallbackDispatcher<SnapshotService
CopyCommandResult result = callback.getResult();
SnapshotInfo destSnapshot = context.destSnapshot;
SnapshotObject srcSnapshot = (SnapshotObject)context.srcSnapshot;
Object payload = srcSnapshot.getPayload();
CreateSnapshotPayload createSnapshotPayload = (CreateSnapshotPayload)payload;
AsyncCallFuture<SnapshotResult> future = context.future;
SnapshotResult snapResult = new SnapshotResult(destSnapshot, result.getAnswer());
if (result.isFailed()) {
try {
destSnapshot.processEvent(Event.OperationFailed);
//if backup snapshot failed, mark srcSnapshot in snapshot_store_ref as failed also
srcSnapshot.processEvent(Event.DestroyRequested);
srcSnapshot.processEvent(Event.OperationSuccessed);

srcSnapshot.processEvent(Snapshot.Event.OperationFailed);
_snapshotDao.remove(srcSnapshot.getId());
} catch (NoTransitionException e) {
s_logger.debug("Failed to update state: " + e.toString());
if (createSnapshotPayload.getAsyncBackup()) {
destSnapshot.processEvent(Event.OperationFailed);
throw new SnapshotBackupException("Failed in creating backup of snapshot with ID "+srcSnapshot.getId());
} else {
destSnapshot.processEvent(Event.OperationFailed);
//if backup snapshot failed, mark srcSnapshot in snapshot_store_ref as failed also
cleanupOnSnapshotBackupFailure(context.srcSnapshot);
}
} catch (SnapshotBackupException e) {
s_logger.debug("Failed to create backup: " + e.toString());
}
snapResult.setResult(result.getResult());
future.complete(snapResult);
Expand Down Expand Up @@ -547,4 +557,38 @@ protected Void syncSnapshotCallBack(AsyncCallbackDispatcher<SnapshotServiceImpl,

return null;
}

@Override
public void processEventOnSnapshotObject(SnapshotInfo snapshot, Snapshot.Event event) {
SnapshotObject object = (SnapshotObject)snapshot;
try {
object.processEvent(event);
} catch (NoTransitionException e) {
s_logger.debug("Unable to update the state " + e.toString());
}
}

@Override
public void cleanupOnSnapshotBackupFailure(SnapshotInfo snapshot) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
try {
SnapshotObject srcSnapshot = (SnapshotObject)snapshot;
srcSnapshot.processEvent(Event.DestroyRequested);
srcSnapshot.processEvent(Event.OperationSuccessed);

srcSnapshot.processEvent(Snapshot.Event.OperationFailed);

_snapshotDetailsDao.removeDetail(srcSnapshot.getId(), AsyncJob.Constants.MS_ID);
_snapshotDao.remove(srcSnapshot.getId());
} catch (NoTransitionException ex) {
s_logger.debug("Failed to create backup " + ex.toString());
throw new CloudRuntimeException("Failed to backup snapshot" + snapshot.getId());
}
}
});

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public SnapshotStateMachineManagerImpl() {
stateMachine.addTransition(Snapshot.State.Destroying, Event.OperationSucceeded, Snapshot.State.Destroyed);
stateMachine.addTransition(Snapshot.State.Destroying, Event.OperationFailed, State.BackedUp);
stateMachine.addTransition(Snapshot.State.Destroying, Event.DestroyRequested, Snapshot.State.Destroying);
stateMachine.addTransition(Snapshot.State.BackingUp, Event.BackupToSecondary, Snapshot.State.BackingUp);

stateMachine.registerListener(new SnapshotStateListener());
}
Expand Down
Loading

0 comments on commit 6bb0ca2

Please sign in to comment.