blob: 4808ee24e139d9e75e1cdb7a1e4dfa6cc69239e5 [file] [log] [blame]
// 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 java.util.List;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
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.SnapshotService;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotDetailsDao;
import com.cloud.storage.dao.SnapshotDetailsVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;
@Component
public class StorPoolSnapshotStrategy implements SnapshotStrategy {
private static final Logger log = Logger.getLogger(StorPoolSnapshotStrategy.class);
@Inject
private SnapshotDao _snapshotDao;
@Inject
private PrimaryDataStoreDao _primaryDataStoreDao;
@Inject
private VolumeDao _volumeDao;
@Inject
private SnapshotDataStoreDao _snapshotStoreDao;
@Inject
private SnapshotDetailsDao _snapshotDetailsDao;
@Inject
private SnapshotService snapshotSvr;
@Inject
private SnapshotDataFactory snapshotDataFactory;
@Inject
private StoragePoolDetailsDao storagePoolDetailsDao;
@Override
public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
SnapshotObject snapshotObj = (SnapshotObject) snapshotInfo;
try {
snapshotObj.processEvent(Snapshot.Event.BackupToSecondary);
snapshotObj.processEvent(Snapshot.Event.OperationSucceeded);
} catch (NoTransitionException ex) {
log.debug("Failed to change state: " + ex.toString());
try {
snapshotObj.processEvent(Snapshot.Event.OperationFailed);
} catch (NoTransitionException ex2) {
log.debug("Failed to change state: " + ex2.toString());
}
}
return snapshotInfo;
}
@Override
public boolean deleteSnapshot(Long snapshotId) {
final SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId);
VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshotVO.getVolumeId());
String name = StorPoolHelper.getSnapshotName(snapshotId, snapshotVO.getUuid(), _snapshotStoreDao, _snapshotDetailsDao);
boolean res = false;
// clean-up snapshot from Storpool storage pools
StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId());
if (storage.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) {
try {
SpConnectionDesc conn = StorPoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao);
SpApiResponse resp = StorPoolUtil.snapshotDelete(name, conn);
if (resp.getError() != null) {
final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError());
StorPoolUtil.spLog(err);
} else {
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid());
if (snapshotDetails != null) {
_snapshotDetailsDao.removeDetails(snapshotId);
}
res = deleteSnapshotFromDb(snapshotId);
StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfully=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name);
}
} catch (Exception e) {
String errMsg = String.format("Cannot delete snapshot due to %s", e.getMessage());
throw new CloudRuntimeException(errMsg);
}
}
return res;
}
@Override
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
log.debug(String.format("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op));
if (op != SnapshotOperation.DELETE) {
return StrategyPriority.CANT_HANDLE;
}
String name = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), _snapshotStoreDao, _snapshotDetailsDao);
if (name != null) {
StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name);
return StrategyPriority.HIGHEST;
}
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), snapshot.getUuid());
if (snapshotDetails != null) {
_snapshotDetailsDao.remove(snapshotDetails.getId());
}
return StrategyPriority.CANT_HANDLE;
}
private boolean deleteSnapshotChain(SnapshotInfo snapshot) {
log.debug("delete snapshot chain for snapshot: " + snapshot.getId());
boolean result = false;
boolean resultIsSet = false;
try {
while (snapshot != null &&
(snapshot.getState() == Snapshot.State.Destroying || snapshot.getState() == Snapshot.State.Destroyed || snapshot.getState() == Snapshot.State.Error)) {
SnapshotInfo child = snapshot.getChild();
if (child != null) {
log.debug("the snapshot has child, can't delete it on the storage");
break;
}
log.debug("Snapshot: " + snapshot.getId() + " doesn't have children, so it's ok to delete it and its parents");
SnapshotInfo parent = snapshot.getParent();
boolean deleted = false;
if (parent != null) {
if (parent.getPath() != null && parent.getPath().equalsIgnoreCase(snapshot.getPath())) {
log.debug("for empty delta snapshot, only mark it as destroyed in db");
snapshot.processEvent(Event.DestroyRequested);
snapshot.processEvent(Event.OperationSuccessed);
deleted = true;
if (!resultIsSet) {
result = true;
resultIsSet = true;
}
}
}
if (!deleted) {
SnapshotInfo snap = snapshotDataFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
if (StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getPath(), true) == null) {
try {
boolean r = snapshotSvr.deleteSnapshot(snapshot);
if (r) {
List<SnapshotInfo> cacheSnaps = snapshotDataFactory.listSnapshotOnCache(snapshot.getId());
for (SnapshotInfo cacheSnap : cacheSnaps) {
log.debug("Delete snapshot " + snapshot.getId() + " from image cache store: " + cacheSnap.getDataStore().getName());
cacheSnap.delete();
}
}
if (!resultIsSet) {
result = r;
resultIsSet = true;
}
} catch (Exception e) {
log.debug("Failed to delete snapshot on storage. ", e);
}
}
} else {
result = true;
}
snapshot = parent;
}
} catch (Exception e) {
log.debug("delete snapshot failed: ", e);
}
return result;
}
private boolean deleteSnapshotFromDb(Long snapshotId) {
SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId);
if (snapshotVO.getState() == Snapshot.State.Allocated) {
_snapshotDao.remove(snapshotId);
return true;
}
if (snapshotVO.getState() == Snapshot.State.Destroyed) {
return true;
}
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
List<SnapshotDataStoreVO> storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotId);
for (SnapshotDataStoreVO ref : storeRefs) {
_snapshotStoreDao.expunge(ref.getId());
}
_snapshotDao.remove(snapshotId);
return true;
}
if (snapshotVO.getState() == Snapshot.State.CreatedOnPrimary) {
snapshotVO.setState(Snapshot.State.Destroyed);
_snapshotDao.update(snapshotId, snapshotVO);
return true;
}
if (!Snapshot.State.BackedUp.equals(snapshotVO.getState()) && !Snapshot.State.Error.equals(snapshotVO.getState()) &&
!Snapshot.State.Destroying.equals(snapshotVO.getState())) {
throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is in " + snapshotVO.getState() + " Status");
}
SnapshotInfo snapshotOnImage = snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image);
if (snapshotOnImage == null) {
log.debug("Can't find snapshot on backup storage, delete it in db");
_snapshotDao.remove(snapshotId);
return true;
}
SnapshotObject obj = (SnapshotObject)snapshotOnImage;
try {
obj.processEvent(Snapshot.Event.DestroyRequested);
} catch (NoTransitionException e) {
log.debug("Failed to set the state to destroying: ", e);
return false;
}
try {
boolean result = deleteSnapshotChain(snapshotOnImage);
obj.processEvent(Snapshot.Event.OperationSucceeded);
if (result) {
SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Primary);
if (snapshotOnPrimary != null) {
snapshotOnPrimary.setState(State.Destroyed);
_snapshotStoreDao.update(snapshotOnPrimary.getId(), snapshotOnPrimary);
}
}
} catch (Exception e) {
log.debug("Failed to delete snapshot: ", e);
try {
obj.processEvent(Snapshot.Event.OperationFailed);
} catch (NoTransitionException e1) {
log.debug("Failed to change snapshot state: " + e.toString());
}
return false;
}
return true;
}
@Override
public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) {
return null;
}
@Override
public boolean revertSnapshot(SnapshotInfo snapshot) {
return false;
}
@Override
public void postSnapshotCreation(SnapshotInfo snapshot) {
}
}