mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
Add ability to archive snapshots on primary storage
This commit is contained in:
parent
0afba54cd5
commit
cd70ede3c2
@ -100,6 +100,13 @@ public interface SnapshotApiService {
|
||||
*/
|
||||
Snapshot createSnapshot(Long volumeId, Long policyId, Long snapshotId, Account snapshotOwner);
|
||||
|
||||
/**
|
||||
* Archives a snapshot from primary storage to secondary storage.
|
||||
* @param id Snapshot ID
|
||||
* @return Archived Snapshot object
|
||||
*/
|
||||
Snapshot archiveSnapshot(Long id);
|
||||
|
||||
/**
|
||||
* @param vol
|
||||
* @return
|
||||
|
||||
@ -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.api.command.user.snapshot;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
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.storage.Snapshot;
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
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.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@APICommand(name = "archiveSnapshot", description = "Archives (moves) a snapshot on primary storage to secondary storage",
|
||||
responseObject = SnapshotResponse.class, entityType = {Snapshot.class},
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class ArchiveSnapshotCmd extends BaseAsyncCmd {
|
||||
public static final Logger s_logger = Logger.getLogger(CreateSnapshotCmd.class.getName());
|
||||
private static final String s_name = "createsnapshotresponse";
|
||||
|
||||
@ACL(accessType = SecurityChecker.AccessType.OperateEntry)
|
||||
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = SnapshotResponse.class,
|
||||
required=true, description="The ID of the snapshot")
|
||||
private Long id;
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_SNAPSHOT_CREATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Archiving snapshot " + id + " to secondary storage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
CallContext.current().setEventDetails("Snapshot Id: " + this._uuidMgr.getUuid(Snapshot.class,getId()));
|
||||
Snapshot snapshot = _snapshotService.archiveSnapshot(getId());
|
||||
if (snapshot != null) {
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to archive snapshot");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return s_name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Snapshot snapshot = _entityMgr.findById(Snapshot.class, getId());
|
||||
if (snapshot != null) {
|
||||
return snapshot.getAccountId();
|
||||
}
|
||||
|
||||
return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@ -262,6 +262,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
SnapshotObject snapObj = (SnapshotObject)snapshot;
|
||||
AsyncCallFuture<SnapshotResult> future = new AsyncCallFuture<SnapshotResult>();
|
||||
SnapshotResult result = new SnapshotResult(snapshot, null);
|
||||
Snapshot.State origState = snapObj.getState();
|
||||
try {
|
||||
snapObj.processEvent(Snapshot.Event.BackupToSecondary);
|
||||
|
||||
@ -281,7 +282,13 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
s_logger.debug("Failed to copy snapshot", e);
|
||||
result.setResult("Failed to copy snapshot:" + e.toString());
|
||||
try {
|
||||
snapObj.processEvent(Snapshot.Event.OperationFailed);
|
||||
// When error archiving an already existing snapshot, emit OperationNotPerformed.
|
||||
// This will ensure that the original snapshot does not get deleted
|
||||
if (origState.equals(Snapshot.State.BackedUp)) {
|
||||
snapObj.processEvent(Snapshot.Event.OperationNotPerformed);
|
||||
} else {
|
||||
snapObj.processEvent(Snapshot.Event.OperationFailed);
|
||||
}
|
||||
} catch (NoTransitionException e1) {
|
||||
s_logger.debug("Failed to change state: " + e1.toString());
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ public class SnapshotStateMachineManagerImpl implements SnapshotStateMachineMana
|
||||
stateMachine.addTransition(Snapshot.State.CreatedOnPrimary, Event.OperationNotPerformed, Snapshot.State.BackedUp);
|
||||
stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationSucceeded, Snapshot.State.BackedUp);
|
||||
stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationFailed, Snapshot.State.Error);
|
||||
stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationNotPerformed, State.BackedUp);
|
||||
stateMachine.addTransition(Snapshot.State.BackedUp, Event.DestroyRequested, Snapshot.State.Destroying);
|
||||
stateMachine.addTransition(Snapshot.State.BackedUp, Event.CopyingRequested, Snapshot.State.Copying);
|
||||
stateMachine.addTransition(Snapshot.State.BackedUp, Event.BackupToSecondary, Snapshot.State.BackingUp);
|
||||
|
||||
@ -418,6 +418,7 @@ import org.apache.cloudstack.api.command.user.securitygroup.DeleteSecurityGroupC
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.RevokeSecurityGroupEgressCmd;
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.RevokeSecurityGroupIngressCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.ArchiveSnapshotCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotFromVMSnapshotCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd;
|
||||
@ -2820,6 +2821,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
||||
cmdList.add(CreateSnapshotCmd.class);
|
||||
cmdList.add(CreateSnapshotFromVMSnapshotCmd.class);
|
||||
cmdList.add(DeleteSnapshotCmd.class);
|
||||
cmdList.add(ArchiveSnapshotCmd.class);
|
||||
cmdList.add(CreateSnapshotPolicyCmd.class);
|
||||
cmdList.add(UpdateSnapshotPolicyCmd.class);
|
||||
cmdList.add(DeleteSnapshotPoliciesCmd.class);
|
||||
|
||||
@ -381,6 +381,30 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Snapshot archiveSnapshot(Long snapshotId) {
|
||||
SnapshotInfo snapshotOnPrimary = snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
|
||||
|
||||
if (snapshotOnPrimary == null || !snapshotOnPrimary.getStatus().equals(ObjectInDataStoreStateMachine.State.Ready)) {
|
||||
throw new CloudRuntimeException("Can only archive snapshots present on primary storage. " +
|
||||
"Cannot find snapshot " + snapshotId + " on primary storage");
|
||||
}
|
||||
|
||||
SnapshotInfo snapshotOnSecondary = snapshotSrv.backupSnapshot(snapshotOnPrimary);
|
||||
SnapshotVO snapshotVO = _snapshotDao.findById(snapshotOnSecondary.getId());
|
||||
snapshotVO.setLocationType(Snapshot.LocationType.SECONDARY);
|
||||
_snapshotDao.persist(snapshotVO);
|
||||
|
||||
try {
|
||||
snapshotSrv.deleteSnapshot(snapshotOnPrimary);
|
||||
} catch (Exception e) {
|
||||
throw new CloudRuntimeException("Snapshot archived to Secondary Storage but there was an error deleting " +
|
||||
" the snapshot on Primary Storage. Please manually delete the primary snapshot " + snapshotId, e);
|
||||
}
|
||||
|
||||
return snapshotOnSecondary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Snapshot backupSnapshot(Long snapshotId) {
|
||||
SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image);
|
||||
|
||||
@ -23,6 +23,7 @@ import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy;
|
||||
@ -336,4 +337,17 @@ public class SnapshotManagerTest {
|
||||
Snapshot snapshot = _snapshotMgr.backupSnapshotFromVmSnapshot(TEST_SNAPSHOT_ID, TEST_VM_ID, TEST_VOLUME_ID, TEST_VM_SNAPSHOT_ID);
|
||||
Assert.assertNull(snapshot);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testArchiveSnapshotSnapshotNotOnPrimary() {
|
||||
when(snapshotFactory.getSnapshot(anyLong(), Mockito.eq(DataStoreRole.Primary))).thenReturn(null);
|
||||
_snapshotMgr.archiveSnapshot(TEST_SNAPSHOT_ID);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testArchiveSnapshotSnapshotNotReady() {
|
||||
when(snapshotFactory.getSnapshot(anyLong(), Mockito.eq(DataStoreRole.Primary))).thenReturn(snapshotInfoMock);
|
||||
when(snapshotInfoMock.getStatus()).thenReturn(ObjectInDataStoreStateMachine.State.Destroyed);
|
||||
_snapshotMgr.archiveSnapshot(TEST_SNAPSHOT_ID);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user