package org.zstack.image;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.Platform;
import org.zstack.core.asyncbatch.AsyncBatchRunner;
import org.zstack.core.asyncbatch.LoopAsyncBatch;
import org.zstack.core.cloudbus.*;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.config.GlobalConfig;
import org.zstack.core.config.GlobalConfigUpdateExtensionPoint;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SQL;
import org.zstack.core.db.SQLBatchWithReturn;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.defer.Defer;
import org.zstack.core.defer.Deferred;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.notification.N;
import org.zstack.core.thread.CancelablePeriodicTask;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.core.workflow.ShareFlow;
import org.zstack.header.AbstractService;
import org.zstack.header.core.AsyncLatch;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.errorcode.ErrorCodeList;
import org.zstack.header.errorcode.SysErrors;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.identity.*;
import org.zstack.header.image.*;
import org.zstack.header.image.APICreateRootVolumeTemplateFromVolumeSnapshotEvent.Failure;
import org.zstack.header.image.ImageConstant.ImageMediaType;
import org.zstack.header.image.ImageDeletionPolicyManager.ImageDeletionPolicy;
import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint;
import org.zstack.header.message.APIMessage;
import org.zstack.header.message.Message;
import org.zstack.header.message.MessageReply;
import org.zstack.header.message.NeedQuotaCheckMessage;
import org.zstack.header.rest.RESTFacade;
import org.zstack.header.search.SearchOp;
import org.zstack.header.storage.backup.*;
import org.zstack.header.storage.primary.PrimaryStorageVO;
import org.zstack.header.storage.primary.PrimaryStorageVO_;
import org.zstack.header.storage.snapshot.*;
import org.zstack.header.vm.CreateTemplateFromVmRootVolumeMsg;
import org.zstack.header.vm.CreateTemplateFromVmRootVolumeReply;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.volume.*;
import org.zstack.identity.AccountManager;
import org.zstack.identity.QuotaUtil;
import org.zstack.search.SearchQuery;
import org.zstack.tag.TagManager;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.ObjectUtils;
import org.zstack.utils.RunOnce;
import org.zstack.utils.Utils;
import org.zstack.utils.data.SizeUnit;
import org.zstack.utils.function.ForEachFunction;
import org.zstack.utils.function.Function;
import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static org.zstack.core.Platform.operr;
import static org.zstack.utils.CollectionDSL.list;
public class ImageManagerImpl extends AbstractService implements ImageManager, ManagementNodeReadyExtensionPoint,
ReportQuotaExtensionPoint, ResourceOwnerPreChangeExtensionPoint {
private static final CLogger logger = Utils.getLogger(ImageManagerImpl.class);
@Autowired
private CloudBus bus;
@Autowired
private PluginRegistry pluginRgty;
@Autowired
private DatabaseFacade dbf;
@Autowired
private AccountManager acntMgr;
@Autowired
private ErrorFacade errf;
@Autowired
private TagManager tagMgr;
@Autowired
private ThreadFacade thdf;
@Autowired
private ResourceDestinationMaker destMaker;
@Autowired
private ImageDeletionPolicyManager deletionPolicyMgr;
@Autowired
protected RESTFacade restf;
private Map<String, ImageFactory> imageFactories = Collections.synchronizedMap(new HashMap<>());
private static final Set<Class> allowedMessageAfterDeletion = new HashSet<>();
private Future<Void> expungeTask;
static {
allowedMessageAfterDeletion.add(ImageDeletionMsg.class);
}
@Override
@MessageSafe
public void handleMessage(Message msg) {
if (msg instanceof ImageMessage) {
passThrough((ImageMessage) msg);
} else if (msg instanceof APIMessage) {
handleApiMessage(msg);
} else {
handleLocalMessage(msg);
}
}
private void handleLocalMessage(Message msg) {
bus.dealWithUnknownMessage(msg);
}
private void handleApiMessage(Message msg) {
if (msg instanceof APIAddImageMsg) {
handle((APIAddImageMsg) msg);
} else if (msg instanceof APIListImageMsg) {
handle((APIListImageMsg) msg);
} else if (msg instanceof APISearchImageMsg) {
handle((APISearchImageMsg) msg);
} else if (msg instanceof APIGetImageMsg) {
handle((APIGetImageMsg) msg);
} else if (msg instanceof APICreateRootVolumeTemplateFromRootVolumeMsg) {
handle((APICreateRootVolumeTemplateFromRootVolumeMsg) msg);
} else if (msg instanceof APICreateRootVolumeTemplateFromVolumeSnapshotMsg) {
handle((APICreateRootVolumeTemplateFromVolumeSnapshotMsg) msg);
} else if (msg instanceof APICreateDataVolumeTemplateFromVolumeMsg) {
handle((APICreateDataVolumeTemplateFromVolumeMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(final APICreateDataVolumeTemplateFromVolumeMsg msg) {
final APICreateDataVolumeTemplateFromVolumeEvent evt = new APICreateDataVolumeTemplateFromVolumeEvent(msg.getId());
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("create-data-volume-template-from-volume-%s", msg.getVolumeUuid()));
chain.then(new ShareFlow() {
List<BackupStorageInventory> backupStorage = new ArrayList<>();
ImageVO image;
long actualSize;
@Override
public void setup() {
flow(new NoRollbackFlow() {
String __name__ = "get-actual-size-of-data-volume";
@Override
public void run(final FlowTrigger trigger, Map data) {
SyncVolumeSizeMsg smsg = new SyncVolumeSizeMsg();
smsg.setVolumeUuid(msg.getVolumeUuid());
bus.makeTargetServiceIdByResourceUuid(smsg, VolumeConstant.SERVICE_ID, msg.getVolumeUuid());
bus.send(smsg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
return;
}
SyncVolumeSizeReply sr = reply.castReply();
actualSize = sr.getActualSize();
trigger.next();
}
});
}
});
flow(new Flow() {
String __name__ = "create-image-in-database";
@Override
public void run(FlowTrigger trigger, Map data) {
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.select(VolumeVO_.format, VolumeVO_.size);
q.add(VolumeVO_.uuid, Op.EQ, msg.getVolumeUuid());
Tuple t = q.findTuple();
String format = t.get(0, String.class);
long size = t.get(1, Long.class);
final ImageVO vo = new ImageVO();
vo.setUuid(msg.getResourceUuid() == null ? Platform.getUuid() : msg.getResourceUuid());
vo.setName(msg.getName());
vo.setDescription(msg.getDescription());
vo.setType(ImageConstant.ZSTACK_IMAGE_TYPE);
vo.setMediaType(ImageMediaType.DataVolumeTemplate);
vo.setSize(size);
vo.setActualSize(actualSize);
vo.setState(ImageState.Enabled);
vo.setStatus(ImageStatus.Creating);
vo.setFormat(format);
vo.setUrl(String.format("volume://%s", msg.getVolumeUuid()));
image = dbf.persistAndRefresh(vo);
acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), vo.getUuid(), ImageVO.class);
tagMgr.createTagsFromAPICreateMessage(msg, vo.getUuid(), ImageVO.class.getSimpleName());
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (image != null) {
dbf.remove(image);
}
trigger.rollback();
}
});
flow(new Flow() {
String __name__ = "select-backup-storage";
@Override
public void run(final FlowTrigger trigger, Map data) {
final String zoneUuid = new Callable<String>() {
@Override
@Transactional(readOnly = true)
public String call() {
String sql = "select ps.zoneUuid" +
" from PrimaryStorageVO ps, VolumeVO vol" +
" where vol.primaryStorageUuid = ps.uuid" +
" and vol.uuid = :volUuid";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("volUuid", msg.getVolumeUuid());
return q.getSingleResult();
}
}.call();
if (msg.getBackupStorageUuids() == null) {
AllocateBackupStorageMsg amsg = new AllocateBackupStorageMsg();
amsg.setRequiredZoneUuid(zoneUuid);
amsg.setSize(actualSize);
bus.makeLocalServiceId(amsg, BackupStorageConstant.SERVICE_ID);
bus.send(amsg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
backupStorage.add(((AllocateBackupStorageReply) reply).getInventory());
trigger.next();
} else {
trigger.fail(errf.stringToOperationError("cannot find proper backup storage", reply.getError()));
}
}
});
} else {
List<AllocateBackupStorageMsg> amsgs = CollectionUtils.transformToList(msg.getBackupStorageUuids(), new Function<AllocateBackupStorageMsg, String>() {
@Override
public AllocateBackupStorageMsg call(String arg) {
AllocateBackupStorageMsg amsg = new AllocateBackupStorageMsg();
amsg.setRequiredZoneUuid(zoneUuid);
amsg.setSize(actualSize);
amsg.setBackupStorageUuid(arg);
bus.makeLocalServiceId(amsg, BackupStorageConstant.SERVICE_ID);
return amsg;
}
});
bus.send(amsgs, new CloudBusListCallBack(trigger) {
@Override
public void run(List<MessageReply> replies) {
List<ErrorCode> errs = new ArrayList<>();
for (MessageReply r : replies) {
if (r.isSuccess()) {
backupStorage.add(((AllocateBackupStorageReply) r).getInventory());
} else {
errs.add(r.getError());
}
}
if (backupStorage.isEmpty()) {
trigger.fail(operr("failed to allocate all backup storage[uuid:%s], a list of error: %s",
msg.getBackupStorageUuids(), JSONObjectUtil.toJsonString(errs)));
} else {
trigger.next();
}
}
});
}
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (!backupStorage.isEmpty()) {
List<ReturnBackupStorageMsg> rmsgs = CollectionUtils.transformToList(backupStorage, new Function<ReturnBackupStorageMsg, BackupStorageInventory>() {
@Override
public ReturnBackupStorageMsg call(BackupStorageInventory arg) {
ReturnBackupStorageMsg rmsg = new ReturnBackupStorageMsg();
rmsg.setBackupStorageUuid(arg.getUuid());
rmsg.setSize(actualSize);
bus.makeLocalServiceId(rmsg, BackupStorageConstant.SERVICE_ID);
return rmsg;
}
});
bus.send(rmsgs, new CloudBusListCallBack(null) {
@Override
public void run(List<MessageReply> replies) {
for (MessageReply r : replies) {
BackupStorageInventory bs = backupStorage.get(replies.indexOf(r));
logger.warn(String.format("failed to return %s bytes to backup storage[uuid:%s]", acntMgr, bs.getUuid()));
}
}
});
}
trigger.rollback();
}
});
flow(new NoRollbackFlow() {
String __name__ = "create-data-volume-template-from-volume";
@Override
public void run(final FlowTrigger trigger, Map data) {
List<CreateDataVolumeTemplateFromDataVolumeMsg> cmsgs = CollectionUtils.transformToList(backupStorage, new Function<CreateDataVolumeTemplateFromDataVolumeMsg, BackupStorageInventory>() {
@Override
public CreateDataVolumeTemplateFromDataVolumeMsg call(BackupStorageInventory bs) {
CreateDataVolumeTemplateFromDataVolumeMsg cmsg = new CreateDataVolumeTemplateFromDataVolumeMsg();
cmsg.setVolumeUuid(msg.getVolumeUuid());
cmsg.setBackupStorageUuid(bs.getUuid());
cmsg.setImageUuid(image.getUuid());
bus.makeTargetServiceIdByResourceUuid(cmsg, VolumeConstant.SERVICE_ID, msg.getVolumeUuid());
return cmsg;
}
});
bus.send(cmsgs, new CloudBusListCallBack(msg) {
@Override
public void run(List<MessageReply> replies) {
int fail = 0;
String mdsum = null;
ErrorCode err = null;
String format = null;
for (MessageReply r : replies) {
BackupStorageInventory bs = backupStorage.get(replies.indexOf(r));
if (!r.isSuccess()) {
logger.warn(String.format("failed to create data volume template from volume[uuid:%s] on backup storage[uuid:%s], %s",
msg.getVolumeUuid(), bs.getUuid(), r.getError()));
fail++;
err = r.getError();
continue;
}
CreateDataVolumeTemplateFromDataVolumeReply reply = r.castReply();
ImageBackupStorageRefVO ref = new ImageBackupStorageRefVO();
ref.setBackupStorageUuid(bs.getUuid());
ref.setStatus(ImageStatus.Ready);
ref.setImageUuid(image.getUuid());
ref.setInstallPath(reply.getInstallPath());
dbf.persist(ref);
if (mdsum == null) {
mdsum = reply.getMd5sum();
}
if (reply.getFormat() != null) {
format = reply.getFormat();
}
}
int backupStorageNum = msg.getBackupStorageUuids() == null ? 1 : msg.getBackupStorageUuids().size();
if (fail == backupStorageNum) {
ErrorCode errCode = operr("failed to create data volume template from volume[uuid:%s] on all backup storage%s. See cause for one of errors",
msg.getVolumeUuid(), msg.getBackupStorageUuids()).causedBy(err);
trigger.fail(errCode);
} else {
image = dbf.reload(image);
if (format != null) {
image.setFormat(format);
}
image.setMd5Sum(mdsum);
image.setStatus(ImageStatus.Ready);
image = dbf.updateAndRefresh(image);
trigger.next();
}
}
});
}
});
done(new FlowDoneHandler(msg) {
@Override
public void handle(Map data) {
evt.setInventory(ImageInventory.valueOf(image));
bus.publish(evt);
}
});
error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
evt.setError(errCode);
bus.publish(evt);
}
});
}
}).start();
}
private void handle(final APICreateRootVolumeTemplateFromVolumeSnapshotMsg msg) {
final APICreateRootVolumeTemplateFromVolumeSnapshotEvent evt = new APICreateRootVolumeTemplateFromVolumeSnapshotEvent(msg.getId());
SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class);
q.select(VolumeSnapshotVO_.format);
q.add(VolumeSnapshotVO_.uuid, Op.EQ, msg.getSnapshotUuid());
String format = q.findValue();
final ImageVO vo = new ImageVO();
if (msg.getResourceUuid() != null) {
vo.setUuid(msg.getResourceUuid());
} else {
vo.setUuid(Platform.getUuid());
}
vo.setName(msg.getName());
vo.setSystem(msg.isSystem());
vo.setDescription(msg.getDescription());
vo.setPlatform(ImagePlatform.valueOf(msg.getPlatform()));
vo.setGuestOsType(vo.getGuestOsType());
vo.setStatus(ImageStatus.Creating);
vo.setState(ImageState.Enabled);
vo.setFormat(format);
vo.setMediaType(ImageMediaType.RootVolumeTemplate);
vo.setType(ImageConstant.ZSTACK_IMAGE_TYPE);
vo.setUrl(String.format("volumeSnapshot://%s", msg.getSnapshotUuid()));
dbf.persist(vo);
acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), vo.getUuid(), ImageVO.class);
tagMgr.createTagsFromAPICreateMessage(msg, vo.getUuid(), ImageVO.class.getSimpleName());
SimpleQuery<VolumeSnapshotVO> sq = dbf.createQuery(VolumeSnapshotVO.class);
sq.select(VolumeSnapshotVO_.volumeUuid, VolumeSnapshotVO_.treeUuid);
sq.add(VolumeSnapshotVO_.uuid, Op.EQ, msg.getSnapshotUuid());
Tuple t = sq.findTuple();
String volumeUuid = t.get(0, String.class);
String treeUuid = t.get(1, String.class);
List<CreateTemplateFromVolumeSnapshotMsg> cmsgs = msg.getBackupStorageUuids().stream().map(bsUuid -> {
CreateTemplateFromVolumeSnapshotMsg cmsg = new CreateTemplateFromVolumeSnapshotMsg();
cmsg.setSnapshotUuid(msg.getSnapshotUuid());
cmsg.setImageUuid(vo.getUuid());
cmsg.setVolumeUuid(volumeUuid);
cmsg.setTreeUuid(treeUuid);
cmsg.setBackupStorageUuid(bsUuid);
String resourceUuid = volumeUuid != null ? volumeUuid : treeUuid;
bus.makeTargetServiceIdByResourceUuid(cmsg, VolumeSnapshotConstant.SERVICE_ID, resourceUuid);
return cmsg;
}).collect(Collectors.toList());
List<Failure> failures = new ArrayList<>();
AsyncLatch latch = new AsyncLatch(cmsgs.size(), new NoErrorCompletion(msg) {
@Override
public void done() {
if (failures.size() == cmsgs.size()) {
// failed on all
ErrorCodeList error = errf.stringToOperationError(String.format("failed to create template from" +
" the volume snapshot[uuid:%s] on backup storage[uuids:%s]", msg.getSnapshotUuid(),
msg.getBackupStorageUuids()), failures.stream().map(f -> f.error).collect(Collectors.toList()));
evt.setError(error);
dbf.remove(vo);
} else {
ImageVO imvo = dbf.reload(vo);
evt.setInventory(ImageInventory.valueOf(imvo));
logger.debug(String.format("successfully created image[uuid:%s, name:%s] from volume snapshot[uuid:%s]",
imvo.getUuid(), imvo.getName(), msg.getSnapshotUuid()));
}
if (!failures.isEmpty()) {
evt.setFailuresOnBackupStorage(failures);
}
bus.publish(evt);
}
});
RunOnce once = new RunOnce();
for (CreateTemplateFromVolumeSnapshotMsg cmsg : cmsgs) {
bus.send(cmsg, new CloudBusCallBack(latch) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
synchronized (failures) {
Failure failure = new Failure();
failure.error = reply.getError();
failure.backupStorageUuid = cmsg.getBackupStorageUuid();
failures.add(failure);
}
} else {
CreateTemplateFromVolumeSnapshotReply cr = reply.castReply();
ImageBackupStorageRefVO ref = new ImageBackupStorageRefVO();
ref.setBackupStorageUuid(cr.getBackupStorageUuid());
ref.setInstallPath(cr.getBackupStorageInstallPath());
ref.setStatus(ImageStatus.Ready);
ref.setImageUuid(vo.getUuid());
dbf.persist(ref);
once.run(() -> {
vo.setSize(cr.getSize());
vo.setActualSize(cr.getActualSize());
vo.setStatus(ImageStatus.Ready);
dbf.update(vo);
});
}
latch.ack();
}
});
}
}
private void passThrough(ImageMessage msg) {
ImageVO vo = dbf.findByUuid(msg.getImageUuid(), ImageVO.class);
if (vo == null && allowedMessageAfterDeletion.contains(msg.getClass())) {
ImageEO eo = dbf.findByUuid(msg.getImageUuid(), ImageEO.class);
vo = ObjectUtils.newAndCopy(eo, ImageVO.class);
}
if (vo == null) {
String err = String.format("Cannot find image[uuid:%s], it may have been deleted", msg.getImageUuid());
logger.warn(err);
bus.replyErrorByMessageType((Message) msg, errf.instantiateErrorCode(SysErrors.RESOURCE_NOT_FOUND, err));
return;
}
ImageFactory factory = getImageFacotry(ImageType.valueOf(vo.getType()));
Image img = factory.getImage(vo);
img.handleMessage((Message) msg);
}
private void handle(final APICreateRootVolumeTemplateFromRootVolumeMsg msg) {
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("create-template-from-root-volume-%s", msg.getRootVolumeUuid()));
chain.then(new ShareFlow() {
ImageVO imageVO;
VolumeInventory rootVolume;
Long imageActualSize;
List<BackupStorageInventory> targetBackupStorages = new ArrayList<>();
String zoneUuid;
{
VolumeVO rootvo = dbf.findByUuid(msg.getRootVolumeUuid(), VolumeVO.class);
rootVolume = VolumeInventory.valueOf(rootvo);
SimpleQuery<PrimaryStorageVO> q = dbf.createQuery(PrimaryStorageVO.class);
q.select(PrimaryStorageVO_.zoneUuid);
q.add(PrimaryStorageVO_.uuid, Op.EQ, rootVolume.getPrimaryStorageUuid());
zoneUuid = q.findValue();
}
@Override
public void setup() {
flow(new NoRollbackFlow() {
String __name__ = "get-volume-actual-size";
@Override
public void run(final FlowTrigger trigger, Map data) {
SyncVolumeSizeMsg msg = new SyncVolumeSizeMsg();
msg.setVolumeUuid(rootVolume.getUuid());
bus.makeTargetServiceIdByResourceUuid(msg, VolumeConstant.SERVICE_ID, rootVolume.getPrimaryStorageUuid());
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
return;
}
SyncVolumeSizeReply sr = reply.castReply();
imageActualSize = sr.getActualSize();
trigger.next();
}
});
}
});
flow(new Flow() {
String __name__ = "create-image-in-database";
public void run(FlowTrigger trigger, Map data) {
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.add(VolumeVO_.uuid, Op.EQ, msg.getRootVolumeUuid());
final VolumeVO volvo = q.find();
String accountUuid = acntMgr.getOwnerAccountUuidOfResource(volvo.getUuid());
final ImageVO imvo = new ImageVO();
if (msg.getResourceUuid() != null) {
imvo.setUuid(msg.getResourceUuid());
} else {
imvo.setUuid(Platform.getUuid());
}
imvo.setDescription(msg.getDescription());
imvo.setMediaType(ImageMediaType.RootVolumeTemplate);
imvo.setState(ImageState.Enabled);
imvo.setGuestOsType(msg.getGuestOsType());
imvo.setFormat(volvo.getFormat());
imvo.setName(msg.getName());
imvo.setSystem(msg.isSystem());
imvo.setPlatform(ImagePlatform.valueOf(msg.getPlatform()));
imvo.setStatus(ImageStatus.Downloading);
imvo.setType(ImageConstant.ZSTACK_IMAGE_TYPE);
imvo.setUrl(String.format("volume://%s", msg.getRootVolumeUuid()));
imvo.setSize(volvo.getSize());
imvo.setActualSize(imageActualSize);
dbf.persist(imvo);
acntMgr.createAccountResourceRef(accountUuid, imvo.getUuid(), ImageVO.class);
tagMgr.createTagsFromAPICreateMessage(msg, imvo.getUuid(), ImageVO.class.getSimpleName());
imageVO = imvo;
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (imageVO != null) {
dbf.remove(imageVO);
}
trigger.rollback();
}
});
flow(new Flow() {
String __name__ = String.format("select-backup-storage");
@Override
public void run(final FlowTrigger trigger, Map data) {
if (msg.getBackupStorageUuids() == null) {
AllocateBackupStorageMsg abmsg = new AllocateBackupStorageMsg();
abmsg.setRequiredZoneUuid(zoneUuid);
abmsg.setSize(imageActualSize);
bus.makeLocalServiceId(abmsg, BackupStorageConstant.SERVICE_ID);
bus.send(abmsg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (reply.isSuccess()) {
targetBackupStorages.add(((AllocateBackupStorageReply) reply).getInventory());
trigger.next();
} else {
trigger.fail(reply.getError());
}
}
});
} else {
List<AllocateBackupStorageMsg> amsgs = CollectionUtils.transformToList(msg.getBackupStorageUuids(), new Function<AllocateBackupStorageMsg, String>() {
@Override
public AllocateBackupStorageMsg call(String arg) {
AllocateBackupStorageMsg abmsg = new AllocateBackupStorageMsg();
abmsg.setSize(imageActualSize);
abmsg.setBackupStorageUuid(arg);
bus.makeLocalServiceId(abmsg, BackupStorageConstant.SERVICE_ID);
return abmsg;
}
});
bus.send(amsgs, new CloudBusListCallBack(trigger) {
@Override
public void run(List<MessageReply> replies) {
List<ErrorCode> errs = new ArrayList<>();
for (MessageReply r : replies) {
if (r.isSuccess()) {
targetBackupStorages.add(((AllocateBackupStorageReply) r).getInventory());
} else {
errs.add(r.getError());
}
}
if (targetBackupStorages.isEmpty()) {
trigger.fail(operr("unable to allocate backup storage specified by uuids%s, list errors are: %s",
msg.getBackupStorageUuids(), JSONObjectUtil.toJsonString(errs)));
} else {
trigger.next();
}
}
});
}
}
@Override
public void rollback(final FlowRollback trigger, Map data) {
if (targetBackupStorages.isEmpty()) {
trigger.rollback();
return;
}
List<ReturnBackupStorageMsg> rmsgs = CollectionUtils.transformToList(targetBackupStorages, new Function<ReturnBackupStorageMsg, BackupStorageInventory>() {
@Override
public ReturnBackupStorageMsg call(BackupStorageInventory arg) {
ReturnBackupStorageMsg rmsg = new ReturnBackupStorageMsg();
rmsg.setBackupStorageUuid(arg.getUuid());
rmsg.setSize(imageActualSize);
bus.makeLocalServiceId(rmsg, BackupStorageConstant.SERVICE_ID);
return rmsg;
}
});
bus.send(rmsgs, new CloudBusListCallBack(trigger) {
@Override
public void run(List<MessageReply> replies) {
for (MessageReply r : replies) {
if (!r.isSuccess()) {
BackupStorageInventory bs = targetBackupStorages.get(replies.indexOf(r));
logger.warn(String.format("failed to return capacity[%s] to backup storage[uuid:%s], because %s",
imageActualSize, bs.getUuid(), r.getError()));
}
}
trigger.rollback();
}
});
}
});
flow(new NoRollbackFlow() {
String __name__ = String.format("start-creating-template");
@Override
public void run(final FlowTrigger trigger, Map data) {
List<CreateTemplateFromVmRootVolumeMsg> cmsgs = CollectionUtils.transformToList(targetBackupStorages, new Function<CreateTemplateFromVmRootVolumeMsg, BackupStorageInventory>() {
@Override
public CreateTemplateFromVmRootVolumeMsg call(BackupStorageInventory arg) {
CreateTemplateFromVmRootVolumeMsg cmsg = new CreateTemplateFromVmRootVolumeMsg();
cmsg.setRootVolumeInventory(rootVolume);
cmsg.setBackupStorageUuid(arg.getUuid());
cmsg.setImageInventory(ImageInventory.valueOf(imageVO));
bus.makeTargetServiceIdByResourceUuid(cmsg, VmInstanceConstant.SERVICE_ID, rootVolume.getVmInstanceUuid());
return cmsg;
}
});
bus.send(cmsgs, new CloudBusListCallBack(trigger) {
@Override
public void run(List<MessageReply> replies) {
boolean success = false;
ErrorCode err = null;
for (MessageReply r : replies) {
BackupStorageInventory bs = targetBackupStorages.get(replies.indexOf(r));
if (!r.isSuccess()) {
logger.warn(String.format("failed to create image from root volume[uuid:%s] on backup storage[uuid:%s], because %s",
msg.getRootVolumeUuid(), bs.getUuid(), r.getError()));
err = r.getError();
continue;
}
CreateTemplateFromVmRootVolumeReply reply = (CreateTemplateFromVmRootVolumeReply) r;
ImageBackupStorageRefVO ref = new ImageBackupStorageRefVO();
ref.setBackupStorageUuid(bs.getUuid());
ref.setStatus(ImageStatus.Ready);
ref.setImageUuid(imageVO.getUuid());
ref.setInstallPath(reply.getInstallPath());
dbf.persist(ref);
imageVO.setStatus(ImageStatus.Ready);
if (reply.getFormat() != null) {
imageVO.setFormat(reply.getFormat());
}
dbf.update(imageVO);
imageVO = dbf.reload(imageVO);
success = true;
logger.debug(String.format("successfully created image[uuid:%s] from root volume[uuid:%s] on backup storage[uuid:%s]",
imageVO.getUuid(), msg.getRootVolumeUuid(), bs.getUuid()));
}
if (success) {
trigger.next();
} else {
trigger.fail(operr("failed to create image from root volume[uuid:%s] on all backup storage, see cause for one of errors",
msg.getRootVolumeUuid()).causedBy(err));
}
}
});
}
});
flow(new Flow() {
String __name__ = "copy-system-tag-to-image";
public void run(FlowTrigger trigger, Map data) {
// find the rootimage and create some systemtag if it has
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.add(VolumeVO_.uuid, SimpleQuery.Op.EQ, msg.getRootVolumeUuid());
q.select(VolumeVO_.vmInstanceUuid);
String vmInstanceUuid = q.findValue();
if (tagMgr.hasSystemTag(vmInstanceUuid, ImageSystemTags.IMAGE_INJECT_QEMUGA.getTagFormat())) {
tagMgr.createNonInherentSystemTag(imageVO.getUuid(),
ImageSystemTags.IMAGE_INJECT_QEMUGA.getTagFormat(),
ImageVO.class.getSimpleName());
}
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
trigger.rollback();
}
});
done(new FlowDoneHandler(msg) {
@Override
public void handle(Map data) {
APICreateRootVolumeTemplateFromRootVolumeEvent evt = new APICreateRootVolumeTemplateFromRootVolumeEvent(msg.getId());
imageVO = dbf.reload(imageVO);
ImageInventory iinv = ImageInventory.valueOf(imageVO);
evt.setInventory(iinv);
logger.warn(String.format("successfully create template[uuid:%s] from root volume[uuid:%s]", iinv.getUuid(), msg.getRootVolumeUuid()));
bus.publish(evt);
}
});
error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
APICreateRootVolumeTemplateFromRootVolumeEvent evt = new APICreateRootVolumeTemplateFromRootVolumeEvent(msg.getId());
evt.setError(errCode);
logger.warn(String.format("failed to create template from root volume[uuid:%s], because %s", msg.getRootVolumeUuid(), errCode));
bus.publish(evt);
}
});
}
}).start();
}
private void handle(APIGetImageMsg msg) {
SearchQuery<ImageInventory> sq = new SearchQuery(ImageInventory.class);
sq.addAccountAsAnd(msg);
sq.add("uuid", SearchOp.AND_EQ, msg.getUuid());
List<ImageInventory> invs = sq.list();
APIGetImageReply reply = new APIGetImageReply();
if (!invs.isEmpty()) {
reply.setInventory(JSONObjectUtil.toJsonString(invs.get(0)));
}
bus.reply(msg, reply);
}
private void handle(APISearchImageMsg msg) {
SearchQuery<ImageInventory> sq = SearchQuery.create(msg, ImageInventory.class);
sq.addAccountAsAnd(msg);
String content = sq.listAsString();
APISearchImageReply reply = new APISearchImageReply();
reply.setContent(content);
bus.reply(msg, reply);
}
private void handle(APIListImageMsg msg) {
List<ImageVO> vos = dbf.listAll(ImageVO.class);
List<ImageInventory> invs = ImageInventory.valueOf(vos);
APIListImageReply reply = new APIListImageReply();
reply.setInventories(invs);
bus.reply(msg, reply);
}
@Deferred
private void handle(final APIAddImageMsg msg) {
String imageType = msg.getType();
imageType = imageType == null ? DefaultImageFactory.type.toString() : imageType;
final APIAddImageEvent evt = new APIAddImageEvent(msg.getId());
ImageVO vo = new ImageVO();
if (msg.getResourceUuid() != null) {
vo.setUuid(msg.getResourceUuid());
} else {
vo.setUuid(Platform.getUuid());
}
vo.setName(msg.getName());
vo.setDescription(msg.getDescription());
if (msg.getFormat().equals(ImageConstant.ISO_FORMAT_STRING)) {
vo.setMediaType(ImageMediaType.ISO);
} else {
vo.setMediaType(ImageMediaType.valueOf(msg.getMediaType()));
}
vo.setType(imageType);
vo.setSystem(msg.isSystem());
vo.setGuestOsType(msg.getGuestOsType());
vo.setFormat(msg.getFormat());
vo.setStatus(ImageStatus.Downloading);
vo.setState(ImageState.Enabled);
vo.setUrl(msg.getUrl());
vo.setDescription(msg.getDescription());
vo.setPlatform(ImagePlatform.valueOf(msg.getPlatform()));
ImageFactory factory = getImageFacotry(ImageType.valueOf(imageType));
final ImageVO ivo = new SQLBatchWithReturn<ImageVO>() {
@Override
protected ImageVO scripts() {
final ImageVO ivo = factory.createImage(vo, msg);
acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), vo.getUuid(), ImageVO.class);
tagMgr.createTagsFromAPICreateMessage(msg, vo.getUuid(), ImageVO.class.getSimpleName());
return ivo;
}
}.execute();
Defer.guard(() -> dbf.remove(ivo));
final ImageInventory inv = ImageInventory.valueOf(ivo);
for (AddImageExtensionPoint ext : pluginRgty.getExtensionList(AddImageExtensionPoint.class)) {
ext.preAddImage(inv);
}
final List<DownloadImageMsg> dmsgs = CollectionUtils.transformToList(msg.getBackupStorageUuids(), new Function<DownloadImageMsg, String>() {
@Override
public DownloadImageMsg call(String arg) {
DownloadImageMsg dmsg = new DownloadImageMsg(inv);
dmsg.setBackupStorageUuid(arg);
dmsg.setFormat(msg.getFormat());
dmsg.setSystemTags(msg.getSystemTags());
bus.makeTargetServiceIdByResourceUuid(dmsg, BackupStorageConstant.SERVICE_ID, arg);
return dmsg;
}
});
CollectionUtils.safeForEach(pluginRgty.getExtensionList(AddImageExtensionPoint.class), new ForEachFunction<AddImageExtensionPoint>() {
@Override
public void run(AddImageExtensionPoint ext) {
ext.beforeAddImage(inv);
}
});
new LoopAsyncBatch<DownloadImageMsg>(msg) {
AtomicBoolean success = new AtomicBoolean(false);
@Override
protected Collection<DownloadImageMsg> collect() {
return dmsgs;
}
@Override
protected AsyncBatchRunner forEach(DownloadImageMsg dmsg) {
return new AsyncBatchRunner() {
@Override
public void run(NoErrorCompletion completion) {
ImageBackupStorageRefVO ref = new ImageBackupStorageRefVO();
ref.setImageUuid(ivo.getUuid());
ref.setInstallPath("");
ref.setBackupStorageUuid(dmsg.getBackupStorageUuid());
ref.setStatus(ImageStatus.Downloading);
dbf.persist(ref);
bus.send(dmsg, new CloudBusCallBack(completion) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
errors.add(reply.getError());
dbf.remove(ref);
} else {
DownloadImageReply re = reply.castReply();
ref.setStatus(ImageStatus.Ready);
ref.setInstallPath(re.getInstallPath());
if (dbf.reload(ref) == null) {
logger.debug(String.format("image[uuid: %s] has been deleted", ref.getImageUuid()));
completion.done();
return;
}
dbf.update(ref);
if (success.compareAndSet(false, true)) {
// In case 'Platform' etc. is changed.
ImageVO vo = dbf.reload(ivo);
vo.setMd5Sum(re.getMd5sum());
vo.setSize(re.getSize());
vo.setActualSize(re.getActualSize());
vo.setStatus(ImageStatus.Ready);
dbf.update(vo);
}
logger.debug(String.format("successfully downloaded image[uuid:%s, name:%s] to backup storage[uuid:%s]",
inv.getUuid(), inv.getName(), dmsg.getBackupStorageUuid()));
}
completion.done();
}
});
}
};
}
@Override
protected void done() {
// check if the database still has the record of the image
// if there is no record, that means user delete the image during the downloading,
// then we need to cleanup
ImageVO vo = dbf.reload(ivo);
if (vo == null) {
evt.setError(operr("image [uuid:%s] has been deleted", ivo.getUuid()));
SQL.New("delete from ImageBackupStorageRefVO where imageUuid = :uuid")
.param("uuid", ivo.getUuid())
.execute();
bus.publish(evt);
return;
}
if (success.get()) {
final ImageInventory einv = ImageInventory.valueOf(vo);
CollectionUtils.safeForEach(pluginRgty.getExtensionList(AddImageExtensionPoint.class), new ForEachFunction<AddImageExtensionPoint>() {
@Override
public void run(AddImageExtensionPoint ext) {
ext.afterAddImage(einv);
}
});
evt.setInventory(einv);
} else {
final ErrorCode err = errf.instantiateErrorCode(SysErrors.CREATE_RESOURCE_ERROR, String.format("Failed to download image[name:%s] on all backup storage%s.",
inv.getName(), msg.getBackupStorageUuids()), errors);
CollectionUtils.safeForEach(pluginRgty.getExtensionList(AddImageExtensionPoint.class), new ForEachFunction<AddImageExtensionPoint>() {
@Override
public void run(AddImageExtensionPoint ext) {
ext.failedToAddImage(inv, err);
}
});
dbf.remove(ivo);
evt.setError(err);
}
bus.publish(evt);
}
}.start();
}
@Override
public String getId() {
return bus.makeLocalServiceId(ImageConstant.SERVICE_ID);
}
private void populateExtensions() {
for (ImageFactory f : pluginRgty.getExtensionList(ImageFactory.class)) {
ImageFactory old = imageFactories.get(f.getType().toString());
if (old != null) {
throw new CloudRuntimeException(String.format("duplicate ImageFactory[%s, %s] for type[%s]",
f.getClass().getName(), old.getClass().getName(), f.getType()));
}
imageFactories.put(f.getType().toString(), f);
}
}
@Override
public boolean start() {
populateExtensions();
installGlobalConfigUpdater();
return true;
}
private void installGlobalConfigUpdater() {
ImageGlobalConfig.DELETION_POLICY.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() {
@Override
public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) {
startExpungeTask();
}
});
ImageGlobalConfig.EXPUNGE_INTERVAL.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() {
@Override
public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) {
startExpungeTask();
}
});
ImageGlobalConfig.EXPUNGE_PERIOD.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() {
@Override
public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) {
startExpungeTask();
}
});
}
private void startExpungeTask() {
if (expungeTask != null) {
expungeTask.cancel(true);
}
expungeTask = thdf.submitCancelablePeriodicTask(new CancelablePeriodicTask() {
private List<Tuple> getDeletedImageManagedByUs() {
int qun = 1000;
SimpleQuery q = dbf.createQuery(ImageBackupStorageRefVO.class);
q.add(ImageBackupStorageRefVO_.status, Op.EQ, ImageStatus.Deleted);
long amount = q.count();
int times = (int) (amount / qun) + (amount % qun != 0 ? 1 : 0);
int start = 0;
List<Tuple> ret = new ArrayList<Tuple>();
for (int i = 0; i < times; i++) {
q = dbf.createQuery(ImageBackupStorageRefVO.class);
q.select(ImageBackupStorageRefVO_.imageUuid, ImageBackupStorageRefVO_.lastOpDate, ImageBackupStorageRefVO_.backupStorageUuid);
q.add(ImageBackupStorageRefVO_.status, Op.EQ, ImageStatus.Deleted);
q.setLimit(qun);
q.setStart(start);
List<Tuple> ts = q.listTuple();
start += qun;
for (Tuple t : ts) {
String imageUuid = t.get(0, String.class);
if (!destMaker.isManagedByUs(imageUuid)) {
continue;
}
ret.add(t);
}
}
return ret;
}
@Override
public boolean run() {
final List<Tuple> images = getDeletedImageManagedByUs();
if (images.isEmpty()) {
logger.debug("[Image Expunge Task]: no images to expunge");
return false;
}
for (Tuple t : images) {
String imageUuid = t.get(0, String.class);
Timestamp date = t.get(1, Timestamp.class);
String bsUuid = t.get(2, String.class);
final Timestamp current = dbf.getCurrentSqlTime();
if (current.getTime() >= date.getTime() + TimeUnit.SECONDS.toMillis(ImageGlobalConfig.EXPUNGE_PERIOD.value(Long.class))) {
ImageDeletionPolicy deletionPolicy = deletionPolicyMgr.getDeletionPolicy(imageUuid);
if (ImageDeletionPolicy.Never == deletionPolicy) {
logger.debug(String.format("the deletion policy[Never] is set for the image[uuid:%s] on the backup storage[uuid:%s]," +
"don't expunge it", images, bsUuid));
continue;
}
ExpungeImageMsg msg = new ExpungeImageMsg();
msg.setImageUuid(imageUuid);
msg.setBackupStorageUuid(bsUuid);
bus.makeTargetServiceIdByResourceUuid(msg, ImageConstant.SERVICE_ID, imageUuid);
bus.send(msg, new CloudBusCallBack(null) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
N.New(ImageVO.class, imageUuid).warn_("failed to expunge the image[uuid:%s] on the backup storage[uuid:%s], will try it later. %s",
imageUuid, bsUuid, reply.getError());
}
}
});
}
}
return false;
}
@Override
public TimeUnit getTimeUnit() {
return TimeUnit.SECONDS;
}
@Override
public long getInterval() {
return ImageGlobalConfig.EXPUNGE_INTERVAL.value(Long.class);
}
@Override
public String getName() {
return "expunge-image";
}
});
}
@Override
public boolean stop() {
return true;
}
private ImageFactory getImageFacotry(ImageType type) {
ImageFactory factory = imageFactories.get(type.toString());
if (factory == null) {
throw new CloudRuntimeException(String.format("Unable to find ImageFactory with type[%s]", type));
}
return factory;
}
@Override
public void managementNodeReady() {
startExpungeTask();
}
@Override
public List<Quota> reportQuota() {
Quota.QuotaOperator checker = new Quota.QuotaOperator() {
@Override
public void checkQuota(APIMessage msg, Map<String, Quota.QuotaPair> pairs) {
if (!new QuotaUtil().isAdminAccount(msg.getSession().getAccountUuid())) {
if (msg instanceof APIAddImageMsg) {
check((APIAddImageMsg) msg, pairs);
} else if (msg instanceof APIRecoverImageMsg) {
check((APIRecoverImageMsg) msg, pairs);
} else if (msg instanceof APIChangeResourceOwnerMsg) {
check((APIChangeResourceOwnerMsg) msg, pairs);
}
} else {
if (msg instanceof APIChangeResourceOwnerMsg) {
check((APIChangeResourceOwnerMsg) msg, pairs);
}
}
}
@Override
public void checkQuota(NeedQuotaCheckMessage msg, Map<String, Quota.QuotaPair> pairs) {
}
@Override
public List<Quota.QuotaUsage> getQuotaUsageByAccount(String accountUuid) {
List<Quota.QuotaUsage> usages = new ArrayList<>();
ImageQuotaUtil.ImageQuota imageQuota = new ImageQuotaUtil().getUsed(accountUuid);
Quota.QuotaUsage usage = new Quota.QuotaUsage();
usage.setName(ImageConstant.QUOTA_IMAGE_NUM);
usage.setUsed(imageQuota.imageNum);
usages.add(usage);
usage = new Quota.QuotaUsage();
usage.setName(ImageConstant.QUOTA_IMAGE_SIZE);
usage.setUsed(imageQuota.imageSize);
usages.add(usage);
return usages;
}
@Transactional(readOnly = true)
private void check(APIChangeResourceOwnerMsg msg, Map<String, Quota.QuotaPair> pairs) {
String currentAccountUuid = msg.getSession().getAccountUuid();
String resourceTargetOwnerAccountUuid = msg.getAccountUuid();
if (new QuotaUtil().isAdminAccount(resourceTargetOwnerAccountUuid)) {
return;
}
SimpleQuery<AccountResourceRefVO> q = dbf.createQuery(AccountResourceRefVO.class);
q.add(AccountResourceRefVO_.resourceUuid, Op.EQ, msg.getResourceUuid());
AccountResourceRefVO accResRefVO = q.find();
if (accResRefVO.getResourceType().equals(ImageVO.class.getSimpleName())) {
long imageNumQuota = pairs.get(ImageConstant.QUOTA_IMAGE_NUM).getValue();
long imageSizeQuota = pairs.get(ImageConstant.QUOTA_IMAGE_SIZE).getValue();
long imageNumUsed = new ImageQuotaUtil().getUsedImageNum(resourceTargetOwnerAccountUuid);
long imageSizeUsed = new ImageQuotaUtil().getUsedImageSize(resourceTargetOwnerAccountUuid);
ImageVO image = dbf.getEntityManager().find(ImageVO.class, msg.getResourceUuid());
long imageNumAsked = 1;
long imageSizeAsked = image.getSize();
QuotaUtil.QuotaCompareInfo quotaCompareInfo;
{
quotaCompareInfo = new QuotaUtil.QuotaCompareInfo();
quotaCompareInfo.currentAccountUuid = currentAccountUuid;
quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid;
quotaCompareInfo.quotaName = ImageConstant.QUOTA_IMAGE_NUM;
quotaCompareInfo.quotaValue = imageNumQuota;
quotaCompareInfo.currentUsed = imageNumUsed;
quotaCompareInfo.request = imageNumAsked;
new QuotaUtil().CheckQuota(quotaCompareInfo);
}
{
quotaCompareInfo = new QuotaUtil.QuotaCompareInfo();
quotaCompareInfo.currentAccountUuid = currentAccountUuid;
quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid;
quotaCompareInfo.quotaName = ImageConstant.QUOTA_IMAGE_SIZE;
quotaCompareInfo.quotaValue = imageSizeQuota;
quotaCompareInfo.currentUsed = imageSizeUsed;
quotaCompareInfo.request = imageSizeAsked;
new QuotaUtil().CheckQuota(quotaCompareInfo);
}
}
}
@Transactional(readOnly = true)
private void check(APIRecoverImageMsg msg, Map<String, Quota.QuotaPair> pairs) {
String currentAccountUuid = msg.getSession().getAccountUuid();
String resourceTargetOwnerAccountUuid = new QuotaUtil().getResourceOwnerAccountUuid(msg.getImageUuid());
long imageNumQuota = pairs.get(ImageConstant.QUOTA_IMAGE_NUM).getValue();
long imageSizeQuota = pairs.get(ImageConstant.QUOTA_IMAGE_SIZE).getValue();
long imageNumUsed = new ImageQuotaUtil().getUsedImageNum(resourceTargetOwnerAccountUuid);
long imageSizeUsed = new ImageQuotaUtil().getUsedImageSize(resourceTargetOwnerAccountUuid);
ImageVO image = dbf.getEntityManager().find(ImageVO.class, msg.getImageUuid());
long imageNumAsked = 1;
long imageSizeAsked = image.getSize();
QuotaUtil.QuotaCompareInfo quotaCompareInfo;
{
quotaCompareInfo = new QuotaUtil.QuotaCompareInfo();
quotaCompareInfo.currentAccountUuid = currentAccountUuid;
quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid;
quotaCompareInfo.quotaName = ImageConstant.QUOTA_IMAGE_NUM;
quotaCompareInfo.quotaValue = imageNumQuota;
quotaCompareInfo.currentUsed = imageNumUsed;
quotaCompareInfo.request = imageNumAsked;
new QuotaUtil().CheckQuota(quotaCompareInfo);
}
{
quotaCompareInfo = new QuotaUtil.QuotaCompareInfo();
quotaCompareInfo.currentAccountUuid = currentAccountUuid;
quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid;
quotaCompareInfo.quotaName = ImageConstant.QUOTA_IMAGE_SIZE;
quotaCompareInfo.quotaValue = imageSizeQuota;
quotaCompareInfo.currentUsed = imageSizeUsed;
quotaCompareInfo.request = imageSizeAsked;
new QuotaUtil().CheckQuota(quotaCompareInfo);
}
}
@Transactional(readOnly = true)
private void check(APIAddImageMsg msg, Map<String, Quota.QuotaPair> pairs) {
String currentAccountUuid = msg.getSession().getAccountUuid();
String resourceTargetOwnerAccountUuid = msg.getSession().getAccountUuid();
long imageNumQuota = pairs.get(ImageConstant.QUOTA_IMAGE_NUM).getValue();
long imageNumUsed = new ImageQuotaUtil().getUsedImageNum(resourceTargetOwnerAccountUuid);
long imageNumAsked = 1;
QuotaUtil.QuotaCompareInfo quotaCompareInfo;
{
quotaCompareInfo = new QuotaUtil.QuotaCompareInfo();
quotaCompareInfo.currentAccountUuid = currentAccountUuid;
quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid;
quotaCompareInfo.quotaName = ImageConstant.QUOTA_IMAGE_NUM;
quotaCompareInfo.quotaValue = imageNumQuota;
quotaCompareInfo.currentUsed = imageNumUsed;
quotaCompareInfo.request = imageNumAsked;
new QuotaUtil().CheckQuota(quotaCompareInfo);
}
new ImageQuotaUtil().checkImageSizeQuotaUseHttpHead(msg, pairs);
}
};
Quota quota = new Quota();
quota.setOperator(checker);
quota.addMessageNeedValidation(APIAddImageMsg.class);
quota.addMessageNeedValidation(APIRecoverImageMsg.class);
quota.addMessageNeedValidation(APIChangeResourceOwnerMsg.class);
Quota.QuotaPair p = new Quota.QuotaPair();
p.setName(ImageConstant.QUOTA_IMAGE_NUM);
p.setValue(20);
quota.addPair(p);
p = new Quota.QuotaPair();
p.setName(ImageConstant.QUOTA_IMAGE_SIZE);
p.setValue(SizeUnit.TERABYTE.toByte(10));
quota.addPair(p);
return list(quota);
}
@Override
@Transactional(readOnly = true)
public void resourceOwnerPreChange(AccountResourceRefInventory ref, String newOwnerUuid) {
}
}