blob: 6cf68f64fd92573ecd9e6fbf6348a786078a65fc [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.ArrayList;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
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;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy.SnapshotOperation;
import org.apache.cloudstack.engine.subsystem.api.storage.StorageStrategyFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataTO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
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.VolumeDao;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.db.QueryBuilder;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;
public class SnapshotObject implements SnapshotInfo {
private static final Logger s_logger = Logger.getLogger(SnapshotObject.class);
private SnapshotVO snapshot;
private DataStore store;
private Object payload;
private Boolean fullBackup;
private String url;
@Inject
protected SnapshotDao snapshotDao;
@Inject
protected VolumeDao volumeDao;
@Inject
protected VolumeDataFactory volFactory;
@Inject
protected SnapshotStateMachineManager stateMachineMgr;
@Inject
SnapshotDataFactory snapshotFactory;
@Inject
ObjectInDataStoreManager objectInStoreMgr;
@Inject
SnapshotDataStoreDao snapshotStoreDao;
@Inject
StorageStrategyFactory storageStrategyFactory;
@Inject
DataStoreManager dataStoreManager;
private String installPath; // temporarily set installPath before passing to resource for entries with empty installPath for object store migration case
private Long zoneId = null;
public SnapshotObject() {
}
protected void configure(SnapshotVO snapshot, DataStore store) {
this.snapshot = snapshot;
this.store = store;
}
public static SnapshotObject getSnapshotObject(SnapshotVO snapshot, DataStore store) {
SnapshotObject snapObj = ComponentContext.inject(SnapshotObject.class);
snapObj.configure(snapshot, store);
return snapObj;
}
public DataStore getStore() {
return store;
}
@Override
public SnapshotInfo getParent() {
SnapshotDataStoreVO snapStoreVO = snapshotStoreDao.findByStoreSnapshot(store.getRole(), store.getId(), snapshot.getId());
Long parentId = null;
if (snapStoreVO != null) {
parentId = snapStoreVO.getParentSnapshotId();
if (parentId != null && parentId != 0) {
return snapshotFactory.getSnapshot(parentId, store);
}
}
return null;
}
@Override
public SnapshotInfo getChild() {
QueryBuilder<SnapshotDataStoreVO> sc = QueryBuilder.create(SnapshotDataStoreVO.class);
sc.and(sc.entity().getDataStoreId(), Op.EQ, store.getId());
sc.and(sc.entity().getRole(), Op.EQ, store.getRole());
sc.and(sc.entity().getState(), Op.NIN, State.Destroying, State.Destroyed, State.Error);
sc.and(sc.entity().getParentSnapshotId(), Op.EQ, getId());
SnapshotDataStoreVO vo = sc.find();
if (vo == null) {
return null;
}
return snapshotFactory.getSnapshot(vo.getSnapshotId(), store);
}
@Override
public List<SnapshotInfo> getChildren() {
QueryBuilder<SnapshotDataStoreVO> sc = QueryBuilder.create(SnapshotDataStoreVO.class);
sc.and(sc.entity().getDataStoreId(), Op.EQ, store.getId());
sc.and(sc.entity().getRole(), Op.EQ, store.getRole());
sc.and(sc.entity().getState(), Op.NIN, State.Destroying, State.Destroyed, State.Error);
sc.and(sc.entity().getParentSnapshotId(), Op.EQ, getId());
List<SnapshotDataStoreVO> vos = sc.list();
List<SnapshotInfo> children = new ArrayList<>();
if (vos != null) {
for (SnapshotDataStoreVO vo : vos) {
SnapshotInfo info = snapshotFactory.getSnapshot(vo.getSnapshotId(), vo.getDataStoreId(), DataStoreRole.Image);
if (info != null) {
children.add(info);
}
}
}
return children;
}
@Override
public boolean isRevertable() {
SnapshotStrategy snapshotStrategy = storageStrategyFactory.getSnapshotStrategy(snapshot, SnapshotOperation.REVERT);
if (snapshotStrategy != null) {
return true;
}
return false;
}
@Override
public long getPhysicalSize() {
long physicalSize = 0;
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Image, store.getId(), snapshot.getId());
if (snapshotStore != null) {
physicalSize = snapshotStore.getPhysicalSize();
}
return physicalSize;
}
@Override
public void markBackedUp() throws CloudRuntimeException{
try {
processEvent(Event.OperationNotPerformed);
} catch (NoTransitionException ex) {
s_logger.error("no transition error: ", ex);
throw new CloudRuntimeException("Error marking snapshot backed up: " +
this.snapshot.getId() + " " + ex.getMessage());
}
}
@Override
public VolumeInfo getBaseVolume() {
return volFactory.getVolume(snapshot.getVolumeId());
}
@Override
public long getId() {
return snapshot.getId();
}
@Override
public String getUri() {
if (url != null) {
return url;
}
return snapshot.getUuid();
}
public void setUrl(String url) {
this.url = url;
}
@Override
public DataStore getDataStore() {
return store;
}
@Override
public Long getSize() {
return snapshot.getSize();
}
@Override
public DataObjectType getType() {
return DataObjectType.SNAPSHOT;
}
@Override
public String getUuid() {
return snapshot.getUuid();
}
@Override
public void processEvent(ObjectInDataStoreStateMachine.Event event) {
try {
objectInStoreMgr.update(this, event);
} catch (Exception e) {
s_logger.debug("Failed to update state:" + e.toString());
throw new CloudRuntimeException("Failed to update state: " + e.toString());
} finally {
DataObjectInStore obj = objectInStoreMgr.findObject(this, this.getDataStore());
if (event == ObjectInDataStoreStateMachine.Event.OperationFailed && !obj.getState().equals(ObjectInDataStoreStateMachine.State.Destroying)) {
// Don't delete db entry if snapshot is successfully removed.
objectInStoreMgr.deleteIfNotReady(this);
}
}
}
@Override
public long getAccountId() {
return snapshot.getAccountId();
}
@Override
public long getVolumeId() {
return snapshot.getVolumeId();
}
@Override
public String getPath() {
if (installPath != null)
return installPath;
DataObjectInStore objectInStore = objectInStoreMgr.findObject(this, getDataStore());
if (objectInStore != null) {
return objectInStore.getInstallPath();
}
return null;
}
public void setPath(String installPath) {
this.installPath = installPath;
}
@Override
public String getName() {
return snapshot.getName();
}
@Override
public long getSnapshotId() {
return snapshot.getSnapshotId();
}
@Override
public Date getCreated() {
return snapshot.getCreated();
}
@Override
public Type getRecurringType() {
return snapshot.getRecurringType();
}
@Override
public LocationType getLocationType() { return snapshot.getLocationType(); }
@Override
public State getState() {
return snapshot.getState();
}
@Override
public HypervisorType getHypervisorType() {
return snapshot.getHypervisorType();
}
@Override
public boolean isRecursive() {
return snapshot.isRecursive();
}
@Override
public short getSnapshotType() {
return snapshot.getSnapshotType();
}
@Override
public long getDomainId() {
return snapshot.getDomainId();
}
@Override
public Long getDataCenterId() {
if (zoneId == null) {
zoneId = dataStoreManager.getStoreZoneId(store.getId(), store.getRole());
}
return zoneId;
}
public void processEvent(Snapshot.Event event) throws NoTransitionException {
stateMachineMgr.processEvent(snapshot, event);
}
public SnapshotVO getSnapshotVO() {
return snapshot;
}
@Override
public DataTO getTO() {
DataTO to = store.getDriver().getTO(this);
if (to == null) {
return new SnapshotObjectTO(this);
}
return to;
}
@Override
public void processEvent(ObjectInDataStoreStateMachine.Event event, Answer answer) {
try {
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(getDataStore().getRole(), getDataStore().getId(), getId());
if (answer instanceof CreateObjectAnswer) {
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer)answer).getData();
snapshotStore.setInstallPath(snapshotTO.getPath());
snapshotStoreDao.update(snapshotStore.getId(), snapshotStore);
} else if (answer instanceof CopyCmdAnswer) {
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CopyCmdAnswer)answer).getNewData();
snapshotStore.setInstallPath(snapshotTO.getPath());
if (snapshotTO.getPhysicalSize() != null) {
// For S3 delta snapshot, physical size is currently not set
snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize());
}
if (snapshotTO.getParentSnapshotPath() == null) {
snapshotStore.setParentSnapshotId(0L);
}
snapshotStoreDao.update(snapshotStore.getId(), snapshotStore);
// update side-effect of snapshot operation
if (snapshotTO.getVolume() != null && snapshotTO.getVolume().getPath() != null) {
VolumeVO vol = volumeDao.findByUuid(snapshotTO.getVolume().getUuid());
if (vol != null) {
s_logger.info("Update volume path change due to snapshot operation, volume " + vol.getId() + " path: " + vol.getPath() + "->" +
snapshotTO.getVolume().getPath());
vol.setPath(snapshotTO.getVolume().getPath());
volumeDao.update(vol.getId(), vol);
} else {
s_logger.error("Cound't find the original volume with uuid: " + snapshotTO.getVolume().getUuid());
}
}
} else {
throw new CloudRuntimeException("Unknown answer: " + answer.getClass());
}
} catch (RuntimeException ex) {
if (event == ObjectInDataStoreStateMachine.Event.OperationFailed) {
objectInStoreMgr.deleteIfNotReady(this);
}
throw ex;
}
this.processEvent(event);
}
@Override
public void incRefCount() {
if (store == null) {
return;
}
if (store.getRole() == DataStoreRole.Image || store.getRole() == DataStoreRole.ImageCache) {
SnapshotDataStoreVO store = snapshotStoreDao.findByStoreSnapshot(this.store.getRole(), this.store.getId(), getId());
store.incrRefCnt();
store.setLastUpdated(new Date());
snapshotStoreDao.update(store.getId(), store);
}
}
@Override
public void decRefCount() {
if (store == null) {
return;
}
if (store.getRole() == DataStoreRole.Image || store.getRole() == DataStoreRole.ImageCache) {
SnapshotDataStoreVO store = snapshotStoreDao.findByStoreSnapshot(this.store.getRole(), this.store.getId(), getId());
store.decrRefCnt();
store.setLastUpdated(new Date());
snapshotStoreDao.update(store.getId(), store);
}
}
@Override
public Long getRefCount() {
if (store == null) {
return null;
}
if (store.getRole() == DataStoreRole.Image || store.getRole() == DataStoreRole.ImageCache) {
SnapshotDataStoreVO store = snapshotStoreDao.findByStoreSnapshot(this.store.getRole(), this.store.getId(), getId());
return store.getRefCnt();
}
return null;
}
@Override
public ObjectInDataStoreStateMachine.State getStatus() {
return objectInStoreMgr.findObject(this, store).getObjectInStoreState();
}
@Override
public void addPayload(Object data) {
payload = data;
}
@Override
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) {
return store.delete(this);
}
return true;
}
@Override
public Class<?> getEntityType() {
return Snapshot.class;
}
}