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.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.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.image.ImageBackupStorageRefInventory;
import org.zstack.header.image.ImageConstant.ImageMediaType;
import org.zstack.header.storage.primary.ImageCacheVO;
import org.zstack.header.storage.primary.ImageCacheVO_;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceConstant.VmOperation;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmInstanceSpec.VolumeSpec;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.DebugUtils;
import org.zstack.utils.function.Function;
import static org.zstack.core.Platform.operr;
import javax.persistence.TypedQuery;
import java.util.List;
import java.util.Map;
import static org.zstack.core.progress.ProgressReportService.taskProgress;
/**
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VmImageSelectBackupStorageFlow extends NoRollbackFlow {
@Autowired
private DatabaseFacade dbf;
@Autowired
private ErrorFacade errf;
private String findBackupStorage(VmInstanceSpec spec, String imageUuid) {
taskProgress("Choose backup storage for downloading the image");
if (spec.getImageSpec().getInventory().getBackupStorageRefs().size() == 1) {
return spec.getImageSpec().getInventory().getBackupStorageRefs().iterator().next().getBackupStorageUuid();
}
DebugUtils.Assert(spec.getVmInventory().getZoneUuid() != null, "zone uuid must be set if the image is on multiple backup storage");
ImageBackupStorageSelector selector = new ImageBackupStorageSelector();
selector.setZoneUuid(spec.getVmInventory().getZoneUuid());
selector.setImageUuid(imageUuid);
String bsUuid = selector.select();
if (bsUuid != null) {
return bsUuid;
}
String psUuid;
if (VmOperation.NewCreate == spec.getCurrentVmOperation()) {
VolumeSpec rootVolumeSpec = spec.getVolumeSpecs().get(0);
psUuid = rootVolumeSpec.getPrimaryStorageInventory().getUuid();
} else {
psUuid = spec.getVmInventory().getRootVolume().getPrimaryStorageUuid();
}
SimpleQuery<ImageCacheVO> q = dbf.createQuery(ImageCacheVO.class);
q.add(ImageCacheVO_.imageUuid, Op.EQ, imageUuid);
q.add(ImageCacheVO_.primaryStorageUuid, Op.EQ, psUuid);
if (q.isExists()) {
// the image is already on the primary storage,
// in this case, the backup storage needs not to be Connected
selector.setCheckStatus(false);
bsUuid = selector.select();
if (bsUuid != null) {
return bsUuid;
}
}
if (spec.getVmInventory().getZoneUuid() != null) {
throw new OperationFailureException(operr("cannot find the image[uuid:%s] in any connected backup storage attached to the zone[uuid:%s]. check below:\n" +
"1. if the backup storage is attached to the zone where the VM[name: %s, uuid:%s] is in\n" +
"2. if the backup storage is in connected status, if not, try reconnecting it",
imageUuid, spec.getVmInventory().getZoneUuid(), spec.getVmInventory().getName(), spec.getVmInventory().getUuid())
);
} else {
throw new OperationFailureException(operr("cannot find the image[uuid:%s] in any connected backup storage. check below:\n" +
"1. if the backup storage is attached to the zone where the VM[name: %s, uuid:%s] is in\n" +
"2. if the backup storage is in connected status, if not, try reconnecting it",
imageUuid, spec.getVmInventory().getName(), spec.getVmInventory().getUuid())
);
}
}
@Transactional(readOnly = true)
private String findIsoBsUuidInTheZone(final String isoImageUuid, final String zoneUuid) {
String sql = "select ref.backupStorageUuid" +
" from ImageBackupStorageRefVO ref, BackupStorageZoneRefVO zoneref" +
" where ref.backupStorageUuid = zoneref.backupStorageUuid" +
" and zoneref.zoneUuid = :zoneUuid" +
" and ref.imageUuid = :imgUuid";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("zoneUuid", zoneUuid);
q.setParameter("imgUuid", isoImageUuid);
q.setMaxResults(1);
List<String> ret = q.getResultList();
if (ret.isEmpty()) {
throw new OperationFailureException(operr("no backup storage attached to the zone[uuid:%s] contains the ISO[uuid:%s]",
zoneUuid, isoImageUuid));
}
return ret.get(0);
}
@Override
public void run(FlowTrigger trigger, Map data) {
VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
if (VmOperation.NewCreate == spec.getCurrentVmOperation()) {
final String bsUuid = findBackupStorage(spec, spec.getImageSpec().getInventory().getUuid());
spec.getImageSpec().setSelectedBackupStorage(CollectionUtils.find(
spec.getImageSpec().getInventory().getBackupStorageRefs(),
new Function<ImageBackupStorageRefInventory, ImageBackupStorageRefInventory>() {
@Override
public ImageBackupStorageRefInventory call(ImageBackupStorageRefInventory arg) {
return arg.getBackupStorageUuid().equals(bsUuid) ? arg : null;
}
}));
if (ImageMediaType.ISO.toString().equals(spec.getImageSpec().getInventory().getMediaType())) {
spec.getDestIso().setBackupStorageUuid(bsUuid);
}
} else if ((VmOperation.Start == spec.getCurrentVmOperation()
|| VmOperation.Reboot == spec.getCurrentVmOperation())
&& spec.getDestIso() != null) {
spec.getDestIso().setBackupStorageUuid(
findIsoBsUuidInTheZone(spec.getDestIso().getImageUuid(), spec.getVmInventory().getZoneUuid())
);
}
trigger.next();
}
}