package org.zstack.storage.primary.smp;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.compute.vm.VmExpungeRootVolumeValidator;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.core.db.SQL;
import org.zstack.core.db.SQLBatch;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.core.workflow.Flow;
import org.zstack.header.core.workflow.FlowRollback;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.host.*;
import org.zstack.header.message.MessageReply;
import org.zstack.header.storage.backup.BackupStorageAskInstallPathMsg;
import org.zstack.header.storage.backup.BackupStorageAskInstallPathReply;
import org.zstack.header.storage.backup.BackupStorageConstant;
import org.zstack.header.storage.backup.DeleteBitsOnBackupStorageMsg;
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.snapshot.CreateTemplateFromVolumeSnapshotExtensionPoint;
import org.zstack.header.volume.VolumeFormat;
import org.zstack.header.volume.VolumeVO;
import org.zstack.header.volume.VolumeVO_;
import org.zstack.storage.primary.PrimaryStorageCapacityUpdater;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import java.util.List;
import java.util.Map;
import static org.zstack.core.Platform.operr;
/**
* Created by xing5 on 2016/3/26.
*/
public class SMPPrimaryStorageFactory implements PrimaryStorageFactory, CreateTemplateFromVolumeSnapshotExtensionPoint, HostDeleteExtensionPoint, PrimaryStorageDetachExtensionPoint {
private static final CLogger logger = Utils.getLogger(SMPPrimaryStorageFactory.class);
public static final PrimaryStorageType type = new PrimaryStorageType(SMPConstants.SMP_TYPE);
static {
type.setSupportPingStorageGateway(true);
type.setSupportHeartbeatFile(true);
type.setOrder(699);
}
@Autowired
private DatabaseFacade dbf;
@Autowired
private CloudBus bus;
@Autowired
private ErrorFacade errf;
@Override
public PrimaryStorageType getPrimaryStorageType() {
return type;
}
@Override
public PrimaryStorageInventory createPrimaryStorage(PrimaryStorageVO vo, APIAddPrimaryStorageMsg msg) {
vo.setMountPath(vo.getUrl());
vo.setType(SMPConstants.SMP_TYPE);
vo = dbf.persistAndRefresh(vo);
return PrimaryStorageInventory.valueOf(vo);
}
@Override
public PrimaryStorage getPrimaryStorage(PrimaryStorageVO vo) {
return new SMPPrimaryStorageBase(vo);
}
@Override
public PrimaryStorageInventory getInventory(String uuid) {
return PrimaryStorageInventory.valueOf(dbf.findByUuid(uuid, PrimaryStorageVO.class));
}
@VmExpungeRootVolumeValidator.VmExpungeRootVolumeValidatorMethod
static void vmExpungeRootVolumeValidator(String vmUuid, String volumeUuid) {
new SQLBatch() {
@Override
protected void scripts() {
String psUuid = q(VolumeVO.class).select(VolumeVO_.primaryStorageUuid).eq(VolumeVO_.uuid, volumeUuid)
.findValue();
if (psUuid == null) {
return;
}
if (!q(PrimaryStorageVO.class).eq(PrimaryStorageVO_.uuid, psUuid)
.eq(PrimaryStorageVO_.type, SMPConstants.SMP_TYPE)
.isExists()) {
// not SMP
return;
}
if (!q(PrimaryStorageClusterRefVO.class).eq(PrimaryStorageClusterRefVO_.primaryStorageUuid, psUuid).isExists()) {
throw new OperationFailureException(operr("the SMP primary storage[uuid:%s] is not attached" +
" to any clusters, and cannot expunge the root volume[uuid:%s] of the VM[uuid:%s]", psUuid, vmUuid, volumeUuid));
}
}
}.execute();
}
@Override
public WorkflowTemplate createTemplateFromVolumeSnapshot(final ParamIn paramIn) {
WorkflowTemplate template = new WorkflowTemplate();
final HypervisorType hvType = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(paramIn.getSnapshot().getFormat());
class Context {
String temporaryInstallPath;
}
final Context ctx = new Context();
template.setCreateTemporaryTemplate(new Flow() {
String __name__ = "create-temporary-template";
@Override
public void run(final FlowTrigger trigger, final Map data) {
CreateTemporaryVolumeFromSnapshotMsg msg = new CreateTemporaryVolumeFromSnapshotMsg();
msg.setHypervisorType(hvType.toString());
msg.setPrimaryStorageUuid(paramIn.getPrimaryStorageUuid());
msg.setTemporaryVolumeUuid(paramIn.getImage().getUuid());
msg.setSnapshot(paramIn.getSnapshot());
bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, paramIn.getPrimaryStorageUuid());
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
} else {
ParamOut paramOut = (ParamOut) data.get(ParamOut.class);
CreateTemporaryVolumeFromSnapshotReply ar = reply.castReply();
ctx.temporaryInstallPath = ar.getInstallPath();
paramOut.setSize(ar.getSize());
paramOut.setActualSize(ar.getActualSize());
trigger.next();
}
}
});
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (ctx.temporaryInstallPath != null) {
DeleteBitsOnPrimaryStorageMsg msg = new DeleteBitsOnPrimaryStorageMsg();
msg.setHypervisorType(hvType.toString());
msg.setPrimaryStorageUuid(paramIn.getPrimaryStorageUuid());
msg.setInstallPath(ctx.temporaryInstallPath);
bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, paramIn.getPrimaryStorageUuid());
bus.send(msg);
}
trigger.rollback();
}
});
template.setUploadToBackupStorage(new Flow() {
String __name__ = "upload-to-backup-storage";
@Override
public void run(final FlowTrigger trigger, Map data) {
final ParamOut out = (ParamOut) data.get(ParamOut.class);
BackupStorageAskInstallPathMsg ask = new BackupStorageAskInstallPathMsg();
ask.setImageUuid(paramIn.getImage().getUuid());
ask.setBackupStorageUuid(paramIn.getBackupStorageUuid());
ask.setImageMediaType(paramIn.getImage().getMediaType());
bus.makeTargetServiceIdByResourceUuid(ask, BackupStorageConstant.SERVICE_ID, paramIn.getBackupStorageUuid());
MessageReply ar = bus.call(ask);
if (!ar.isSuccess()) {
trigger.fail(ar.getError());
return;
}
String bsInstallPath = ((BackupStorageAskInstallPathReply)ar).getInstallPath();
UploadBitsToBackupStorageMsg msg = new UploadBitsToBackupStorageMsg();
msg.setPrimaryStorageUuid(paramIn.getPrimaryStorageUuid());
msg.setHypervisorType(hvType.toString());
msg.setPrimaryStorageInstallPath(paramIn.getSnapshot().getPrimaryStorageInstallPath());
msg.setBackupStorageUuid(paramIn.getBackupStorageUuid());
msg.setBackupStorageInstallPath(bsInstallPath);
bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, paramIn.getPrimaryStorageUuid());
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
} else {
UploadBitsToBackupStorageReply r = reply.castReply();
out.setBackupStorageInstallPath(r.getBackupStorageInstallPath());
trigger.next();
}
}
});
}
@Override
public void rollback(FlowRollback trigger, Map data) {
final ParamOut out = (ParamOut) data.get(ParamOut.class);
if (out.getBackupStorageInstallPath() != null) {
DeleteBitsOnBackupStorageMsg msg = new DeleteBitsOnBackupStorageMsg();
msg.setInstallPath(out.getBackupStorageInstallPath());
msg.setBackupStorageUuid(paramIn.getBackupStorageUuid());
bus.makeTargetServiceIdByResourceUuid(msg, BackupStorageConstant.SERVICE_ID, paramIn.getBackupStorageUuid());
bus.send(msg);
}
trigger.rollback();
}
});
template.setDeleteTemporaryTemplate(new NoRollbackFlow() {
String __name__ = "delete-temporary-template";
@Override
public void run(FlowTrigger trigger, Map data) {
DeleteBitsOnPrimaryStorageMsg msg = new DeleteBitsOnPrimaryStorageMsg();
msg.setInstallPath(ctx.temporaryInstallPath);
msg.setPrimaryStorageUuid(paramIn.getPrimaryStorageUuid());
msg.setHypervisorType(hvType.toString());
bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, paramIn.getPrimaryStorageUuid());
bus.send(msg);
trigger.next();
}
});
return template;
}
@Override
public String createTemplateFromVolumeSnapshotPrimaryStorageType() {
return SMPConstants.SMP_TYPE;
}
@Override
public void preDeleteHost(HostInventory inventory) throws HostException {
}
@Override
public void beforeDeleteHost(HostInventory inventory) {
}
@Override
public void afterDeleteHost(HostInventory inventory) {
String clusterUuid = inventory.getClusterUuid();
if (Q.New(HostVO.class).eq(HostVO_.clusterUuid, clusterUuid).notEq(HostVO_.uuid, inventory.getUuid()).isExists()) {
return;
}
final List<String> psUuids = getSMPPrimaryStorageInCluster(clusterUuid);
if(psUuids == null || psUuids.isEmpty()) {
return;
}
for (String psUuid : psUuids) {
releasePrimaryStorageCapacity(psUuid);
}
}
private void releasePrimaryStorageCapacity(String psUuid) {
SMPRecalculatePrimaryStorageCapacityMsg msg = new SMPRecalculatePrimaryStorageCapacityMsg();
msg.setPrimaryStorageUuid(psUuid);
msg.setRelease(true);
bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, psUuid);
bus.send(msg);
}
private List<String> getSMPPrimaryStorageInCluster(String clusterUuid) {
return SQL.New("select pri.uuid" +
" from PrimaryStorageVO pri, PrimaryStorageClusterRefVO ref" +
" where pri.uuid = ref.primaryStorageUuid" +
" and ref.clusterUuid = :cuuid" +
" and pri.type = :ptype")
.param("cuuid", clusterUuid)
.param("ptype", SMPConstants.SMP_TYPE)
.list();
}
@Override
public void preDetachPrimaryStorage(PrimaryStorageInventory inventory, String clusterUuid) throws PrimaryStorageException {
return;
}
@Override
public void beforeDetachPrimaryStorage(PrimaryStorageInventory inventory, String clusterUuid) {
return;
}
@Override
public void failToDetachPrimaryStorage(PrimaryStorageInventory inventory, String clusterUuid) {
return;
}
@Override
public void afterDetachPrimaryStorage(PrimaryStorageInventory inventory, String clusterUuid) {
PrimaryStorageVO vo = dbf.findByUuid(inventory.getUuid(), PrimaryStorageVO.class);
if(null == vo){
logger.warn(String.format("run afterRecalculatePrimaryStorageCapacity fail, not find ps[%s] db record", inventory.getUuid()));
return;
}
SMPPrimaryStorageBase base = new SMPPrimaryStorageBase(vo);
if(base.isUnmounted()){
//base.resetDefaultCapacityWhenUnmounted();
releasePrimaryStorageCapacity(inventory.getUuid());
}
}
}