package org.zstack.compute.vm; import org.apache.commons.validator.routines.DomainValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.compute.allocator.HostAllocatorManager; import org.zstack.core.Platform; import org.zstack.core.asyncbatch.While; 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.*; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.jsonlabel.JsonLabel; import org.zstack.core.notification.N; import org.zstack.core.scheduler.SchedulerConstant; import org.zstack.core.scheduler.SchedulerFacade; import org.zstack.core.thread.AsyncThread; import org.zstack.core.thread.CancelablePeriodicTask; import org.zstack.core.thread.ThreadFacade; import org.zstack.core.workflow.FlowChainBuilder; import org.zstack.header.AbstractService; import org.zstack.header.allocator.AllocateHostDryRunReply; import org.zstack.header.allocator.DesignatedAllocateHostMsg; import org.zstack.header.allocator.HostAllocatorConstant; import org.zstack.header.apimediator.ApiMessageInterceptionException; import org.zstack.header.apimediator.GlobalApiMessageInterceptor; import org.zstack.header.cluster.ClusterInventory; import org.zstack.header.cluster.ClusterVO; import org.zstack.header.configuration.DiskOfferingInventory; import org.zstack.header.configuration.DiskOfferingVO; import org.zstack.header.configuration.DiskOfferingVO_; import org.zstack.header.configuration.InstanceOfferingVO; import org.zstack.header.core.FutureCompletion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.core.scheduler.APICreateSchedulerMessage; import org.zstack.header.core.workflow.FlowChain; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.errorcode.SysErrors; import org.zstack.header.exception.CloudConfigureFailException; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.host.AfterChangeHostStatusExtensionPoint; import org.zstack.header.host.HostInventory; import org.zstack.header.host.HostStatus; import org.zstack.header.identity.*; import org.zstack.header.identity.Quota.QuotaOperator; import org.zstack.header.identity.Quota.QuotaPair; import org.zstack.header.image.ImageConstant.ImageMediaType; import org.zstack.header.image.ImageInventory; import org.zstack.header.image.ImagePlatform; import org.zstack.header.image.ImageVO; import org.zstack.header.image.ImageVO_; import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint; import org.zstack.header.message.*; import org.zstack.header.network.l3.*; import org.zstack.header.search.SearchOp; import org.zstack.header.storage.backup.BackupStorageType; import org.zstack.header.storage.backup.BackupStorageVO; import org.zstack.header.storage.primary.PrimaryStorageType; import org.zstack.header.storage.primary.PrimaryStorageVO; import org.zstack.header.tag.SystemTagCreateMessageValidator; import org.zstack.header.tag.SystemTagVO; import org.zstack.header.tag.SystemTagValidator; import org.zstack.header.vm.*; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import org.zstack.header.vm.VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy; import org.zstack.header.volume.*; import org.zstack.header.zone.ZoneInventory; import org.zstack.header.zone.ZoneVO; 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.TagUtils; import org.zstack.utils.Utils; import org.zstack.utils.data.SizeUnit; import org.zstack.utils.function.Function; import org.zstack.utils.gson.JSONObjectUtil; import org.zstack.utils.logging.CLogger; import org.zstack.utils.network.NetworkUtils; import static org.zstack.core.Platform.*; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.sql.Timestamp; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static org.zstack.utils.CollectionDSL.list; public class VmInstanceManagerImpl extends AbstractService implements VmInstanceManager, ReportQuotaExtensionPoint, ManagementNodeReadyExtensionPoint, L3NetworkDeleteExtensionPoint, ResourceOwnerAfterChangeExtensionPoint, GlobalApiMessageInterceptor, AfterChangeHostStatusExtensionPoint { private static final CLogger logger = Utils.getLogger(VmInstanceManagerImpl.class); private Map<String, VmInstanceFactory> vmInstanceFactories = Collections.synchronizedMap(new HashMap<>()); private List<String> createVmWorkFlowElements; private List<String> stopVmWorkFlowElements; private List<String> rebootVmWorkFlowElements; private List<String> startVmWorkFlowElements; private List<String> destroyVmWorkFlowElements; private List<String> migrateVmWorkFlowElements; private List<String> attachIsoWorkFlowElements; private List<String> detachIsoWorkFlowElements; private List<String> attachVolumeWorkFlowElements; private List<String> expungeVmWorkFlowElements; private List<String> pauseVmWorkFlowElements; private List<String> resumeVmWorkFlowElements; private FlowChainBuilder createVmFlowBuilder; private FlowChainBuilder stopVmFlowBuilder; private FlowChainBuilder rebootVmFlowBuilder; private FlowChainBuilder startVmFlowBuilder; private FlowChainBuilder destroyVmFlowBuilder; private FlowChainBuilder migrateVmFlowBuilder; private FlowChainBuilder attachVolumeFlowBuilder; private FlowChainBuilder attachIsoFlowBuilder; private FlowChainBuilder detachIsoFlowBuilder; private FlowChainBuilder expungeVmFlowBuilder; private FlowChainBuilder pauseVmFlowBuilder; private FlowChainBuilder resumeVmFlowBuilder; private static final Set<Class> allowedMessageAfterSoftDeletion = new HashSet<>(); private Future<Void> expungeVmTask; private Map<Class, VmInstanceBaseExtensionFactory> vmInstanceBaseExtensionFactories = new HashMap<>(); static { allowedMessageAfterSoftDeletion.add(VmInstanceDeletionMsg.class); } @Autowired private CloudBus bus; @Autowired private DatabaseFacade dbf; @Autowired private PluginRegistry pluginRgty; @Autowired private DbEntityLister dl; @Autowired private AccountManager acntMgr; @Autowired private TagManager tagMgr; @Autowired private ErrorFacade errf; @Autowired private ResourceDestinationMaker destMaker; @Autowired private ThreadFacade thdf; @Autowired private VmInstanceDeletionPolicyManager deletionPolicyMgr; @Autowired private EventFacade evtf; @Autowired private HostAllocatorManager hostAllocatorMgr; @Autowired private SchedulerFacade schedulerFacade; @Override @MessageSafe public void handleMessage(Message msg) { if (msg instanceof APIMessage) { handleApiMessage((APIMessage) msg); } else { handleLocalMessage(msg); } } void passThrough(VmInstanceMessage msg) { VmInstanceVO vo = dbf.findByUuid(msg.getVmInstanceUuid(), VmInstanceVO.class); if (vo == null && allowedMessageAfterSoftDeletion.contains(msg.getClass())) { VmInstanceEO eo = dbf.findByUuid(msg.getVmInstanceUuid(), VmInstanceEO.class); vo = ObjectUtils.newAndCopy(eo, VmInstanceVO.class); } if (vo == null) { String err = String.format("Cannot find VmInstance[uuid:%s], it may have been deleted", msg.getVmInstanceUuid()); bus.replyErrorByMessageType((Message) msg, err); return; } VmInstanceFactory factory = getVmInstanceFactory(VmInstanceType.valueOf(vo.getType())); VmInstance vm = factory.getVmInstance(vo); vm.handleMessage((Message) msg); } private void handleLocalMessage(Message msg) { if (msg instanceof CreateVmInstanceMsg) { handle((CreateVmInstanceMsg) msg); } else if (msg instanceof VmInstanceMessage) { passThrough((VmInstanceMessage) msg); } else { bus.dealWithUnknownMessage(msg); } } private void handleApiMessage(APIMessage msg) { if (msg instanceof APICreateVmInstanceMsg) { handle((APICreateVmInstanceMsg) msg); } else if (msg instanceof APIListVmInstanceMsg) { handle((APIListVmInstanceMsg) msg); } else if (msg instanceof APISearchVmInstanceMsg) { handle((APISearchVmInstanceMsg) msg); } else if (msg instanceof APIGetVmInstanceMsg) { handle((APIGetVmInstanceMsg) msg); } else if (msg instanceof APIListVmNicMsg) { handle((APIListVmNicMsg) msg); } else if (msg instanceof APIGetCandidateZonesClustersHostsForCreatingVmMsg) { handle((APIGetCandidateZonesClustersHostsForCreatingVmMsg) msg); } else if (msg instanceof APIGetInterdependentL3NetworksImagesMsg) { handle((APIGetInterdependentL3NetworksImagesMsg) msg); } else if (msg instanceof APIGetCandidateVmForAttachingIsoMsg) { handle((APIGetCandidateVmForAttachingIsoMsg) msg); } else if (msg instanceof VmInstanceMessage) { passThrough((VmInstanceMessage) msg); } else { bus.dealWithUnknownMessage(msg); } } @Transactional(readOnly = true) private void handle(APIGetCandidateVmForAttachingIsoMsg msg) { APIGetCandidateVmForAttachingIsoReply reply = new APIGetCandidateVmForAttachingIsoReply(); String sql = "select bs" + " from BackupStorageVO bs, ImageBackupStorageRefVO ref" + " where ref.imageUuid = :isoUuid" + " and bs.uuid = ref.backupStorageUuid"; TypedQuery<BackupStorageVO> q = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); q.setParameter("isoUuid", msg.getIsoUuid()); List<BackupStorageVO> bss = q.getResultList(); if (bss.isEmpty()) { reply.setInventories(new ArrayList<>()); bus.reply(msg, reply); return; } List<String> psUuids = new ArrayList<>(); List<String> psTypes = new ArrayList<>(); for (BackupStorageVO bs : bss) { BackupStorageType bsType = BackupStorageType.valueOf(bs.getType()); List<String> lst = bsType.findRelatedPrimaryStorage(bs.getUuid()); if (lst != null) { psUuids.addAll(lst); } else { psTypes.addAll(hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics(bs.getType())); } } List<VmInstanceVO> vms = new ArrayList<>(); if (!psUuids.isEmpty()) { sql = "select vm" + " from VmInstanceVO vm, VolumeVO vol" + " where vol.type = :volType" + " and vol.vmInstanceUuid = vm.uuid" + " and vm.state in (:vmStates)" + " and vol.primaryStorageUuid in (:psUuids)"; TypedQuery<VmInstanceVO> vmq = dbf.getEntityManager().createQuery(sql, VmInstanceVO.class); vmq.setParameter("volType", VolumeType.Root); vmq.setParameter("vmStates", asList(VmInstanceState.Running, VmInstanceState.Stopped)); vmq.setParameter("psUuids", psUuids); vms.addAll(vmq.getResultList()); } if (!psTypes.isEmpty()) { sql = "select vm" + " from VmInstanceVO vm, VolumeVO vol, PrimaryStorageVO ps" + " where vol.type = :volType" + " and vol.vmInstanceUuid = vm.uuid" + " and vm.state in (:vmStates)" + " and vol.primaryStorageUuid = ps.uuid" + " and ps.type in (:psTypes)"; TypedQuery<VmInstanceVO> vmq = dbf.getEntityManager().createQuery(sql, VmInstanceVO.class); vmq.setParameter("volType", VolumeType.Root); vmq.setParameter("vmStates", asList(VmInstanceState.Running, VmInstanceState.Stopped)); vmq.setParameter("psTypes", psTypes); vms.addAll(vmq.getResultList()); } reply.setInventories(VmInstanceInventory.valueOf(vms)); bus.reply(msg, reply); } private void handle(APIGetInterdependentL3NetworksImagesMsg msg) { final String accountUuid = msg.getSession().getAccountUuid(); if (msg.getImageUuid() != null) { getInterdependentL3NetworksByImageUuid(msg, accountUuid); } else { getInterdependentImagesByL3NetworkUuids(msg); } } private List<BackupStorageVO> listIntersection(List<BackupStorageVO> a, List<BackupStorageVO> b) { List<BackupStorageVO> ret = new ArrayList<>(); for (BackupStorageVO s : a) { if (b.stream().filter(it -> it.getUuid().equals(s.getUuid())).findAny().isPresent()) { ret.add(s); } } return ret; } @Transactional(readOnly = true) private void getInterdependentImagesByL3NetworkUuids(APIGetInterdependentL3NetworksImagesMsg msg) { APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); List<List<BackupStorageVO>> bss = new ArrayList<>(); for (String l3uuid : msg.getL3NetworkUuids()) { String sql = "select ps" + " from PrimaryStorageVO ps, L2NetworkClusterRefVO l2ref," + " L3NetworkVO l3, PrimaryStorageClusterRefVO psref" + " where ps.uuid = psref.primaryStorageUuid" + " and psref.clusterUuid = l2ref.clusterUuid" + " and l2ref.l2NetworkUuid = l3.l2NetworkUuid" + " and l3.uuid = :l3uuid"; TypedQuery<PrimaryStorageVO> psq = dbf.getEntityManager().createQuery(sql, PrimaryStorageVO.class); psq.setParameter("l3uuid", l3uuid); List<PrimaryStorageVO> pss = psq.getResultList(); List<BackupStorageVO> lst = new ArrayList<>(); for (PrimaryStorageVO ps : pss) { PrimaryStorageType psType = PrimaryStorageType.valueOf(ps.getType()); List<String> bsUuids = psType.findBackupStorage(ps.getUuid()); if (bsUuids == null) { // the primary storage doesn't have bound backup storage sql = "select bs from BackupStorageVO bs where bs.type in (:types)"; TypedQuery<BackupStorageVO> bq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); bq.setParameter("types", hostAllocatorMgr.getBackupStorageTypesByPrimaryStorageTypeFromMetrics(ps.getType())); lst.addAll(bq.getResultList()); } else if (!bsUuids.isEmpty()) { // the primary storage has bound backup storage, e.g. ceph, fusionstor sql = "select bs from BackupStorageVO bs where bs.uuid in (:uuids)"; TypedQuery<BackupStorageVO> bq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); bq.setParameter("uuids", bsUuids); lst.addAll(bq.getResultList()); } else { logger.warn(String.format("the primary storage[uuid:%s, type:%s] needs a bound backup storage," + " but seems it's not added", ps.getUuid(), ps.getType())); } } bss.add(lst); } List<BackupStorageVO> selectedBss = new ArrayList<>(); for (List<BackupStorageVO> lst : bss) { selectedBss.addAll(lst); } for (List<BackupStorageVO> l : bss) { selectedBss = listIntersection(selectedBss, l); } if (selectedBss.isEmpty()) { reply.setInventories(new ArrayList()); bus.reply(msg, reply); return; } List<String> bsUuids = selectedBss.stream().map(BackupStorageVO::getUuid).collect(Collectors.toList()); String sql = "select img" + " from ImageVO img, ImageBackupStorageRefVO iref, BackupStorageZoneRefVO zref, BackupStorageVO bs" + " where img.uuid = iref.imageUuid" + " and iref.backupStorageUuid = zref.backupStorageUuid" + " and bs.uuid = zref.backupStorageUuid" + " and bs.uuid in (:uuids)" + " and zref.zoneUuid = :zoneUuid" + " group by img.uuid"; TypedQuery<ImageVO> iq = dbf.getEntityManager().createQuery(sql, ImageVO.class); iq.setParameter("uuids", bsUuids); iq.setParameter("zoneUuid", msg.getZoneUuid()); List<ImageVO> vos = iq.getResultList(); reply.setInventories(ImageInventory.valueOf(vos)); bus.reply(msg, reply); } @Transactional(readOnly = true) private void getInterdependentL3NetworksByImageUuid(APIGetInterdependentL3NetworksImagesMsg msg, String accountUuid) { APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); String sql = "select bs" + " from BackupStorageVO bs, ImageBackupStorageRefVO ref, BackupStorageZoneRefVO zref" + " where bs.uuid = ref.backupStorageUuid" + " and ref.imageUuid = :imgUuid" + " and ref.backupStorageUuid = zref.backupStorageUuid" + " and zref.zoneUuid = :zoneUuid"; TypedQuery<BackupStorageVO> bsq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); bsq.setParameter("imgUuid", msg.getImageUuid()); bsq.setParameter("zoneUuid", msg.getZoneUuid()); List<BackupStorageVO> bss = bsq.getResultList(); if (bss.isEmpty()) { throw new OperationFailureException(argerr("the image[uuid:%s] is not on any backup storage that has been attached to the zone[uuid:%s]", msg.getImageUuid(), msg.getZoneUuid())); } List<L3NetworkVO> l3s = new ArrayList<>(); for (BackupStorageVO bs : bss) { BackupStorageType bsType = BackupStorageType.valueOf(bs.getType()); List<String> relatedPrimaryStorageUuids = bsType.findRelatedPrimaryStorage(bs.getUuid()); if (relatedPrimaryStorageUuids == null) { // the backup storage has no strongly-bound primary storage List<String> psTypes = hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics(bs.getType()); sql = "select l3" + " from L3NetworkVO l3, L2NetworkClusterRefVO l2ref," + " PrimaryStorageClusterRefVO psref, PrimaryStorageVO ps" + " where l3.l2NetworkUuid = l2ref.l2NetworkUuid" + " and l2ref.clusterUuid = psref.clusterUuid" + " and psref.primaryStorageUuid = ps.uuid" + " and ps.type in (:psTypes)" + " and ps.zoneUuid = l3.zoneUuid" + " and l3.zoneUuid = :zoneUuid" + " group by l3.uuid"; TypedQuery<L3NetworkVO> l3q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class); l3q.setParameter("psTypes", psTypes); l3q.setParameter("zoneUuid", msg.getZoneUuid()); l3s.addAll(l3q.getResultList()); } else if (!relatedPrimaryStorageUuids.isEmpty()) { // the backup storage has strongly-bound primary storage, e.g. ceph, fusionstor sql = "select l3" + " from L3NetworkVO l3, L2NetworkClusterRefVO l2ref," + " PrimaryStorageClusterRefVO psref, PrimaryStorageVO ps" + " where l3.l2NetworkUuid = l2ref.l2NetworkUuid" + " and l2ref.clusterUuid = psref.clusterUuid" + " and psref.primaryStorageUuid = ps.uuid" + " and ps.uuid in (:psUuids)" + " and ps.zoneUuid = l3.zoneUuid" + " and l3.zoneUuid = :zoneUuid" + " group by l3.uuid"; TypedQuery<L3NetworkVO> l3q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class); l3q.setParameter("psUuids", relatedPrimaryStorageUuids); l3q.setParameter("zoneUuid", msg.getZoneUuid()); l3s.addAll(l3q.getResultList()); } else { logger.warn(String.format("the backup storage[uuid:%s, type: %s] needs a strongly-bound primary storage," + " but seems the primary storage is not added", bs.getUuid(), bs.getType())); } } List<String> l3sFromAccount = acntMgr.getResourceUuidsCanAccessByAccount(accountUuid, L3NetworkVO.class); if (l3sFromAccount == null) { reply.setInventories(L3NetworkInventory.valueOf(l3s)); } else { reply.setInventories(L3NetworkInventory.valueOf(l3s.stream() .filter(vo -> l3sFromAccount.contains(vo.getUuid())) .collect(Collectors.toList()))); } bus.reply(msg, reply); } private void handle(APIGetCandidateZonesClustersHostsForCreatingVmMsg msg) { DesignatedAllocateHostMsg amsg = new DesignatedAllocateHostMsg(); ImageVO image = dbf.findByUuid(msg.getImageUuid(), ImageVO.class); if (image.getMediaType() == ImageMediaType.ISO && msg.getRootDiskOfferingUuid() == null) { throw new OperationFailureException(argerr("the image[name:%s, uuid:%s] is an ISO, rootDiskOfferingUuid must be set", image.getName(), image.getUuid())); } amsg.setImage(ImageInventory.valueOf(image)); amsg.setZoneUuid(msg.getZoneUuid()); amsg.setClusterUuid(msg.getClusterUuid()); InstanceOfferingVO insvo = dbf.findByUuid(msg.getInstanceOfferingUuid(), InstanceOfferingVO.class); amsg.setCpuCapacity(insvo.getCpuNum()); amsg.setMemoryCapacity(insvo.getMemorySize()); long diskSize = 0; List<DiskOfferingInventory> diskOfferings = new ArrayList<>(); if (msg.getDataDiskOfferingUuids() != null) { SimpleQuery<DiskOfferingVO> q = dbf.createQuery(DiskOfferingVO.class); q.add(DiskOfferingVO_.uuid, Op.IN, msg.getDataDiskOfferingUuids()); List<DiskOfferingVO> dvos = q.list(); diskOfferings.addAll(DiskOfferingInventory.valueOf(dvos)); } if (image.getMediaType() == ImageMediaType.ISO) { DiskOfferingVO rootDiskOffering = dbf.findByUuid(msg.getRootDiskOfferingUuid(), DiskOfferingVO.class); diskOfferings.add(DiskOfferingInventory.valueOf(rootDiskOffering)); } else { diskSize = image.getSize(); } diskSize += diskOfferings.stream().mapToLong(DiskOfferingInventory::getDiskSize).sum(); amsg.setDiskSize(diskSize); amsg.setL3NetworkUuids(msg.getL3NetworkUuids()); amsg.setVmOperation(VmOperation.NewCreate.toString()); amsg.setDryRun(true); amsg.setListAllHosts(true); amsg.setAllocatorStrategy(HostAllocatorConstant.DESIGNATED_HOST_ALLOCATOR_STRATEGY_TYPE); if (image.getBackupStorageRefs().size() == 1) { amsg.setRequiredBackupStorageUuid(image.getBackupStorageRefs().iterator().next().getBackupStorageUuid()); } else { if (msg.getZoneUuid() == null) { throw new OperationFailureException(argerr("zoneUuid must be set because the image[name:%s, uuid:%s] is on multiple backup storage", image.getName(), image.getUuid())); } ImageBackupStorageSelector selector = new ImageBackupStorageSelector(); selector.setZoneUuid(msg.getZoneUuid()); selector.setImageUuid(image.getUuid()); amsg.setRequiredBackupStorageUuid(selector.select()); } VmInstanceInventory vm = new VmInstanceInventory(); vm.setUuid(Platform.FAKE_UUID); vm.setInstanceOfferingUuid(insvo.getUuid()); vm.setImageUuid(image.getUuid()); vm.setCpuNum(insvo.getCpuNum()); vm.setMemorySize(insvo.getMemorySize()); vm.setDefaultL3NetworkUuid(msg.getDefaultL3NetworkUuid() == null ? msg.getL3NetworkUuids().get(0) : msg.getDefaultL3NetworkUuid()); vm.setName("for-getting-candidates-zones-clusters-hosts"); amsg.setVmInstance(vm); APIGetCandidateZonesClustersHostsForCreatingVmReply areply = new APIGetCandidateZonesClustersHostsForCreatingVmReply(); bus.makeLocalServiceId(amsg, HostAllocatorConstant.SERVICE_ID); bus.send(amsg, new CloudBusCallBack(msg) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { areply.setError(reply.getError()); } else { AllocateHostDryRunReply re = reply.castReply(); if (!re.getHosts().isEmpty()) { areply.setHosts(re.getHosts()); List<String> clusterUuids = re.getHosts().stream(). map(HostInventory::getClusterUuid).collect(Collectors.toList()); areply.setClusters(ClusterInventory.valueOf(dbf.listByPrimaryKeys(clusterUuids, ClusterVO.class))); List<String> zoneUuids = re.getHosts().stream(). map(HostInventory::getZoneUuid).collect(Collectors.toList()); areply.setZones(ZoneInventory.valueOf(dbf.listByPrimaryKeys(zoneUuids, ZoneVO.class))); } else { areply.setHosts(new ArrayList<>()); areply.setClusters(new ArrayList<>()); areply.setZones(new ArrayList<>()); } } bus.reply(msg, areply); } }); } private void handle(APIListVmNicMsg msg) { List<VmNicVO> vos = dbf.listByApiMessage(msg, VmNicVO.class); List<VmNicInventory> invs = VmNicInventory.valueOf(vos); APIListVmNicReply reply = new APIListVmNicReply(); reply.setInventories(invs); bus.reply(msg, reply); } private void handle(APIGetVmInstanceMsg msg) { SearchQuery<VmInstanceInventory> query = new SearchQuery(VmInstanceInventory.class); query.addAccountAsAnd(msg); query.add("uuid", SearchOp.AND_EQ, msg.getUuid()); List<VmInstanceInventory> invs = query.list(); APIGetVmInstanceReply reply = new APIGetVmInstanceReply(); if (!invs.isEmpty()) { reply.setInventory(JSONObjectUtil.toJsonString(invs.get(0))); } bus.reply(msg, reply); } private void handle(APISearchVmInstanceMsg msg) { SearchQuery<VmInstanceInventory> query = SearchQuery.create(msg, VmInstanceInventory.class); query.addAccountAsAnd(msg); String content = query.listAsString(); APISearchVmInstanceReply reply = new APISearchVmInstanceReply(); reply.setContent(content); bus.reply(msg, reply); } private void handle(APIListVmInstanceMsg msg) { List<VmInstanceVO> vos = dl.listByApiMessage(msg, VmInstanceVO.class); List<VmInstanceInventory> invs = VmInstanceInventory.valueOf(vos); APIListVmInstanceReply reply = new APIListVmInstanceReply(); reply.setInventories(invs); bus.reply(msg, reply); } private void doCreateVmInstance(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, ReturnValueCompletion<VmInstanceInventory> completion) { final String instanceOfferingUuid = msg.getInstanceOfferingUuid(); VmInstanceVO vo = new VmInstanceVO(); if (msg.getResourceUuid() != null) { vo.setUuid(msg.getResourceUuid()); } else { vo.setUuid(Platform.getUuid()); } vo.setName(msg.getName()); vo.setClusterUuid(msg.getClusterUuid()); vo.setDescription(msg.getDescription()); vo.setHostUuid(msg.getHostUuid()); vo.setImageUuid(msg.getImageUuid()); vo.setInstanceOfferingUuid(instanceOfferingUuid); vo.setState(VmInstanceState.Created); vo.setZoneUuid(msg.getZoneUuid()); vo.setInternalId(dbf.generateSequenceNumber(VmInstanceSequenceNumberVO.class)); vo.setDefaultL3NetworkUuid(msg.getDefaultL3NetworkUuid()); SimpleQuery<ImageVO> imgq = dbf.createQuery(ImageVO.class); imgq.select(ImageVO_.platform); imgq.add(ImageVO_.uuid, Op.EQ, msg.getImageUuid()); ImagePlatform platform = imgq.findValue(); vo.setPlatform(platform.toString()); vo.setCpuNum(msg.getCpuNum()); vo.setCpuSpeed(msg.getCpuSpeed()); vo.setMemorySize(msg.getMemorySize()); vo.setAllocatorStrategy(msg.getAllocatorStrategy()); String vmType = msg.getType() == null ? VmInstanceConstant.USER_VM_TYPE : msg.getType(); VmInstanceType type = VmInstanceType.valueOf(vmType); VmInstanceFactory factory = getVmInstanceFactory(type); VmInstanceVO finalVo = vo; vo = new SQLBatchWithReturn<VmInstanceVO>() { @Override protected VmInstanceVO scripts() { factory.createVmInstance(finalVo, msg); dbf.getEntityManager().flush(); dbf.getEntityManager().refresh(finalVo); acntMgr.createAccountResourceRef(msg.getAccountUuid(), finalVo.getUuid(), VmInstanceVO.class); return finalVo; } }.execute(); if (cmsg != null) { tagMgr.createTagsFromAPICreateMessage(cmsg, vo.getUuid(), VmInstanceVO.class.getSimpleName()); } if (instanceOfferingUuid != null) { tagMgr.copySystemTag( instanceOfferingUuid, InstanceOfferingVO.class.getSimpleName(), vo.getUuid(), VmInstanceVO.class.getSimpleName()); } if (msg.getImageUuid() != null) { tagMgr.copySystemTag( msg.getImageUuid(), ImageVO.class.getSimpleName(), vo.getUuid(), VmInstanceVO.class.getSimpleName()); } if (VmCreationStrategy.JustCreate == VmCreationStrategy.valueOf(msg.getStrategy())) { VmInstanceInventory inv = VmInstanceInventory.valueOf(vo); createVmButNotStart(msg, inv); completion.success(inv); return; } StartNewCreatedVmInstanceMsg smsg = new StartNewCreatedVmInstanceMsg(); smsg.setDataDiskOfferingUuids(msg.getDataDiskOfferingUuids()); smsg.setL3NetworkUuids(msg.getL3NetworkUuids()); smsg.setRootDiskOfferingUuid(msg.getRootDiskOfferingUuid()); smsg.setVmInstanceInventory(VmInstanceInventory.valueOf(vo)); smsg.setPrimaryStorageUuidForRootVolume(msg.getPrimaryStorageUuidForRootVolume()); bus.makeTargetServiceIdByResourceUuid(smsg, VmInstanceConstant.SERVICE_ID, vo.getUuid()); bus.send(smsg, new CloudBusCallBack(smsg) { @Override public void run(MessageReply reply) { try { if (reply.isSuccess()) { StartNewCreatedVmInstanceReply r = (StartNewCreatedVmInstanceReply) reply; completion.success(r.getVmInventory()); } else { completion.fail(reply.getError()); } } catch (Exception e) { bus.logExceptionWithMessageDump(msg, e); bus.replyErrorByMessageType(msg, e); } } }); } private void createVmButNotStart(CreateVmInstanceMsg msg, VmInstanceInventory inv) { StartVmFromNewCreatedStruct struct = StartVmFromNewCreatedStruct.fromMessage(msg); new JsonLabel().create(StartVmFromNewCreatedStruct.makeLabelKey(inv.getUuid()), struct, inv.getUuid()); } private void handle(final CreateVmInstanceMsg msg) { doCreateVmInstance(msg, null, new ReturnValueCompletion<VmInstanceInventory>(msg) { @Override public void success(VmInstanceInventory inv) { CreateVmInstanceReply reply = new CreateVmInstanceReply(); reply.setInventory(inv); bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { CreateVmInstanceReply r = new CreateVmInstanceReply(); r.setError(errorCode); bus.reply(msg, r); } }); } private CreateVmInstanceMsg fromAPICreateVmInstanceMsg(APICreateVmInstanceMsg msg) { CreateVmInstanceMsg cmsg = new CreateVmInstanceMsg(); InstanceOfferingVO iovo = dbf.findByUuid(msg.getInstanceOfferingUuid(), InstanceOfferingVO.class); cmsg.setInstanceOfferingUuid(iovo.getUuid()); cmsg.setCpuNum(iovo.getCpuNum()); cmsg.setCpuSpeed(iovo.getCpuSpeed()); cmsg.setMemorySize(iovo.getMemorySize()); cmsg.setAllocatorStrategy(iovo.getAllocatorStrategy()); cmsg.setAccountUuid(msg.getSession().getAccountUuid()); cmsg.setName(msg.getName()); cmsg.setImageUuid(msg.getImageUuid()); cmsg.setL3NetworkUuids(msg.getL3NetworkUuids()); cmsg.setType(msg.getType()); cmsg.setRootDiskOfferingUuid(msg.getRootDiskOfferingUuid()); cmsg.setDataDiskOfferingUuids(msg.getDataDiskOfferingUuids()); cmsg.setZoneUuid(msg.getZoneUuid()); cmsg.setClusterUuid(msg.getClusterUuid()); cmsg.setHostUuid(msg.getHostUuid()); cmsg.setPrimaryStorageUuidForRootVolume(msg.getPrimaryStorageUuidForRootVolume()); cmsg.setDescription(msg.getDescription()); cmsg.setResourceUuid(msg.getResourceUuid()); cmsg.setDefaultL3NetworkUuid(msg.getDefaultL3NetworkUuid()); cmsg.setStrategy(msg.getStrategy()); return cmsg; } private void handle(final APICreateVmInstanceMsg msg) { doCreateVmInstance(fromAPICreateVmInstanceMsg(msg), msg, new ReturnValueCompletion<VmInstanceInventory>(msg) { APICreateVmInstanceEvent evt = new APICreateVmInstanceEvent(msg.getId()); @Override public void success(VmInstanceInventory inv) { evt.setInventory(inv); bus.publish(evt); } @Override public void fail(ErrorCode errorCode) { evt.setError(errorCode); bus.publish(evt); } }); } @Override public String getId() { return bus.makeLocalServiceId(VmInstanceConstant.SERVICE_ID); } private void createVmFlowChainBuilder() throws InstantiationException, IllegalAccessException, ClassNotFoundException { createVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(createVmWorkFlowElements).construct(); stopVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(stopVmWorkFlowElements).construct(); rebootVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(rebootVmWorkFlowElements).construct(); startVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(startVmWorkFlowElements).construct(); destroyVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(destroyVmWorkFlowElements).construct(); migrateVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(migrateVmWorkFlowElements).construct(); attachVolumeFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(attachVolumeWorkFlowElements).construct(); attachIsoFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(attachIsoWorkFlowElements).construct(); detachIsoFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(detachIsoWorkFlowElements).construct(); expungeVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(expungeVmWorkFlowElements).construct(); pauseVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(pauseVmWorkFlowElements).construct(); resumeVmFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(resumeVmWorkFlowElements).construct(); } private void populateExtensions() { for (VmInstanceFactory ext : pluginRgty.getExtensionList(VmInstanceFactory.class)) { VmInstanceFactory old = vmInstanceFactories.get(ext.getType().toString()); if (old != null) { throw new CloudRuntimeException(String.format("duplicate VmInstanceFactory[%s, %s] for type[%s]", old.getClass().getName(), ext.getClass().getName(), ext.getType())); } vmInstanceFactories.put(ext.getType().toString(), ext); } for (VmInstanceBaseExtensionFactory ext : pluginRgty.getExtensionList(VmInstanceBaseExtensionFactory.class)) { for (Class clz : ext.getMessageClasses()) { VmInstanceBaseExtensionFactory old = vmInstanceBaseExtensionFactories.get(clz); if (old != null) { throw new CloudRuntimeException(String.format("duplicate VmInstanceBaseExtensionFactory[%s, %s] for the" + " message[%s]", old.getClass(), ext.getClass(), clz)); } vmInstanceBaseExtensionFactories.put(clz, ext); } } } @Override public boolean start() { try { createVmFlowChainBuilder(); populateExtensions(); installSystemTagValidator(); installGlobalConfigUpdater(); 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, QuotaPair> pairs = new QuotaUtil(). makeQuotaPairs(((NeedQuotaCheckMessage) msg).getAccountUuid()); for (Quota quota : quotas) { quota.getOperator().checkQuota((NeedQuotaCheckMessage) msg, pairs); } } } }, StartVmInstanceMsg.class); return true; } catch (Exception e) { throw new CloudConfigureFailException(VmInstanceManagerImpl.class, e.getMessage(), e); } } private void installGlobalConfigUpdater() { VmGlobalConfig.VM_EXPUNGE_INTERVAL.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() { @Override public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) { startVmExpungeTask(); } }); VmGlobalConfig.VM_EXPUNGE_PERIOD.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() { @Override public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) { startVmExpungeTask(); } }); VmGlobalConfig.VM_DELETION_POLICY.installUpdateExtension(new GlobalConfigUpdateExtensionPoint() { @Override public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) { startVmExpungeTask(); } }); } private void installSystemTagValidator() { class HostNameValidator implements SystemTagCreateMessageValidator, SystemTagValidator { private void validateHostname(String tag, String hostname) { DomainValidator domainValidator = DomainValidator.getInstance(true); if (!domainValidator.isValid(hostname)) { throw new ApiMessageInterceptionException(argerr("hostname[%s] specified in system tag[%s] is not a valid domain name", hostname, tag)); } } @Override public void validateSystemTagInCreateMessage(APICreateMessage cmsg) { final APICreateVmInstanceMsg msg = (APICreateVmInstanceMsg) cmsg; int hostnameCount = 0; for (String sysTag : msg.getSystemTags()) { if (VmSystemTags.HOSTNAME.isMatch(sysTag)) { if (++hostnameCount > 1) { throw new ApiMessageInterceptionException(argerr("only one hostname system tag is allowed, but %s got", hostnameCount)); } String hostname = VmSystemTags.HOSTNAME.getTokenByTag(sysTag, VmSystemTags.HOSTNAME_TOKEN); validateHostname(sysTag, hostname); validateHostNameOnDefaultL3Network(sysTag, hostname, msg.getDefaultL3NetworkUuid()); } else if (VmSystemTags.STATIC_IP.isMatch(sysTag)) { validateStaticIp(sysTag); } } } private void validateStaticIp(String sysTag) { Map<String, String> token = TagUtils.parse(VmSystemTags.STATIC_IP.getTagFormat(), sysTag); String l3Uuid = token.get(VmSystemTags.STATIC_IP_L3_UUID_TOKEN); if (!dbf.isExist(l3Uuid, L3NetworkVO.class)) { throw new ApiMessageInterceptionException(argerr("L3 network[uuid:%s] not found. Please correct your system tag[%s] of static IP", l3Uuid, sysTag)); } String ip = token.get(VmSystemTags.STATIC_IP_TOKEN); if (!NetworkUtils.isIpv4Address(ip)) { throw new ApiMessageInterceptionException(argerr("%s is not a valid IPv4 address. Please correct your system tag[%s] of static IP", ip, sysTag)); } CheckIpAvailabilityMsg cmsg = new CheckIpAvailabilityMsg(); cmsg.setIp(ip); cmsg.setL3NetworkUuid(l3Uuid); bus.makeLocalServiceId(cmsg, L3NetworkConstant.SERVICE_ID); MessageReply r = bus.call(cmsg); if (!r.isSuccess()) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(SysErrors.INTERNAL, r.getError())); } CheckIpAvailabilityReply cr = r.castReply(); if (!cr.isAvailable()) { throw new ApiMessageInterceptionException(operr("IP[%s] is not available on the L3 network[uuid:%s]", ip, l3Uuid)); } } @Transactional(readOnly = true) private void validateHostNameOnDefaultL3Network(String tag, String hostname, String l3Uuid) { String sql = "select t" + " from SystemTagVO t, VmInstanceVO vm, VmNicVO nic" + " where t.resourceUuid = vm.uuid" + " and vm.uuid = nic.vmInstanceUuid" + " and nic.l3NetworkUuid = :l3Uuid" + " and t.tag = :sysTag"; TypedQuery<SystemTagVO> q = dbf.getEntityManager().createQuery(sql, SystemTagVO.class); q.setParameter("l3Uuid", l3Uuid); q.setParameter("sysTag", tag); List<SystemTagVO> vos = q.getResultList(); if (!vos.isEmpty()) { SystemTagVO sameTag = vos.get(0); throw new ApiMessageInterceptionException(argerr("conflict hostname in system tag[%s];" + " there has been a VM[uuid:%s] having hostname[%s] on L3 network[uuid:%s]", tag, sameTag.getResourceUuid(), hostname, l3Uuid)); } } @Override public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { if (VmSystemTags.HOSTNAME.isMatch(systemTag)) { String hostname = VmSystemTags.HOSTNAME.getTokenByTag(systemTag, VmSystemTags.HOSTNAME_TOKEN); validateHostname(systemTag, hostname); SimpleQuery<VmInstanceVO> q = dbf.createQuery(VmInstanceVO.class); q.select(VmInstanceVO_.defaultL3NetworkUuid); q.add(VmInstanceVO_.uuid, Op.EQ, resourceUuid); String defaultL3Uuid = q.findValue(); validateHostNameOnDefaultL3Network(systemTag, hostname, defaultL3Uuid); } else if (VmSystemTags.STATIC_IP.isMatch(systemTag)) { validateStaticIp(systemTag); } else if (VmSystemTags.BOOT_ORDER.isMatch(systemTag)) { validateBootOrder(systemTag); } } private void validateBootOrder(String systemTag) { String order = VmSystemTags.BOOT_ORDER.getTokenByTag(systemTag, VmSystemTags.BOOT_ORDER_TOKEN); for (String o : order.split(",")) { try { VmBootDevice.valueOf(o); } catch (IllegalArgumentException e) { throw new OperationFailureException(argerr("invalid boot device[%s] in boot order[%s]", o, order)); } } } } HostNameValidator hostnameValidator = new HostNameValidator(); tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), hostnameValidator); VmSystemTags.HOSTNAME.installValidator(hostnameValidator); } @Override public boolean stop() { return true; } @Override public VmInstanceFactory getVmInstanceFactory(VmInstanceType type) { VmInstanceFactory factory = vmInstanceFactories.get(type.toString()); if (factory == null) { throw new CloudRuntimeException(String.format("No VmInstanceFactory of type[%s] found", type)); } return factory; } @Override public VmInstanceBaseExtensionFactory getVmInstanceBaseExtensionFactory(Message msg) { return vmInstanceBaseExtensionFactories.get(msg.getClass()); } @Override public FlowChain getCreateVmWorkFlowChain(VmInstanceInventory inv) { return createVmFlowBuilder.build(); } @Override public FlowChain getStopVmWorkFlowChain(VmInstanceInventory inv) { return stopVmFlowBuilder.build(); } @Override public FlowChain getRebootVmWorkFlowChain(VmInstanceInventory inv) { return rebootVmFlowBuilder.build(); } @Override public FlowChain getStartVmWorkFlowChain(VmInstanceInventory inv) { return startVmFlowBuilder.build(); } @Override public FlowChain getDestroyVmWorkFlowChain(VmInstanceInventory inv) { return destroyVmFlowBuilder.build(); } @Override public FlowChain getMigrateVmWorkFlowChain(VmInstanceInventory inv) { return migrateVmFlowBuilder.build(); } @Override public FlowChain getAttachUninstantiatedVolumeWorkFlowChain(VmInstanceInventory inv) { return attachVolumeFlowBuilder.build(); } @Override public FlowChain getAttachIsoWorkFlowChain(VmInstanceInventory inv) { return attachIsoFlowBuilder.build(); } @Override public FlowChain getDetachIsoWorkFlowChain(VmInstanceInventory inv) { return detachIsoFlowBuilder.build(); } @Override public FlowChain getExpungeVmWorkFlowChain(VmInstanceInventory inv) { return expungeVmFlowBuilder.build(); } public FlowChain getPauseWorkFlowChain(VmInstanceInventory inv) { return pauseVmFlowBuilder.build(); } public FlowChain getResumeVmWorkFlowChain(VmInstanceInventory inv) { return resumeVmFlowBuilder.build(); } public void setCreateVmWorkFlowElements(List<String> createVmWorkFlowElements) { this.createVmWorkFlowElements = createVmWorkFlowElements; } public void setStopVmWorkFlowElements(List<String> stopVmWorkFlowElements) { this.stopVmWorkFlowElements = stopVmWorkFlowElements; } public void setRebootVmWorkFlowElements(List<String> rebootVmWorkFlowElements) { this.rebootVmWorkFlowElements = rebootVmWorkFlowElements; } public void setStartVmWorkFlowElements(List<String> startVmWorkFlowElements) { this.startVmWorkFlowElements = startVmWorkFlowElements; } public void setDestroyVmWorkFlowElements(List<String> destroyVmWorkFlowElements) { this.destroyVmWorkFlowElements = destroyVmWorkFlowElements; } public void setMigrateVmWorkFlowElements(List<String> migrateVmWorkFlowElements) { this.migrateVmWorkFlowElements = migrateVmWorkFlowElements; } public void setAttachVolumeWorkFlowElements(List<String> attachVolumeWorkFlowElements) { this.attachVolumeWorkFlowElements = attachVolumeWorkFlowElements; } public void setAttachIsoWorkFlowElements(List<String> attachIsoWorkFlowElements) { this.attachIsoWorkFlowElements = attachIsoWorkFlowElements; } public void setDetachIsoWorkFlowElements(List<String> detachIsoWorkFlowElements) { this.detachIsoWorkFlowElements = detachIsoWorkFlowElements; } public void setExpungeVmWorkFlowElements(List<String> expungeVmWorkFlowElements) { this.expungeVmWorkFlowElements = expungeVmWorkFlowElements; } public void setPauseVmWorkFlowElements(List<String> pauseVmWorkFlowElements) { this.pauseVmWorkFlowElements = pauseVmWorkFlowElements; } public void setResumeVmWorkFlowElements(List<String> resumeVmWorkFlowElements) { this.resumeVmWorkFlowElements = resumeVmWorkFlowElements; } @Override public List<Quota> reportQuota() { QuotaOperator checker = new QuotaOperator() { @Override public void checkQuota(APIMessage msg, Map<String, QuotaPair> pairs) { AccountType type = new QuotaUtil().getAccountType(msg.getSession().getAccountUuid()); if (type != AccountType.SystemAdmin) { if (msg instanceof APICreateVmInstanceMsg) { if (((APICreateVmInstanceMsg) msg).getStrategy(). equals(VmCreationStrategy.JustCreate.toString())) { return; } check((APICreateVmInstanceMsg) msg, pairs); } else if (msg instanceof APICreateDataVolumeMsg) { check((APICreateDataVolumeMsg) msg, pairs); } else if (msg instanceof APIRecoverDataVolumeMsg) { check((APIRecoverDataVolumeMsg) msg, pairs); } else if (msg instanceof APIStartVmInstanceMsg) { check((APIStartVmInstanceMsg) msg, pairs); } else if (msg instanceof APIChangeResourceOwnerMsg) { check((APIChangeResourceOwnerMsg) msg, pairs); } else if (msg instanceof APIRecoverVmInstanceMsg) { check((APIRecoverVmInstanceMsg) msg, pairs); } else if (msg instanceof APICreateSchedulerMessage) { check((APICreateSchedulerMessage) msg, pairs); } } else { if (msg instanceof APIChangeResourceOwnerMsg) { check((APIChangeResourceOwnerMsg) msg, pairs); } } } @Override public void checkQuota(NeedQuotaCheckMessage msg, Map<String, QuotaPair> pairs) { if (!new QuotaUtil().isAdminAccount(msg.getAccountUuid())) { if (msg instanceof StartVmInstanceMsg) { check((StartVmInstanceMsg) msg, pairs); } } } @Override public List<Quota.QuotaUsage> getQuotaUsageByAccount(String accountUuid) { List<Quota.QuotaUsage> usages = new ArrayList<>(); VmQuotaUtil.VmQuota vmQuota = new VmQuotaUtil().getUsedVmCpuMemory(accountUuid); Quota.QuotaUsage usage; usage = new Quota.QuotaUsage(); usage.setName(VmInstanceConstant.QUOTA_VM_TOTAL_NUM); usage.setUsed(vmQuota.totalVmNum); usages.add(usage); usage = new Quota.QuotaUsage(); usage.setName(VmInstanceConstant.QUOTA_VM_RUNNING_NUM); usage.setUsed(vmQuota.runningVmNum); usages.add(usage); usage = new Quota.QuotaUsage(); usage.setName(VmInstanceConstant.QUOTA_VM_RUNNING_CPU_NUM); usage.setUsed(vmQuota.runningVmCpuNum); usages.add(usage); usage = new Quota.QuotaUsage(); usage.setName(VmInstanceConstant.QUOTA_VM_RUNNING_MEMORY_SIZE); usage.setUsed(vmQuota.runningVmMemorySize); usages.add(usage); usage = new Quota.QuotaUsage(); usage.setName(VolumeConstant.QUOTA_DATA_VOLUME_NUM); usage.setUsed(new VmQuotaUtil().getUsedDataVolumeCount(accountUuid)); usages.add(usage); usage = new Quota.QuotaUsage(); usage.setName(VolumeConstant.QUOTA_VOLUME_SIZE); usage.setUsed(new VmQuotaUtil().getUsedAllVolumeSize(accountUuid)); usages.add(usage); usage = new Quota.QuotaUsage(); usage.setName(SchedulerConstant.QUOTA_SCHEDULER_NUM); usage.setUsed(new VmQuotaUtil().getUsedSchedulerNum(accountUuid)); usages.add(usage); return usages; } private void check(APIStartVmInstanceMsg msg, Map<String, Quota.QuotaPair> pairs) { checkStartVmInstance(msg.getSession().getAccountUuid(), msg.getVmInstanceUuid(), pairs); } private void check(StartVmInstanceMsg msg, Map<String, Quota.QuotaPair> pairs) { checkStartVmInstance(msg.getAccountUuid(), msg.getVmInstanceUuid(), pairs); } private void checkStartVmInstance(String currentAccountUuid, String vmInstanceUuid, Map<String, Quota.QuotaPair> pairs) { String resourceTargetOwnerAccountUuid = new QuotaUtil().getResourceOwnerAccountUuid(vmInstanceUuid); checkVmInstanceQuota(currentAccountUuid, resourceTargetOwnerAccountUuid, vmInstanceUuid, pairs); } @Transactional(readOnly = true) private void checkVmInstanceQuota(String currentAccountUuid, String resourceTargetOwnerAccountUuid, String vmInstanceUuid, Map<String, Quota.QuotaPair> pairs) { long vmNumQuota = pairs.get(VmInstanceConstant.QUOTA_VM_RUNNING_NUM).getValue(); long cpuNumQuota = pairs.get(VmInstanceConstant.QUOTA_VM_RUNNING_CPU_NUM).getValue(); long memoryQuota = pairs.get(VmInstanceConstant.QUOTA_VM_RUNNING_MEMORY_SIZE).getValue(); VmQuotaUtil.VmQuota vmQuotaUsed = new VmQuotaUtil().getUsedVmCpuMemory(resourceTargetOwnerAccountUuid); // { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VmInstanceConstant.QUOTA_VM_RUNNING_NUM; quotaCompareInfo.quotaValue = vmNumQuota; quotaCompareInfo.currentUsed = vmQuotaUsed.runningVmNum; quotaCompareInfo.request = 1; new QuotaUtil().CheckQuota(quotaCompareInfo); } // VmInstanceVO vm = dbf.getEntityManager().find(VmInstanceVO.class, vmInstanceUuid); { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VmInstanceConstant.QUOTA_VM_RUNNING_CPU_NUM; quotaCompareInfo.quotaValue = cpuNumQuota; quotaCompareInfo.currentUsed = vmQuotaUsed.runningVmCpuNum; quotaCompareInfo.request = vm.getCpuNum(); new QuotaUtil().CheckQuota(quotaCompareInfo); } { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VmInstanceConstant.QUOTA_VM_RUNNING_MEMORY_SIZE; quotaCompareInfo.quotaValue = memoryQuota; quotaCompareInfo.currentUsed = vmQuotaUsed.runningVmMemorySize; quotaCompareInfo.request = vm.getMemorySize(); new QuotaUtil().CheckQuota(quotaCompareInfo); } } private void checkVolumeQuotaForChangeResourceOwner(List<String> dataVolumeUuids, List<String> rootVolumeUuids, String resourceTargetOwnerAccountUuid, String currentAccountUuid, Map<String, Quota.QuotaPair> pairs) { long dataVolumeNumQuota = pairs.get(VolumeConstant.QUOTA_DATA_VOLUME_NUM).getValue(); long allVolumeSizeQuota = pairs.get(VolumeConstant.QUOTA_VOLUME_SIZE).getValue(); ArrayList<String> volumeUuids = new ArrayList<>(); if (dataVolumeUuids != null && !dataVolumeUuids.isEmpty()) { for (String uuid : dataVolumeUuids) { volumeUuids.add(uuid); } } if (rootVolumeUuids != null && !rootVolumeUuids.isEmpty()) { for (String uuid : rootVolumeUuids) { volumeUuids.add(uuid); } } // skip empty volume uuid list if (volumeUuids.isEmpty()) { return; } // check data volume num long dataVolumeNumUsed = new VmQuotaUtil().getUsedDataVolumeCount(resourceTargetOwnerAccountUuid); if (dataVolumeUuids != null && !dataVolumeUuids.isEmpty()) { long dataVolumeNumAsked = dataVolumeUuids.size(); { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeConstant.QUOTA_DATA_VOLUME_NUM; quotaCompareInfo.quotaValue = dataVolumeNumQuota; quotaCompareInfo.currentUsed = dataVolumeNumUsed; quotaCompareInfo.request = dataVolumeNumAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } // check data volume size long allVolumeSizeAsked; String sql = "select sum(size) from VolumeVO where uuid in (:uuids) "; TypedQuery<Long> dq = dbf.getEntityManager().createQuery(sql, Long.class); dq.setParameter("uuids", volumeUuids); Long dsize = dq.getSingleResult(); dsize = dsize == null ? 0 : dsize; allVolumeSizeAsked = dsize; long allVolumeSizeUsed = new VmQuotaUtil().getUsedAllVolumeSize(resourceTargetOwnerAccountUuid); { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeConstant.QUOTA_VOLUME_SIZE; quotaCompareInfo.quotaValue = allVolumeSizeQuota; quotaCompareInfo.currentUsed = allVolumeSizeUsed; quotaCompareInfo.request = allVolumeSizeAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } private void checkRunningVMQuotaForChangeResourceOwner(String vmInstanceUuid, String resourceTargetOwnerAccountUuid, String currentAccountUuid, Map<String, Quota.QuotaPair> pairs) { checkVmInstanceQuota(currentAccountUuid, resourceTargetOwnerAccountUuid, vmInstanceUuid, pairs); } @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()); if (resourceType.equals(VolumeVO.class.getSimpleName())) { String volumeUuid = msg.getResourceUuid(); ArrayList<String> volumeUuids = new ArrayList<>(); volumeUuids.add(volumeUuid); checkVolumeQuotaForChangeResourceOwner(volumeUuids, null, resourceTargetOwnerAccountUuid, currentAccountUuid, pairs); } else if (resourceType.equals(VmInstanceVO.class.getSimpleName())) { VmInstanceVO vmInstanceVO = dbf.findByUuid(msg.getResourceUuid(), VmInstanceVO.class); // filter vm state if (vmInstanceVO.getState().equals(VmInstanceState.Created)) { return; } else if (!vmInstanceVO.getState().equals(VmInstanceState.Stopped) && !vmInstanceVO.getState().equals(VmInstanceState.Running)) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(VmErrors.NOT_IN_CORRECT_STATE, String.format("Incorrect VM State.VM[uuid:%s] current state:%s. ", msg.getResourceUuid(), vmInstanceVO.getState()) )); } String vmInstanceUuid = msg.getResourceUuid(); // check vm if (vmInstanceVO.getState().equals(VmInstanceState.Running)) { checkRunningVMQuotaForChangeResourceOwner(vmInstanceUuid, resourceTargetOwnerAccountUuid, currentAccountUuid, pairs); } // check volume ArrayList<String> rootVolumeUuids = new ArrayList<>(); SimpleQuery<VolumeVO> sq = dbf.createQuery(VolumeVO.class); sq.add(VolumeVO_.vmInstanceUuid, Op.EQ, vmInstanceUuid); sq.add(VolumeVO_.type, Op.EQ, VolumeType.Root); VolumeVO volumeVO = sq.find(); if (volumeVO != null) { rootVolumeUuids.add(volumeVO.getUuid()); } ArrayList<String> dataVolumeUuids = new ArrayList<>(); SimpleQuery<VolumeVO> sq1 = dbf.createQuery(VolumeVO.class); sq1.add(VolumeVO_.vmInstanceUuid, Op.EQ, vmInstanceUuid); sq1.add(VolumeVO_.type, Op.EQ, VolumeType.Data); List<VolumeVO> volumeVOs = sq1.list(); if (volumeVOs != null && !volumeVOs.isEmpty()) { for (VolumeVO vvo : volumeVOs) { dataVolumeUuids.add(vvo.getUuid()); } } checkVolumeQuotaForChangeResourceOwner(dataVolumeUuids, rootVolumeUuids, resourceTargetOwnerAccountUuid, currentAccountUuid, pairs); } } private void check(APIRecoverDataVolumeMsg msg, Map<String, Quota.QuotaPair> pairs) { String currentAccountUuid = msg.getSession().getAccountUuid(); String resourceTargetOwnerAccountUuid = new QuotaUtil().getResourceOwnerAccountUuid(msg.getVolumeUuid()); // check data volume num long dataVolumeNumQuota = pairs.get(VolumeConstant.QUOTA_DATA_VOLUME_NUM).getValue(); long dataVolumeNumUsed = new VmQuotaUtil().getUsedDataVolumeCount(resourceTargetOwnerAccountUuid); long dataVolumeNumAsked = 1; QuotaUtil.QuotaCompareInfo quotaCompareInfo; { quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeConstant.QUOTA_DATA_VOLUME_NUM; quotaCompareInfo.quotaValue = dataVolumeNumQuota; quotaCompareInfo.currentUsed = dataVolumeNumUsed; quotaCompareInfo.request = dataVolumeNumAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } @Transactional(readOnly = true) private void check(APICreateDataVolumeMsg msg, Map<String, Quota.QuotaPair> pairs) { String currentAccountUuid = msg.getSession().getAccountUuid(); String resourceTargetOwnerAccountUuid = msg.getSession().getAccountUuid(); long dataVolumeNumQuota = pairs.get(VolumeConstant.QUOTA_DATA_VOLUME_NUM).getValue(); long allVolumeSizeQuota = pairs.get(VolumeConstant.QUOTA_VOLUME_SIZE).getValue(); // check data volume num long dataVolumeNumUsed = new VmQuotaUtil().getUsedDataVolumeCount(currentAccountUuid); long dataVolumeNumAsked = 1; QuotaUtil.QuotaCompareInfo quotaCompareInfo; { quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeConstant.QUOTA_DATA_VOLUME_NUM; quotaCompareInfo.quotaValue = dataVolumeNumQuota; quotaCompareInfo.currentUsed = dataVolumeNumUsed; quotaCompareInfo.request = dataVolumeNumAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } // check data volume size long allVolumeSizeAsked; String sql = "select diskSize from DiskOfferingVO where uuid = :uuid "; TypedQuery<Long> dq = dbf.getEntityManager().createQuery(sql, Long.class); dq.setParameter("uuid", msg.getDiskOfferingUuid()); Long dsize = dq.getSingleResult(); dsize = dsize == null ? 0 : dsize; allVolumeSizeAsked = dsize; long allVolumeSizeUsed = new VmQuotaUtil().getUsedAllVolumeSize(currentAccountUuid); { quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeConstant.QUOTA_VOLUME_SIZE; quotaCompareInfo.quotaValue = allVolumeSizeQuota; quotaCompareInfo.currentUsed = allVolumeSizeUsed; quotaCompareInfo.request = allVolumeSizeAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } @Transactional(readOnly = true) private void check(APICreateVmInstanceMsg msg, Map<String, QuotaPair> pairs) { String currentAccountUuid = msg.getSession().getAccountUuid(); String resourceTargetOwnerAccountUuid = msg.getSession().getAccountUuid(); long totalVmNumQuota = pairs.get(VmInstanceConstant.QUOTA_VM_TOTAL_NUM).getValue(); long runningVmNumQuota = pairs.get(VmInstanceConstant.QUOTA_VM_RUNNING_NUM).getValue(); long runningVmCpuNumQuota = pairs.get(VmInstanceConstant.QUOTA_VM_RUNNING_CPU_NUM).getValue(); long runningVmMemorySizeQuota = pairs.get(VmInstanceConstant.QUOTA_VM_RUNNING_MEMORY_SIZE).getValue(); long dataVolumeNumQuota = pairs.get(VolumeConstant.QUOTA_DATA_VOLUME_NUM).getValue(); long allVolumeSizeQuota = pairs.get(VolumeConstant.QUOTA_VOLUME_SIZE).getValue(); VmQuotaUtil.VmQuota vmQuotaUsed = new VmQuotaUtil().getUsedVmCpuMemory(currentAccountUuid); if (vmQuotaUsed.totalVmNum + 1 > totalVmNumQuota) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_EXCEEDING, String.format("quota exceeding. The account[uuid: %s] exceeds a quota[name: %s, value: %s]", currentAccountUuid, VmInstanceConstant.QUOTA_VM_TOTAL_NUM, totalVmNumQuota) )); } if (vmQuotaUsed.runningVmNum + 1 > runningVmNumQuota) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_EXCEEDING, String.format("quota exceeding. The account[uuid: %s] exceeds a quota[name: %s, value: %s]", currentAccountUuid, VmInstanceConstant.QUOTA_VM_RUNNING_NUM, runningVmNumQuota) )); } String sql = "select i.cpuNum, i.memorySize" + " from InstanceOfferingVO i" + " where i.uuid = :uuid"; TypedQuery<Tuple> iq = dbf.getEntityManager().createQuery(sql, Tuple.class); iq.setParameter("uuid", msg.getInstanceOfferingUuid()); Tuple it = iq.getSingleResult(); int cpuNumAsked = it.get(0, Integer.class); long memoryAsked = it.get(1, Long.class); if (vmQuotaUsed.runningVmCpuNum + cpuNumAsked > runningVmCpuNumQuota) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_EXCEEDING, String.format("quota exceeding. The account[uuid: %s] exceeds a quota[name: %s, value: %s]", currentAccountUuid, VmInstanceConstant.QUOTA_VM_RUNNING_CPU_NUM, runningVmCpuNumQuota) )); } if (vmQuotaUsed.runningVmMemorySize + memoryAsked > runningVmMemorySizeQuota) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_EXCEEDING, String.format("quota exceeding. The account[uuid: %s] exceeds a quota[name: %s, value: %s]", currentAccountUuid, VmInstanceConstant.QUOTA_VM_RUNNING_MEMORY_SIZE, runningVmMemorySizeQuota) )); } // check data volume num if (msg.getDataDiskOfferingUuids() != null && !msg.getDataDiskOfferingUuids().isEmpty()) { long dataVolumeNumUsed = new VmQuotaUtil().getUsedDataVolumeCount(currentAccountUuid); long dataVolumeNumAsked = msg.getDataDiskOfferingUuids().size(); if (dataVolumeNumUsed + dataVolumeNumAsked > dataVolumeNumQuota) { throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_EXCEEDING, String.format("quota exceeding. The account[uuid: %s] exceeds a quota[name: %s, value: %s]", currentAccountUuid, VolumeConstant.QUOTA_DATA_VOLUME_NUM, dataVolumeNumQuota) )); } } // check all volume size long allVolumeSizeAsked = 0; sql = "select img.size, img.mediaType" + " from ImageVO img" + " where img.uuid = :iuuid"; iq = dbf.getEntityManager().createQuery(sql, Tuple.class); iq.setParameter("iuuid", msg.getImageUuid()); it = iq.getSingleResult(); Long imgSize = it.get(0, Long.class); ImageMediaType imgType = it.get(1, ImageMediaType.class); List<String> diskOfferingUuids = new ArrayList<>(); if (msg.getDataDiskOfferingUuids() != null && !msg.getDataDiskOfferingUuids().isEmpty()) { diskOfferingUuids.addAll(msg.getDataDiskOfferingUuids()); } if (imgType == ImageMediaType.RootVolumeTemplate) { allVolumeSizeAsked += imgSize; } else if (imgType == ImageMediaType.ISO) { diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); } HashMap<String, Long> diskOfferingCountMap = new HashMap<>(); if (!diskOfferingUuids.isEmpty()) { for (String diskOfferingUuid : diskOfferingUuids) { if (diskOfferingCountMap.containsKey(diskOfferingUuid)) { diskOfferingCountMap.put(diskOfferingUuid, diskOfferingCountMap.get(diskOfferingUuid) + 1); } else { diskOfferingCountMap.put(diskOfferingUuid, 1L); } } for (String diskOfferingUuid : diskOfferingCountMap.keySet()) { sql = "select diskSize from DiskOfferingVO where uuid = :uuid"; TypedQuery<Long> dq = dbf.getEntityManager().createQuery(sql, Long.class); dq.setParameter("uuid", diskOfferingUuid); Long dsize = dq.getSingleResult(); dsize = dsize == null ? 0 : dsize; allVolumeSizeAsked += dsize * diskOfferingCountMap.get(diskOfferingUuid); } } long allVolumeSizeUsed = new VmQuotaUtil().getUsedAllVolumeSize(currentAccountUuid); QuotaUtil.QuotaCompareInfo quotaCompareInfo; { quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VolumeConstant.QUOTA_VOLUME_SIZE; quotaCompareInfo.quotaValue = allVolumeSizeQuota; quotaCompareInfo.currentUsed = allVolumeSizeUsed; quotaCompareInfo.request = allVolumeSizeAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } private void check(APIRecoverVmInstanceMsg msg, Map<String, QuotaPair> pairs) { String currentAccountUuid = msg.getSession().getAccountUuid(); String resourceTargetOwnerAccountUuid = msg.getSession().getAccountUuid(); long totalVmNumQuota = pairs.get(VmInstanceConstant.QUOTA_VM_TOTAL_NUM).getValue(); VmQuotaUtil.VmQuota vmQuotaUsed = new VmQuotaUtil().getUsedVmCpuMemory(currentAccountUuid); long totalVmNumAsked = 1; QuotaUtil.QuotaCompareInfo quotaCompareInfo; { quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = VmInstanceConstant.QUOTA_VM_TOTAL_NUM; quotaCompareInfo.quotaValue = totalVmNumQuota; quotaCompareInfo.currentUsed = vmQuotaUsed.totalVmNum; quotaCompareInfo.request = totalVmNumAsked; new QuotaUtil().CheckQuota(quotaCompareInfo); } } private void check(APICreateSchedulerMessage msg, Map<String, Quota.QuotaPair> pairs) { String currentAccountUuid = msg.getSession().getAccountUuid(); String resourceTargetOwnerAccountUuid = msg.getSession().getAccountUuid(); long schedulerNumQuota = pairs.get(SchedulerConstant.QUOTA_SCHEDULER_NUM).getValue(); long schedulerNumUsed = new VmQuotaUtil().getUsedSchedulerNum(resourceTargetOwnerAccountUuid); { QuotaUtil.QuotaCompareInfo quotaCompareInfo; quotaCompareInfo = new QuotaUtil.QuotaCompareInfo(); quotaCompareInfo.currentAccountUuid = currentAccountUuid; quotaCompareInfo.resourceTargetOwnerAccountUuid = resourceTargetOwnerAccountUuid; quotaCompareInfo.quotaName = SchedulerConstant.QUOTA_SCHEDULER_NUM; quotaCompareInfo.quotaValue = schedulerNumQuota; quotaCompareInfo.currentUsed = schedulerNumUsed; quotaCompareInfo.request = 1; new QuotaUtil().CheckQuota(quotaCompareInfo); } } }; Quota quota = new Quota(); QuotaPair p; p = new QuotaPair(); p.setName(VmInstanceConstant.QUOTA_VM_TOTAL_NUM); p.setValue(20); quota.addPair(p); p = new QuotaPair(); p.setName(VmInstanceConstant.QUOTA_VM_RUNNING_NUM); p.setValue(20); quota.addPair(p); p = new QuotaPair(); p.setName(VmInstanceConstant.QUOTA_VM_RUNNING_CPU_NUM); p.setValue(80); quota.addPair(p); p = new QuotaPair(); p.setName(VmInstanceConstant.QUOTA_VM_RUNNING_MEMORY_SIZE); p.setValue(SizeUnit.GIGABYTE.toByte(80)); quota.addPair(p); p = new QuotaPair(); p.setName(VolumeConstant.QUOTA_DATA_VOLUME_NUM); p.setValue(40); quota.addPair(p); p = new QuotaPair(); p.setName(VolumeConstant.QUOTA_VOLUME_SIZE); p.setValue(SizeUnit.TERABYTE.toByte(10)); quota.addPair(p); p = new Quota.QuotaPair(); p.setName(SchedulerConstant.QUOTA_SCHEDULER_NUM); p.setValue(80); quota.addPair(p); quota.addMessageNeedValidation(APICreateVmInstanceMsg.class); quota.addMessageNeedValidation(APIRecoverVmInstanceMsg.class); quota.addMessageNeedValidation(APICreateDataVolumeMsg.class); quota.addMessageNeedValidation(APIRecoverDataVolumeMsg.class); quota.addMessageNeedValidation(APIStartVmInstanceMsg.class); quota.addMessageNeedValidation(APIChangeResourceOwnerMsg.class); quota.addMessageNeedValidation(StartVmInstanceMsg.class); // scheduler quota.addMessageNeedValidation(APICreateSchedulerMessage.class); quota.addMessageNeedValidation(APICreateStartVmInstanceSchedulerMsg.class); quota.addMessageNeedValidation(APICreateVolumeSnapshotSchedulerMsg.class); quota.addMessageNeedValidation(APICreateRebootVmInstanceSchedulerMsg.class); quota.addMessageNeedValidation(APICreateStopVmInstanceSchedulerMsg.class); quota.setOperator(checker); return list(quota); } @Override @AsyncThread public void managementNodeReady() { //checkUnknownVm(); startVmExpungeTask(); } private synchronized void startVmExpungeTask() { if (expungeVmTask != null) { expungeVmTask.cancel(true); } expungeVmTask = thdf.submitCancelablePeriodicTask(new CancelablePeriodicTask() { private List<Tuple> getVmDeletedStateManagedByUs() { int qun = 10000; SimpleQuery q = dbf.createQuery(VmInstanceVO.class); q.add(VmInstanceVO_.state, Op.EQ, VmInstanceState.Destroyed); long amount = q.count(); int times = (int) (amount / qun) + (amount % qun != 0 ? 1 : 0); int start = 0; List<Tuple> ret = new ArrayList<>(); for (int i = 0; i < times; i++) { q = dbf.createQuery(VmInstanceVO.class); q.select(VmInstanceVO_.uuid, VmInstanceVO_.lastOpDate); q.add(VmInstanceVO_.state, Op.EQ, VmInstanceState.Destroyed); q.setLimit(qun); q.setStart(start); List<Tuple> ts = q.listTuple(); start += qun; for (Tuple t : ts) { String vmUuid = t.get(0, String.class); if (!destMaker.isManagedByUs(vmUuid)) { continue; } ret.add(t); } } return ret; } @Override public synchronized boolean run() { final List<Tuple> vms = getVmDeletedStateManagedByUs(); if (vms.isEmpty()) { logger.debug("[VM Expunging Task]: no vm to expunge"); return false; } final Timestamp current = dbf.getCurrentSqlTime(); final List<ExpungeVmMsg> msgs = CollectionUtils.transformToList(vms, new Function<ExpungeVmMsg, Tuple>() { @Override public ExpungeVmMsg call(Tuple t) { String uuid = t.get(0, String.class); Timestamp date = t.get(1, Timestamp.class); long end = date.getTime() + TimeUnit.SECONDS.toMillis(VmGlobalConfig.VM_EXPUNGE_PERIOD.value(Long.class)); if (current.getTime() >= end) { VmInstanceDeletionPolicy deletionPolicy = deletionPolicyMgr.getDeletionPolicy(uuid); if (deletionPolicy == VmInstanceDeletionPolicy.Never) { logger.debug(String.format("[VM Expunging Task]: the deletion policy of the vm[uuid:%s] is Never, don't expunge it", uuid)); return null; } else { ExpungeVmMsg msg = new ExpungeVmMsg(); msg.setVmInstanceUuid(uuid); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, uuid); return msg; } } else { return null; } } }); if (msgs.isEmpty()) { logger.debug("[VM Expunging Task]: no vm to expunge"); return false; } bus.send(msgs, 100, new CloudBusListCallBack(null) { @Override public void run(List<MessageReply> replies) { for (MessageReply r : replies) { ExpungeVmMsg msg = msgs.get(replies.indexOf(r)); if (!r.isSuccess()) { logger.warn(String.format("failed to expunge the vm[uuid:%s], %s", msg.getVmInstanceUuid(), r.getError())); } else { logger.debug(String.format("successfully expunged the vm[uuid:%s]", msg.getVmInstanceUuid())); } } } }); return false; } @Override public TimeUnit getTimeUnit() { return TimeUnit.SECONDS; } @Override public long getInterval() { return VmGlobalConfig.VM_EXPUNGE_INTERVAL.value(Long.class); } @Override public String getName() { return "expunge-vm-task"; } }); logger.debug(String.format("vm expunging task starts running, [period: %s seconds, interval: %s seconds]", VmGlobalConfig.VM_EXPUNGE_PERIOD.value(Long.class), VmGlobalConfig.VM_EXPUNGE_INTERVAL.value(Long.class))); } @Override public String preDeleteL3Network(L3NetworkInventory inventory) throws L3NetworkException { return null; } @Override public void beforeDeleteL3Network(L3NetworkInventory inventory) { } @Override public void afterDeleteL3Network(L3NetworkInventory inventory) { new StaticIpOperator().deleteStaticIpByL3NetworkUuid(inventory.getUuid()); } @Override public void resourceOwnerAfterChange(AccountResourceRefInventory ref, String newOwnerUuid) { if (!VmInstanceVO.class.getSimpleName().equals(ref.getResourceType())) { return; } // change root volume SimpleQuery<VmInstanceVO> q = dbf.createQuery(VmInstanceVO.class); q.select(VmInstanceVO_.rootVolumeUuid); q.add(VmInstanceVO_.uuid, Op.EQ, ref.getResourceUuid()); String rootVolumeUuid = q.findValue(); if (rootVolumeUuid == null) { return; } acntMgr.changeResourceOwner(rootVolumeUuid, newOwnerUuid); // change vmnic(s) SimpleQuery<VmNicVO> sq = dbf.createQuery(VmNicVO.class); sq.select(VmNicVO_.uuid); sq.add(VmNicVO_.vmInstanceUuid, Op.EQ, ref.getResourceUuid()); List<String> vmnics = sq.listValue(); if (vmnics.isEmpty()) { return; } for (String vmnicUuid : vmnics) { acntMgr.changeResourceOwner(vmnicUuid, newOwnerUuid); } } @Override public List<Class> getMessageClassToIntercept() { return asList(APIChangeResourceOwnerMsg.class); } @Override public InterceptorPosition getPosition() { return InterceptorPosition.END; } @Override public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException { if (msg instanceof APIChangeResourceOwnerMsg) { validateAPIChangeResourceOwnerMsg((APIChangeResourceOwnerMsg) msg); } return msg; } private void validateAPIChangeResourceOwnerMsg(APIChangeResourceOwnerMsg msg) { SimpleQuery<AccountResourceRefVO> q = dbf.createQuery(AccountResourceRefVO.class); q.add(AccountResourceRefVO_.resourceUuid, Op.EQ, msg.getResourceUuid()); AccountResourceRefVO ref = q.find(); if (ref == null || !VolumeVO.class.getSimpleName().equals(ref.getResourceType())) { return; } SimpleQuery<VolumeVO> vq = dbf.createQuery(VolumeVO.class); vq.add(VolumeVO_.uuid, Op.EQ, ref.getResourceUuid()); vq.add(VolumeVO_.type, Op.EQ, VolumeType.Root); if (vq.isExists()) { throw new OperationFailureException(operr("the resource[uuid:%s] is a ROOT volume, you cannot change its owner, instead," + "change the owner of the VM the root volume belongs to", ref.getResourceUuid())); } } @Override public void afterChangeHostStatus(String hostUuid, HostStatus before, HostStatus next) { if(next == HostStatus.Disconnected) { List<String> vmUuids = Q.New(VmInstanceVO.class).select(VmInstanceVO_.uuid) .eq(VmInstanceVO_.hostUuid, hostUuid) .listValues(); if(vmUuids.isEmpty()){ return; } FutureCompletion future = new FutureCompletion(null); new While<>(vmUuids).all((vmUuid, completion) -> { VmStateChangedOnHostMsg msg = new VmStateChangedOnHostMsg(); msg.setVmInstanceUuid(vmUuid); msg.setHostUuid(hostUuid); msg.setStateOnHost(VmInstanceState.Unknown); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vmUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if(!reply.isSuccess()){ N.New(VmInstanceVO.class, vmUuid).warn_("the host[uuid:%s] becomes Disconnected, but the vm[uuid:%s] fails to change it's state to Unknown, %s", hostUuid, vmUuid, reply.getError()); } else { N.New(VmInstanceVO.class, vmUuid).info_("the host[uuid:%s] becomes Disconnected, change the VM[uuid:%s]' state to Unknown", hostUuid, vmUuid); } completion.done(); } }); }).run(new NoErrorCompletion(future) { @Override public void done() { future.success(); } }); future.await(); } } }