package org.zstack.storage.snapshot;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.apimediator.ApiMessageInterceptor;
import org.zstack.header.apimediator.StopRoutingException;
import org.zstack.header.errorcode.SysErrors;
import org.zstack.header.message.APIMessage;
import org.zstack.header.storage.backup.BackupStorageZoneRefVO;
import org.zstack.header.storage.backup.BackupStorageZoneRefVO_;
import org.zstack.header.storage.primary.PrimaryStorageVO;
import org.zstack.header.storage.primary.PrimaryStorageVO_;
import org.zstack.header.storage.snapshot.*;
import org.zstack.header.volume.*;
import static org.zstack.core.Platform.argerr;
import static org.zstack.core.Platform.operr;
import javax.persistence.Tuple;
import java.util.List;
/**
*/
public class VolumeSnapshotApiInterceptor implements ApiMessageInterceptor {
@Autowired
private CloudBus bus;
@Autowired
private DatabaseFacade dbf;
@Autowired
private ErrorFacade errf;
private void setServiceId(APIMessage msg) {
if (msg instanceof VolumeSnapshotMessage) {
VolumeSnapshotMessage vmsg = (VolumeSnapshotMessage) msg;
SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class);
q.select(VolumeSnapshotVO_.volumeUuid, VolumeSnapshotVO_.treeUuid);
q.add(VolumeSnapshotVO_.uuid, SimpleQuery.Op.EQ, vmsg.getSnapshotUuid());
Tuple t = q.findTuple();
String volumeUuid = t.get(0, String.class);
String treeUuid = t.get(1, String.class);
vmsg.setVolumeUuid(volumeUuid);
vmsg.setTreeUuid(treeUuid);
String resourceUuid = volumeUuid != null ? volumeUuid : treeUuid;
bus.makeTargetServiceIdByResourceUuid(msg, VolumeSnapshotConstant.SERVICE_ID, resourceUuid);
}
}
@Override
public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException {
if (msg instanceof APIDeleteVolumeSnapshotMsg) {
validate((APIDeleteVolumeSnapshotMsg) msg);
} else if (msg instanceof APIRevertVolumeFromSnapshotMsg) {
validate((APIRevertVolumeFromSnapshotMsg) msg);
} else if (msg instanceof APIDeleteVolumeSnapshotFromBackupStorageMsg) {
validate((APIDeleteVolumeSnapshotFromBackupStorageMsg) msg);
} else if (msg instanceof APICreateVolumeSnapshotSchedulerMsg) {
validate((APICreateVolumeSnapshotSchedulerMsg) msg);
} else if (msg instanceof APICreateVolumeSnapshotMsg) {
validate((APICreateVolumeSnapshotMsg) msg);
} else if (msg instanceof APIGetVolumeSnapshotTreeMsg) {
validate((APIGetVolumeSnapshotTreeMsg) msg);
} else if (msg instanceof APIBackupVolumeSnapshotMsg) {
validate((APIBackupVolumeSnapshotMsg) msg);
}
setServiceId(msg);
return msg;
}
private void validate(APIBackupVolumeSnapshotMsg msg) {
SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class);
q.select(VolumeSnapshotVO_.primaryStorageUuid);
q.add(VolumeSnapshotVO_.uuid, Op.EQ, msg.getUuid());
String priUuid = q.findValue();
if (priUuid == null) {
throw new ApiMessageInterceptionException(operr("volume snapshot[uuid:%s] is not on primary storage, cannot be backed up", msg.getUuid()));
}
if (msg.getBackupStorageUuid() != null) {
SimpleQuery<VolumeSnapshotBackupStorageRefVO> rq = dbf.createQuery(VolumeSnapshotBackupStorageRefVO.class);
rq.add(VolumeSnapshotBackupStorageRefVO_.volumeSnapshotUuid, Op.EQ, msg.getUuid());
rq.add(VolumeSnapshotBackupStorageRefVO_.backupStorageUuid, Op.EQ, msg.getBackupStorageUuid());
if (rq.isExists()) {
throw new ApiMessageInterceptionException(operr("volume snapshot[uuid:%s] is already on backup storage[uuid: %s]", msg.getUuid(), msg.getBackupStorageUuid()));
}
SimpleQuery<PrimaryStorageVO> pq = dbf.createQuery(PrimaryStorageVO.class);
pq.select(PrimaryStorageVO_.zoneUuid);
pq.add(PrimaryStorageVO_.uuid, Op.EQ, priUuid);
String zoneUuid = pq.findValue();
SimpleQuery<BackupStorageZoneRefVO> brq = dbf.createQuery(BackupStorageZoneRefVO.class);
brq.add(BackupStorageZoneRefVO_.zoneUuid, Op.EQ, zoneUuid);
brq.add(BackupStorageZoneRefVO_.backupStorageUuid, Op.EQ, msg.getBackupStorageUuid());
if (!brq.isExists()) {
throw new ApiMessageInterceptionException(operr("volume snapshot[uuid:%s] is on primary storage[uuid:%s] which is in zone[uuid:%s] that backup storage[uuid:%s] is not attached to",
msg.getUuid(), priUuid, zoneUuid, msg.getBackupStorageUuid()));
}
}
}
private void validate(APIGetVolumeSnapshotTreeMsg msg) {
if (msg.getTreeUuid() == null && msg.getVolumeUuid() == null) {
throw new ApiMessageInterceptionException(argerr("either volumeUuid or treeUuid must be set"));
}
}
private void validate(APICreateVolumeSnapshotMsg msg) {
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.select(VolumeVO_.status);
q.add(VolumeVO_.uuid, Op.EQ, msg.getVolumeUuid());
VolumeStatus status = q.findValue();
if (status != VolumeStatus.Ready) {
throw new ApiMessageInterceptionException(operr("volume[uuid:%s] is not in status Ready, current is %s, can't create snapshot", msg.getVolumeUuid(), status));
}
}
private void validate(APICreateVolumeSnapshotSchedulerMsg msg) {
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.select(VolumeVO_.status);
q.add(VolumeVO_.uuid, Op.EQ, msg.getVolumeUuid());
VolumeStatus status = q.findValue();
if (status != VolumeStatus.Ready) {
throw new ApiMessageInterceptionException(operr("volume[uuid:%s] is not in status Ready, current is %s, can't create snapshot", msg.getVolumeUuid(), status));
}
}
private void validate(APIDeleteVolumeSnapshotFromBackupStorageMsg msg) {
SimpleQuery<VolumeSnapshotBackupStorageRefVO> q = dbf.createQuery(VolumeSnapshotBackupStorageRefVO.class);
q.select(VolumeSnapshotBackupStorageRefVO_.backupStorageUuid);
q.add(VolumeSnapshotBackupStorageRefVO_.volumeSnapshotUuid, Op.EQ, msg.getSnapshotUuid());
if (!msg.getBackupStorageUuids().isEmpty()) {
q.add(VolumeSnapshotBackupStorageRefVO_.backupStorageUuid, Op.IN, msg.getBackupStorageUuids());
}
List<String> bsUuids = q.listValue();
if (bsUuids.isEmpty()) {
APIDeleteVolumeSnapshotFromBackupStorageEvent evt = new APIDeleteVolumeSnapshotFromBackupStorageEvent(msg.getId());
bus.publish(evt);
throw new StopRoutingException();
}
msg.setBackupStorageUuids(bsUuids);
}
private void validate(APIRevertVolumeFromSnapshotMsg msg) {
SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class);
q.select(VolumeSnapshotVO_.state, VolumeSnapshotVO_.volumeUuid);
q.add(VolumeSnapshotVO_.uuid, Op.EQ, msg.getUuid());
Tuple t = q.findTuple();
VolumeSnapshotState state = t.get(0, VolumeSnapshotState.class);
if (state != VolumeSnapshotState.Enabled) {
throw new ApiMessageInterceptionException(operr("volume snapshot[uuid:%s] is in state %s, cannot revert volume to it", msg.getUuid(), state));
}
String volUuid = t.get(1, String.class);
if (volUuid == null) {
throw new ApiMessageInterceptionException(operr("original volume for snapshot[uuid:%s] has been deleted, cannot revert volume to it", msg.getUuid()));
}
}
private void validate(APIDeleteVolumeSnapshotMsg msg) {
if (!dbf.isExist(msg.getUuid(), VolumeSnapshotVO.class)) {
APIDeleteVolumeSnapshotEvent evt = new APIDeleteVolumeSnapshotEvent(msg.getId());
bus.publish(evt);
throw new StopRoutingException();
}
}
}