package org.zstack.storage.primary.nfs;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.job.Job;
import org.zstack.core.job.JobContext;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.core.workflow.ShareFlow;
import org.zstack.header.core.Completion;
import org.zstack.header.core.ReturnValueCompletion;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.image.ImageConstant.ImageMediaType;
import org.zstack.header.message.MessageReply;
import org.zstack.header.storage.backup.BackupStorageInventory;
import org.zstack.header.storage.backup.BackupStorageType;
import org.zstack.header.storage.backup.BackupStorageVO;
import org.zstack.header.storage.primary.*;
import org.zstack.header.vm.VmInstanceSpec.ImageSpec;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import java.util.Map;
/**
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class NfsDownloadImageToCacheJob implements Job {
private static final CLogger logger = Utils.getLogger(NfsDownloadImageToCacheJob.class);
@JobContext
private ImageSpec image;
@JobContext
private PrimaryStorageInventory primaryStorage;
@Autowired
private NfsPrimaryStorageFactory nfsFactory;
@Autowired
private DatabaseFacade dbf;
@Autowired
private NfsPrimaryStorageManager nfsMgr;
@Autowired
private CloudBus bus;
@Override
public void run(final ReturnValueCompletion<Object> completion) {
SimpleQuery<ImageCacheVO> query = dbf.createQuery(ImageCacheVO.class);
query.add(ImageCacheVO_.primaryStorageUuid, SimpleQuery.Op.EQ, primaryStorage.getUuid());
query.add(ImageCacheVO_.imageUuid, SimpleQuery.Op.EQ, image.getInventory().getUuid());
ImageCacheVO cvo = query.find();
if (cvo != null) {
useExistingCache(cvo, completion);
return;
}
download(completion);
}
private void download(final ReturnValueCompletion<Object> completion) {
BackupStorageVO bsvo = dbf.findByUuid(image.getSelectedBackupStorage().getBackupStorageUuid(), BackupStorageVO.class);
final BackupStorageInventory backupStorage = BackupStorageInventory.valueOf(bsvo);
final NfsPrimaryToBackupStorageMediator mediator = nfsFactory.getPrimaryToBackupStorageMediator(
BackupStorageType.valueOf(backupStorage.getType()),
nfsMgr.findHypervisorTypeByImageFormatAndPrimaryStorageUuid(image.getInventory().getFormat(), primaryStorage.getUuid())
);
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("download-image-%s-to-nfs-primary-storage-%s-cache", image.getInventory().getUuid(), primaryStorage.getUuid()));
chain.then(new ShareFlow() {
String cacheInstallPath = NfsPrimaryStorageKvmHelper.makeCachedImageInstallUrl(primaryStorage, image.getInventory());
@Override
public void setup() {
flow(new Flow() {
String __name__ = "allocate-primary-storage";
boolean s = false;
@Override
public void run(final FlowTrigger trigger, Map data) {
AllocatePrimaryStorageMsg amsg = new AllocatePrimaryStorageMsg();
amsg.setRequiredPrimaryStorageUuid(primaryStorage.getUuid());
amsg.setSize(image.getInventory().getActualSize());
amsg.setPurpose(PrimaryStorageAllocationPurpose.DownloadImage.toString());
amsg.setNoOverProvisioning(true);
bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(amsg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
} else {
s = true;
trigger.next();
}
}
});
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (s) {
IncreasePrimaryStorageCapacityMsg imsg = new IncreasePrimaryStorageCapacityMsg();
imsg.setDiskSize(image.getInventory().getActualSize());
imsg.setNoOverProvisioning(true);
imsg.setPrimaryStorageUuid(primaryStorage.getUuid());
bus.makeLocalServiceId(imsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(imsg);
}
trigger.rollback();
}
});
flow(new NoRollbackFlow() {
String __name__ = "download";
@Override
public void run(final FlowTrigger trigger, Map data) {
mediator.downloadBits(primaryStorage, backupStorage, image.getSelectedBackupStorage().getInstallPath(), cacheInstallPath, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
done(new FlowDoneHandler(completion) {
@Override
public void handle(Map data) {
ImageCacheVO cvo = new ImageCacheVO();
cvo.setImageUuid(image.getInventory().getUuid());
cvo.setInstallUrl(cacheInstallPath);
cvo.setMd5sum("no md5");
cvo.setPrimaryStorageUuid(primaryStorage.getUuid());
cvo.setSize(image.getInventory().getActualSize());
cvo.setMediaType(ImageMediaType.valueOf(image.getInventory().getMediaType()));
cvo = dbf.persistAndRefresh(cvo);
logger.debug(String.format("successfully downloaded image[uuid:%s] in image cache[id:%s, path:%s]",
image.getInventory().getUuid(), cvo.getId(), cvo.getInstallUrl()));
completion.success(ImageCacheInventory.valueOf(cvo));
}
});
error(new FlowErrorHandler(completion) {
@Override
public void handle(ErrorCode errCode, Map data) {
completion.fail(errCode);
}
});
}
}).start();
}
private void useExistingCache(final ImageCacheVO cvo, final ReturnValueCompletion<Object> completion) {
NfsPrimaryStorageBackend bkd = nfsFactory.getHypervisorBackend(nfsMgr.findHypervisorTypeByImageFormatAndPrimaryStorageUuid(image.getInventory().getFormat(), primaryStorage.getUuid()));
bkd.checkIsBitsExisting(primaryStorage, cvo.getInstallUrl(), new ReturnValueCompletion<Boolean>(completion) {
@Override
public void success(Boolean returnValue) {
if (returnValue) {
logger.debug(String.format("found image[uuid:%s] in image cache[id:%s, path:%s]",
image.getInventory().getUuid(), cvo.getId(), cvo.getInstallUrl()));
completion.success(ImageCacheInventory.valueOf(cvo));
return;
}
// return capacity and re-download
IncreasePrimaryStorageCapacityMsg rmsg = new IncreasePrimaryStorageCapacityMsg();
rmsg.setPrimaryStorageUuid(cvo.getPrimaryStorageUuid());
rmsg.setDiskSize(cvo.getSize());
bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(rmsg);
dbf.remove(cvo);
download(completion);
}
@Override
public void fail(ErrorCode errorCode) {
completion.fail(errorCode);
}
});
}
public ImageSpec getImage() {
return image;
}
public void setImage(ImageSpec image) {
this.image = image;
}
public PrimaryStorageInventory getPrimaryStorage() {
return primaryStorage;
}
public void setPrimaryStorage(PrimaryStorageInventory primaryStorage) {
this.primaryStorage = primaryStorage;
}
}