package org.zstack.compute.vm;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.cascade.CascadeConstant;
import org.zstack.core.cascade.CascadeFacade;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.header.core.Completion;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceConstant.Params;
import org.zstack.header.vm.VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.volume.VolumeDeletionStruct;
import org.zstack.header.volume.VolumeInventory;
import org.zstack.header.volume.VolumeType;
import org.zstack.header.volume.VolumeVO;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.function.Function;
import org.zstack.utils.logging.CLogger;
import javax.persistence.Query;
import java.util.List;
import java.util.Map;
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VmDeleteVolumeFlow extends NoRollbackFlow {
private static final CLogger logger = Utils.getLogger(VmDeleteVolumeFlow.class);
@Autowired
protected DatabaseFacade dbf;
@Autowired
protected CloudBus bus;
@Autowired
private CascadeFacade casf;
@Override
public void run(final FlowTrigger trigger, Map data) {
VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
final boolean deleteDataDisk = VmGlobalConfig.DELETE_DATA_VOLUME_ON_VM_DESTROY.value(Boolean.class);
/* data volume must be detached anyway no matter if it is going to be deleted */
if (spec.getVmInventory().getAllVolumes().size() > 1) {
detachDataVolumes(spec);
}
final VmInstanceDeletionPolicy deletionPolicy = (VmInstanceDeletionPolicy) data.get(Params.DeletionPolicy);
List<VolumeDeletionStruct> ctx = CollectionUtils.transformToList(spec.getVmInventory().getAllVolumes(), new Function<VolumeDeletionStruct, VolumeInventory>() {
@Override
public VolumeDeletionStruct call(VolumeInventory arg) {
if (VolumeType.Data.toString().equals(arg.getType()) && !deleteDataDisk) {
return null;
}
VolumeDeletionStruct s = new VolumeDeletionStruct();
s.setInventory(arg);
if (VolumeType.Root.toString().equals(arg.getType())) {
s.setDeletionPolicy(deletionPolicy.toString());
}
// for data volume, use volume's own deletion policy
return s;
}
});
final String issuer = VolumeVO.class.getSimpleName();
casf.asyncCascade(CascadeConstant.DELETION_FORCE_DELETE_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
@Transactional
private void detachDataVolumes(VmInstanceSpec spec) {
List<String> dataVolumeUuids = CollectionUtils.transformToList(spec.getVmInventory().getAllVolumes(), new Function<String, VolumeInventory>() {
@Override
public String call(VolumeInventory arg) {
return VolumeType.Data.toString().equals(arg.getType()) ? arg.getUuid() : null;
}
});
String sql = "update VolumeVO vol set vol.vmInstanceUuid = null where vol.uuid in (:uuids)";
Query q = dbf.getEntityManager().createQuery(sql);
q.setParameter("uuids", dataVolumeUuids);
q.executeUpdate();
}
}