package org.zstack.storage.snapshot; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.Platform; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.cloudbus.MessageSafe; import org.zstack.core.cloudbus.ReplyMessagePreSendingExtensionPoint; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SQLBatchWithReturn; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.scheduler.SchedulerFacade; 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.scheduler.SchedulerInventory; import org.zstack.header.core.scheduler.SchedulerVO; import org.zstack.header.core.workflow.*; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.errorcode.SysErrors; import org.zstack.header.identity.*; import org.zstack.header.message.*; import org.zstack.header.storage.primary.*; import org.zstack.header.storage.primary.VolumeSnapshotCapability.VolumeSnapshotArrangementType; import org.zstack.header.storage.snapshot.*; import org.zstack.header.vm.AfterReimageVmInstanceExtensionPoint; import org.zstack.header.vm.VmInstanceVO; import org.zstack.header.volume.*; import org.zstack.identity.AccountManager; import org.zstack.identity.QuotaUtil; import org.zstack.storage.primary.PrimaryStorageCapacityUpdater; import org.zstack.utils.DebugUtils; import org.zstack.utils.ExceptionDSL; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.operr; import javax.persistence.Query; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.sql.Time; import java.sql.Timestamp; import java.util.*; import static org.zstack.utils.CollectionDSL.list; /** */ public class VolumeSnapshotManagerImpl extends AbstractService implements VolumeSnapshotManager, ReplyMessagePreSendingExtensionPoint, VolumeBeforeExpungeExtensionPoint, ResourceOwnerAfterChangeExtensionPoint, ReportQuotaExtensionPoint, AfterReimageVmInstanceExtensionPoint { private static final CLogger logger = Utils.getLogger(VolumeSnapshotManagerImpl.class); private String syncSignature; @Autowired private ThreadFacade thdf; @Autowired private CloudBus bus; @Autowired private DatabaseFacade dbf; @Autowired private AccountManager acntMgr; @Autowired private ErrorFacade errf; @Autowired private PluginRegistry pluginRgty; @Autowired private SchedulerFacade schedulerFacade; private void passThrough(VolumeSnapshotMessage msg) { VolumeSnapshotVO vo = dbf.findByUuid(msg.getSnapshotUuid(), VolumeSnapshotVO.class); if (vo == null) { throw new OperationFailureException(errf.instantiateErrorCode(SysErrors.RESOURCE_NOT_FOUND, String.format("cannot find volume snapshot[uuid:%s]", msg.getSnapshotUuid()) )); } if (msg.getVolumeUuid() != null) { VolumeSnapshotTreeBase tree = new VolumeSnapshotTreeBase(vo, true); tree.handleMessage((Message) msg); } else if (msg.getTreeUuid() != null) { VolumeSnapshotTreeBase tree = new VolumeSnapshotTreeBase(vo, false); tree.handleMessage((Message) msg); } else { VolumeSnapshot snapshot = new VolumeSnapshotBase(vo); snapshot.handleMessage((Message) msg); } } @Override @MessageSafe public void handleMessage(Message msg) { if (msg instanceof VolumeSnapshotMessage) { passThrough((VolumeSnapshotMessage) msg); } else if (msg instanceof APIMessage) { handleApiMessage((APIMessage) msg); } else { handleLocalMessage(msg); } } private void handleLocalMessage(Message msg) { if (msg instanceof CreateVolumeSnapshotMsg) { handle((CreateVolumeSnapshotMsg) msg); } else if (msg instanceof VolumeSnapshotReportPrimaryStorageCapacityUsageMsg) { handle((VolumeSnapshotReportPrimaryStorageCapacityUsageMsg) msg); } else { bus.dealWithUnknownMessage(msg); } } @Transactional(readOnly = true) private void handle(VolumeSnapshotReportPrimaryStorageCapacityUsageMsg msg) { String sql = "select sum(sp.size)" + " from VolumeSnapshotVO sp" + " where sp.type = :sptype" + " and sp.primaryStorageUuid = :prUuid"; TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class); q.setParameter("sptype", VolumeSnapshotConstant.HYPERVISOR_SNAPSHOT_TYPE.toString()); q.setParameter("prUuid", msg.getPrimaryStorageUuid()); Long size = q.getSingleResult(); VolumeSnapshotReportPrimaryStorageCapacityUsageReply reply = new VolumeSnapshotReportPrimaryStorageCapacityUsageReply(); reply.setUsedSize(size == null ? 0 : size); bus.reply(msg, reply); } private void handle(APIGetVolumeSnapshotTreeMsg msg) { APIGetVolumeSnapshotTreeReply reply = new APIGetVolumeSnapshotTreeReply(); if (msg.getTreeUuid() != null) { VolumeSnapshotTreeVO treeVO = dbf.findByUuid(msg.getTreeUuid(), VolumeSnapshotTreeVO.class); if (treeVO == null) { reply.setInventories(new ArrayList<>()); bus.reply(msg, reply); return; } VolumeSnapshotTreeInventory inv = VolumeSnapshotTreeInventory.valueOf(treeVO); SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class); q.add(VolumeSnapshotVO_.treeUuid, Op.EQ, msg.getTreeUuid()); List<VolumeSnapshotVO> vos = q.list(); VolumeSnapshotTree tree = VolumeSnapshotTree.fromVOs(vos); inv.setTree(tree.getRoot().toLeafInventory()); reply.setInventories(Arrays.asList(inv)); } else if (msg.getVolumeUuid() != null) { SimpleQuery<VolumeSnapshotTreeVO> q = dbf.createQuery(VolumeSnapshotTreeVO.class); q.add(VolumeSnapshotTreeVO_.volumeUuid, Op.EQ, msg.getVolumeUuid()); List<VolumeSnapshotTreeVO> trees = q.list(); if (trees.isEmpty()) { reply.setInventories(new ArrayList<>()); bus.reply(msg, reply); return; } List<VolumeSnapshotTreeInventory> treeInventories = new ArrayList<>(); for (VolumeSnapshotTreeVO vo : trees) { VolumeSnapshotTreeInventory inv = VolumeSnapshotTreeInventory.valueOf(vo); SimpleQuery<VolumeSnapshotVO> sq = dbf.createQuery(VolumeSnapshotVO.class); sq.add(VolumeSnapshotVO_.treeUuid, Op.EQ, vo.getUuid()); List<VolumeSnapshotVO> vos = sq.list(); VolumeSnapshotTree tree = VolumeSnapshotTree.fromVOs(vos); inv.setTree(tree.getRoot().toLeafInventory()); treeInventories.add(inv); } reply.setInventories(treeInventories); } bus.reply(msg, reply); } @Transactional private VolumeSnapshotStruct newChain(VolumeSnapshotVO vo, boolean fullsnapshot) { VolumeSnapshotTreeVO chain = new VolumeSnapshotTreeVO(); chain.setCurrent(true); chain.setVolumeUuid(vo.getVolumeUuid()); chain.setUuid(Platform.getUuid()); chain = dbf.getEntityManager().merge(chain); logger.debug(String.format("created new volume snapshot tree[tree uuid:%s, volume uuid:%s, full snapshot uuid:%s]", chain.getUuid(), vo.getVolumeUuid(), vo.getUuid())); vo.setTreeUuid(chain.getUuid()); vo.setDistance(0); vo.setParentUuid(null); vo.setLatest(true); vo.setFullSnapshot(fullsnapshot); vo = dbf.getEntityManager().merge(vo); VolumeSnapshotStruct struct = new VolumeSnapshotStruct(); struct.setCurrent(VolumeSnapshotInventory.valueOf(vo)); struct.setFullSnapshot(fullsnapshot); return struct; } @Transactional private VolumeSnapshotStruct saveChainTypeSnapshot(VolumeSnapshotVO vo) { String sql = "select c" + " from VolumeSnapshotTreeVO c" + " where c.volumeUuid = :volUuid" + " and c.current = true"; TypedQuery<VolumeSnapshotTreeVO> cq = dbf.getEntityManager().createQuery(sql, VolumeSnapshotTreeVO.class); cq.setParameter("volUuid", vo.getVolumeUuid()); List<VolumeSnapshotTreeVO> rets = cq.getResultList(); DebugUtils.Assert(rets.size() < 2, "can not have more than one VolumeSnapshotTreeVO with current=1"); VolumeSnapshotTreeVO chain = rets.isEmpty() ? null : rets.get(0); if (chain == null) { return newChain(vo, false); } else { sql = "select s" + " from VolumeSnapshotVO s" + " where s.latest = true" + " and s.volumeUuid = :volUuid" + " and s.treeUuid = :chainUuid"; TypedQuery<VolumeSnapshotVO> q = dbf.getEntityManager().createQuery(sql, VolumeSnapshotVO.class); q.setParameter("volUuid", vo.getVolumeUuid()); q.setParameter("chainUuid", chain.getUuid()); VolumeSnapshotVO latest = q.getSingleResult(); if (VolumeSnapshotGlobalConfig.MAX_INCREMENTAL_SNAPSHOT_NUM.value(Integer.class) == latest.getDistance()) { chain.setCurrent(false); dbf.getEntityManager().merge(chain); return newChain(vo, true); } latest.setLatest(false); latest = dbf.getEntityManager().merge(latest); vo.setTreeUuid(latest.getTreeUuid()); vo.setLatest(true); vo.setParentUuid(latest.getUuid()); vo.setDistance(latest.getDistance() + 1); vo = dbf.getEntityManager().merge(vo); VolumeSnapshotStruct struct = new VolumeSnapshotStruct(); struct.setParent(VolumeSnapshotInventory.valueOf(latest)); struct.setCurrent(VolumeSnapshotInventory.valueOf(vo)); return struct; } } @Transactional private VolumeSnapshotStruct saveIndividualTypeSnapshot(VolumeSnapshotVO vo) { String sql = "update VolumeSnapshotTreeVO tree" + " set tree.current = false" + " where tree.current = true" + " and tree.volumeUuid = :volUuid"; Query q = dbf.getEntityManager().createQuery(sql); q.setParameter("volUuid", vo.getVolumeUuid()); q.executeUpdate(); return newChain(vo, false); } @Transactional private void rollbackSnapshot(String uuid) { VolumeSnapshotVO vo = dbf.getEntityManager().find(VolumeSnapshotVO.class, uuid); dbf.getEntityManager().remove(vo); String sql = "delete from AccountResourceRefVO where resourceUuid = :vsUuid and resourceType = 'VolumeSnapshotVO'"; Query q = dbf.getEntityManager().createQuery(sql); q.setParameter("vsUuid", uuid); q.executeUpdate(); if (vo.getParentUuid() != null) { VolumeSnapshotVO parent = dbf.getEntityManager().find(VolumeSnapshotVO.class, vo.getParentUuid()); parent.setLatest(true); dbf.getEntityManager().merge(parent); } else { VolumeSnapshotTreeVO chain = dbf.getEntityManager().find(VolumeSnapshotTreeVO.class, vo.getTreeUuid()); dbf.getEntityManager().remove(chain); } } private void handle(final CreateVolumeSnapshotMsg msg) { final CreateVolumeSnapshotReply ret = new CreateVolumeSnapshotReply(); final VolumeVO vol = dbf.findByUuid(msg.getVolumeUuid(), VolumeVO.class); final String primaryStorageUuid = vol.getPrimaryStorageUuid(); AskVolumeSnapshotCapabilityMsg askMsg = new AskVolumeSnapshotCapabilityMsg(); askMsg.setPrimaryStorageUuid(primaryStorageUuid); askMsg.setVolume(VolumeInventory.valueOf(vol)); bus.makeTargetServiceIdByResourceUuid(askMsg, PrimaryStorageConstant.SERVICE_ID, primaryStorageUuid); MessageReply reply = bus.call(askMsg); if (!reply.isSuccess()) { ret.setError(errf.stringToOperationError( String.format("cannot ask primary storage[uuid:%s] for volume snapshot capability", vol.getUuid()), reply.getError())); bus.reply(msg, ret); return; } AskVolumeSnapshotCapabilityReply areply = reply.castReply(); VolumeSnapshotCapability capability = areply.getCapability(); if (!capability.isSupport()) { ret.setError(operr("primary storage[uuid:%s] doesn't support volume snapshot;" + " cannot create snapshot for volume[uuid:%s]", primaryStorageUuid, vol.getUuid())); bus.reply(msg, ret); return; } final VolumeSnapshotVO vo = new VolumeSnapshotVO(); if (msg.getResourceUuid() != null) { vo.setUuid(msg.getResourceUuid()); } else { vo.setUuid(Platform.getUuid()); } vo.setName(msg.getName()); vo.setDescription(msg.getDescription()); vo.setVolumeUuid(msg.getVolumeUuid()); vo.setFormat(vol.getFormat()); vo.setState(VolumeSnapshotState.Enabled); vo.setStatus(VolumeSnapshotStatus.Creating); vo.setVolumeType(vol.getType().toString()); final VolumeSnapshotStruct struct = new SQLBatchWithReturn<VolumeSnapshotStruct>() { @Override protected VolumeSnapshotStruct scripts() { VolumeSnapshotStruct s = null; if (VolumeSnapshotArrangementType.CHAIN == capability.getArrangementType()) { s = saveChainTypeSnapshot(vo); } else if (VolumeSnapshotArrangementType.INDIVIDUAL == capability.getArrangementType()) { s = saveIndividualTypeSnapshot(vo); } else { DebugUtils.Assert(false, "should not be here"); } acntMgr.createAccountResourceRef(msg.getAccountUuid(), vo.getUuid(), VolumeSnapshotVO.class); return s; } }.execute(); FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("take-volume-snapshot-for-volume-%s", msg.getVolumeUuid())); chain.then(new ShareFlow() { VolumeSnapshotInventory snapshot; String volumeNewInstallPath; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "take-volume-snapshot"; @Override public void run(final FlowTrigger trigger, Map data) { final TakeSnapshotMsg tmsg = new TakeSnapshotMsg(); tmsg.setPrimaryStorageUuid(primaryStorageUuid); tmsg.setStruct(struct); bus.makeTargetServiceIdByResourceUuid(tmsg, PrimaryStorageConstant.SERVICE_ID, primaryStorageUuid); bus.send(tmsg, new CloudBusCallBack(trigger) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { trigger.fail(reply.getError()); return; } TakeSnapshotReply treply = (TakeSnapshotReply) reply; volumeNewInstallPath = treply.getNewVolumeInstallPath(); snapshot = treply.getInventory(); trigger.next(); } }); } }); flow(new NoRollbackFlow() { String __name__ = "reserve-snapshot-size-on-primary-storage"; @Override public void run(final FlowTrigger trigger, Map data) { ExceptionDSL.exceptionSafe(new Runnable() { @Override public void run() { // TODO PrimaryStorageCapacityUpdater updater = new PrimaryStorageCapacityUpdater(vol.getPrimaryStorageUuid()); updater.reserve(snapshot.getSize()); } }); trigger.next(); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { if (volumeNewInstallPath != null) { vol.setInstallPath(volumeNewInstallPath); dbf.update(vol); } VolumeSnapshotVO svo = dbf.findByUuid(snapshot.getUuid(), VolumeSnapshotVO.class); svo.setType(snapshot.getType()); svo.setPrimaryStorageUuid(snapshot.getPrimaryStorageUuid()); svo.setPrimaryStorageInstallPath(snapshot.getPrimaryStorageInstallPath()); svo.setStatus(VolumeSnapshotStatus.Ready); svo.setSize(snapshot.getSize()); if (snapshot.getFormat() != null) { svo.setFormat(snapshot.getFormat()); } svo = dbf.updateAndRefresh(svo); ret.setInventory(VolumeSnapshotInventory.valueOf(svo)); bus.reply(msg, ret); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { rollbackSnapshot(struct.getCurrent().getUuid()); ret.setError(errCode); bus.reply(msg, ret); } }); } }).start(); } private void handle(APICreateVolumeSnapshotSchedulerMsg msg) { APICreateVolumeSnapshotSchedulerEvent evt = new APICreateVolumeSnapshotSchedulerEvent(msg.getId()); CreateVolumeSnapshotJob job = new CreateVolumeSnapshotJob(msg); job.setVolumeUuid(msg.getVolumeUuid()); job.setTargetResourceUuid(msg.getVolumeUuid()); job.setSnapShotName(msg.getSnapShotName()); job.setSnapShotDescription(msg.getVolumeSnapshotDescription()); SchedulerVO schedulerVO = schedulerFacade.runScheduler(job); acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), schedulerVO.getUuid(), SchedulerVO.class); if (schedulerVO != null) { schedulerVO = dbf.reload(schedulerVO); SchedulerInventory sinv = SchedulerInventory.valueOf(schedulerVO); evt.setInventory(sinv); } bus.publish(evt); } private void handleApiMessage(APIMessage msg) { if (msg instanceof APIGetVolumeSnapshotTreeMsg) { handle((APIGetVolumeSnapshotTreeMsg) msg); } else if (msg instanceof APICreateVolumeSnapshotSchedulerMsg) { handle((APICreateVolumeSnapshotSchedulerMsg) msg); } else { bus.dealWithUnknownMessage(msg); } } @Override public String getId() { return bus.makeLocalServiceId(VolumeSnapshotConstant.SERVICE_ID); } @Override public boolean start() { pluginRgty.saveExtensionAsMap(CreateTemplateFromVolumeSnapshotExtensionPoint.class, new Function<Object, CreateTemplateFromVolumeSnapshotExtensionPoint>() { @Override public Object call(CreateTemplateFromVolumeSnapshotExtensionPoint arg) { return arg.createTemplateFromVolumeSnapshotPrimaryStorageType(); } }); bus.installBeforeDeliveryMessageInterceptor(new AbstractBeforeDeliveryMessageInterceptor() { @Override public void intercept(Message msg) { if (msg instanceof NeedQuotaCheckMessage) { if (((NeedQuotaCheckMessage) msg).getAccountUuid() == null || ((NeedQuotaCheckMessage) msg).getAccountUuid().equals("")) { // skip admin scheduler return; } List<Quota> quotas = acntMgr.getMessageQuotaMap().get(msg.getClass()); if (quotas == null || quotas.size() == 0) { return; } Map<String, Quota.QuotaPair> pairs = new QuotaUtil(). makeQuotaPairs(((NeedQuotaCheckMessage) msg).getAccountUuid()); for (Quota quota : quotas) { quota.getOperator().checkQuota((NeedQuotaCheckMessage) msg, pairs); } } } }, VolumeCreateSnapshotMsg.class); return true; } @Override public boolean stop() { return true; } @Override public List<Class> getReplyMessageClassForPreSendingExtensionPoint() { List<Class> ret = new ArrayList<>(); ret.add(APIQueryVolumeSnapshotTreeReply.class); return ret; } @Override public void marshalReplyMessageBeforeSending(Message msg) { if (msg instanceof APIQueryVolumeSnapshotTreeReply) { marshal((APIQueryVolumeSnapshotTreeReply) msg); } } private void marshal(APIQueryVolumeSnapshotTreeReply reply) { if (reply.getInventories() == null) { // this is for count return; } for (VolumeSnapshotTreeInventory inv : reply.getInventories()) { SimpleQuery<VolumeSnapshotVO> sq = dbf.createQuery(VolumeSnapshotVO.class); sq.add(VolumeSnapshotVO_.treeUuid, Op.EQ, inv.getUuid()); List<VolumeSnapshotVO> vos = sq.list(); VolumeSnapshotTree tree = VolumeSnapshotTree.fromVOs(vos); inv.setTree(tree.getRoot().toLeafInventory()); } } @Override public void volumeBeforeExpunge(VolumeInventory volume) { List<VolumeSnapshotDeletionMsg> msgs = new ArrayList<>(); SimpleQuery<VolumeSnapshotTreeVO> cq = dbf.createQuery(VolumeSnapshotTreeVO.class); cq.select(VolumeSnapshotTreeVO_.uuid); cq.add(VolumeSnapshotTreeVO_.volumeUuid, Op.EQ, volume.getUuid()); List<String> cuuids = cq.listValue(); for (String cuuid : cuuids) { // deleting full snapshot of chain will cause whole chain to be deleted SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class); q.select(VolumeSnapshotVO_.uuid); q.add(VolumeSnapshotVO_.treeUuid, Op.EQ, cuuid); q.add(VolumeSnapshotVO_.parentUuid, Op.NULL); String suuid = q.findValue(); if (suuid == null) { // this is a storage snapshot, don't delete it on primary storage continue; } SimpleQuery<VolumeSnapshotVO> sq = dbf.createQuery(VolumeSnapshotVO.class); sq.select(VolumeSnapshotVO_.volumeUuid, VolumeSnapshotVO_.treeUuid); sq.add(VolumeSnapshotVO_.uuid, Op.EQ, suuid); Tuple t = sq.findTuple(); String volumeUuid = t.get(0, String.class); String treeUuid = t.get(1, String.class); VolumeSnapshotDeletionMsg msg = new VolumeSnapshotDeletionMsg(); msg.setSnapshotUuid(suuid); msg.setTreeUuid(treeUuid); msg.setVolumeUuid(volumeUuid); msg.setVolumeDeletion(true); String resourceUuid = volumeUuid != null ? volumeUuid : treeUuid; bus.makeTargetServiceIdByResourceUuid(msg, VolumeSnapshotConstant.SERVICE_ID, resourceUuid); msgs.add(msg); } if (!msgs.isEmpty()) { bus.call(msgs); } } @Override public void resourceOwnerAfterChange(AccountResourceRefInventory ref, String newOwnerUuid) { if (!VolumeVO.class.getSimpleName().equals(ref.getResourceType())) { return; } changeVolumeSnapshotOwner(ref, newOwnerUuid); } private void changeVolumeSnapshotOwner(AccountResourceRefInventory ref, String newOwnerUuid) { SimpleQuery<VolumeSnapshotVO> q = dbf.createQuery(VolumeSnapshotVO.class); q.select(VolumeSnapshotVO_.uuid); q.add(VolumeSnapshotVO_.volumeUuid, Op.EQ, ref.getResourceUuid()); List<String> spUuids = q.listValue(); for (String spUuid : spUuids) { acntMgr.changeResourceOwner(spUuid, newOwnerUuid); } } @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 APICreateVolumeSnapshotMsg) { check((APICreateVolumeSnapshotMsg) 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) { if (!new QuotaUtil().isAdminAccount(msg.getAccountUuid())) { if (msg instanceof VolumeCreateSnapshotMsg) { check((VolumeCreateSnapshotMsg) msg, pairs); } } } @Override public List<Quota.QuotaUsage> getQuotaUsageByAccount(String accountUuid) { List<Quota.QuotaUsage> usages = new ArrayList<>(); Quota.QuotaUsage usage; usage = new Quota.QuotaUsage(); usage.setName(VolumeSnapshotConstant.QUOTA_VOLUME_SNAPSHOT_NUM); usage.setUsed(getUsedVolumeSnapshotNum(accountUuid)); usages.add(usage); return usages; } private long getUsedVolumeSnapshotNum(String accountUuid) { SimpleQuery<AccountResourceRefVO> queryVolumeSnapshotNum = dbf.createQuery(AccountResourceRefVO.class); queryVolumeSnapshotNum.add(AccountResourceRefVO_.accountUuid, Op.EQ, accountUuid); queryVolumeSnapshotNum.add(AccountResourceRefVO_.resourceType, Op.EQ, VolumeSnapshotVO.class.getSimpleName()); return queryVolumeSnapshotNum.count(); } @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; } String resourceType = new QuotaUtil().getResourceType(msg.getResourceUuid()); long volumeSnapshotNumAsked; if (resourceType.equals(VmInstanceVO.class.getSimpleName())) { String sql = "select count(s)" + " from VolumeVO v, VolumeSnapshotVO s" + " where s.volumeUuid = v.uuid" + " and v.vmInstanceUuid = :vmInstanceUuid"; TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class); q.setParameter("vmInstanceUuid", msg.getResourceUuid()); volumeSnapshotNumAsked = q.getSingleResult(); } else if (resourceType.equals(VolumeVO.class.getSimpleName())) { String sql = "select count(s)" + " from VolumeSnapshotVO s" + " where s.volumeUuid = :volumeUuid"; TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class); q.setParameter("volumeUuid", msg.getResourceUuid()); volumeSnapshotNumAsked = q.getSingleResult(); } else if (resourceType.equals(VolumeSnapshotVO.class.getSimpleName())) { volumeSnapshotNumAsked = 1; } else { return; } checkVolumeSnapshotNumQuota(currentAccountUuid, resourceTargetOwnerAccountUuid, volumeSnapshotNumAsked, pairs); } private void check(VolumeCreateSnapshotMsg msg, Map<String, Quota.QuotaPair> pairs) { checkVolumeSnapshotNumQuota(msg.getAccountUuid(), msg.getAccountUuid(), 1, pairs); } private void check(APICreateVolumeSnapshotMsg msg, Map<String, Quota.QuotaPair> pairs) { String resourceTargetOwnerUuid = new QuotaUtil().getResourceOwnerAccountUuid(msg.getVolumeUuid()); checkVolumeSnapshotNumQuota(msg.getSession().getAccountUuid(), resourceTargetOwnerUuid, 1, pairs); } private void checkVolumeSnapshotNumQuota(String currentAccountUuid, String resourceTargetOwnerAccountUuid, long volumeSnapshotNumAsked, Map<String, Quota.QuotaPair> pairs) { long volumeSnapshotNumQuota = pairs.get(VolumeSnapshotConstant.QUOTA_VOLUME_SNAPSHOT_NUM).getValue(); long volumeSnapshotNumUsed = getUsedVolumeSnapshotNum(resourceTargetOwnerAccountUuid); { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeSnapshotConstant.QUOTA_VOLUME_SNAPSHOT_NUM; quotaCompareInfo.quotaValue = volumeSnapshotNumQuota; quotaCompareInfo.currentUsed = volumeSnapshotNumUsed; quotaCompareInfo.request = volumeSnapshotNumAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } }; Quota quota = new Quota(); Quota.QuotaPair p; p = new Quota.QuotaPair(); p.setName(VolumeSnapshotConstant.QUOTA_VOLUME_SNAPSHOT_NUM); p.setValue(200); quota.addPair(p); quota.addMessageNeedValidation(APICreateVolumeSnapshotMsg.class); quota.addMessageNeedValidation(APIChangeResourceOwnerMsg.class); quota.addMessageNeedValidation(VolumeCreateSnapshotMsg.class); quota.setOperator(checker); return list(quota); } @Transactional @Override public void afterReimageVmInstance(VolumeInventory inventory) { String rootVolumeUuid = inventory.getUuid(); String sql = "update VolumeSnapshotVO s" + " set s.latest = false" + " where s.latest = true" + " and s.volumeUuid = :volumeUuid"; Query q = dbf.getEntityManager().createQuery(sql); q.setParameter("volumeUuid", rootVolumeUuid); q.executeUpdate(); sql = "update VolumeSnapshotTreeVO tree" + " set tree.current = false" + " where tree.current = true" + " and tree.volumeUuid = :volUuid"; q = dbf.getEntityManager().createQuery(sql); q.setParameter("volUuid", rootVolumeUuid); q.executeUpdate(); } }