Skip to content

Commit

Permalink
Merge branch 'pr-2081'
Browse files Browse the repository at this point in the history
  • Loading branch information
Mike Tutkowski committed Oct 18, 2017
2 parents 3985c68 + 6bb0ca2 commit 4c89b5b
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 @@ -27,6 +27,7 @@ public class ApiConstants {
public static final String ALLOCATED_ONLY = "allocatedonly";
public static final String ANNOTATION = "annotation";
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 4c89b5b

Please sign in to comment.