diff --git a/compute/src/main/java/org/zstack/compute/vm/VmCreationSubManager.java b/compute/src/main/java/org/zstack/compute/vm/VmCreationSubManager.java new file mode 100644 index 0000000000..0ea341c055 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmCreationSubManager.java @@ -0,0 +1,1174 @@ +package org.zstack.compute.vm; + +import com.google.common.collect.Maps; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.zstack.compute.allocator.HostAllocatorManager; +import org.zstack.configuration.DiskOfferingSystemTags; +import org.zstack.configuration.InstanceOfferingSystemTags; +import org.zstack.configuration.OfferingUserConfigUtils; +import org.zstack.core.Platform; +import org.zstack.core.asyncbatch.While; +import org.zstack.core.cloudbus.CloudBus; +import org.zstack.core.cloudbus.CloudBusCallBack; +import org.zstack.core.componentloader.PluginRegistry; +import org.zstack.core.db.*; +import org.zstack.core.db.SimpleQuery.Op; +import org.zstack.core.jsonlabel.JsonLabel; +import org.zstack.core.thread.SingleFlightTask; +import org.zstack.core.thread.ThreadFacade; +import org.zstack.core.workflow.FlowChainBuilder; +import org.zstack.core.workflow.ShareFlow; +import org.zstack.header.allocator.AllocateHostDryRunReply; +import org.zstack.header.allocator.DesignatedAllocateHostMsg; +import org.zstack.header.allocator.HostAllocatorConstant; +import org.zstack.header.cluster.ClusterInventory; +import org.zstack.header.cluster.ClusterVO; +import org.zstack.header.configuration.*; +import org.zstack.header.configuration.userconfig.DiskOfferingUserConfig; +import org.zstack.header.configuration.userconfig.InstanceOfferingUserConfig; +import org.zstack.header.core.ReturnValueCompletion; +import org.zstack.header.core.workflow.*; +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.errorcode.ErrorCodeList; +import org.zstack.header.errorcode.OperationFailureException; +import org.zstack.header.host.HostInventory; +import org.zstack.header.image.*; +import org.zstack.header.image.ImageConstant.ImageMediaType; +import org.zstack.header.message.APICreateMessage; +import org.zstack.header.message.MessageReply; +import org.zstack.header.network.l3.*; +import org.zstack.header.storage.backup.BackupStorageInventory; +import org.zstack.header.storage.backup.BackupStorageType; +import org.zstack.header.storage.backup.BackupStorageVO; +import org.zstack.header.storage.backup.BackupStorageVO_; +import org.zstack.header.storage.primary.*; +import org.zstack.header.tag.SystemTagCreator; +import org.zstack.header.vm.*; +import org.zstack.header.vm.VmInstanceConstant.VmOperation; +import org.zstack.header.vm.VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy; +import org.zstack.header.volume.VolumeType; +import org.zstack.header.zone.ZoneInventory; +import org.zstack.header.zone.ZoneVO; +import org.zstack.tag.TagManager; +import org.zstack.utils.CollectionUtils; +import org.zstack.utils.Utils; +import org.zstack.identity.AccountManager; +import org.zstack.utils.logging.CLogger; + +import javax.persistence.Tuple; +import javax.persistence.TypedQuery; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; +import static org.zstack.core.Platform.*; +import static org.zstack.utils.CollectionDSL.*; +import static org.zstack.utils.CollectionUtils.merge; +import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*; + +/** + * Handles VM creation and candidate resource query API messages. + * Extracted from VmInstanceManagerImpl to reduce God Class complexity. + */ +public class VmCreationSubManager { + private static final CLogger logger = Utils.getLogger(VmCreationSubManager.class); + + @Autowired + private CloudBus bus; + @Autowired + private DatabaseFacade dbf; + @Autowired + private PluginRegistry pluginRgty; + @Autowired + private TagManager tagMgr; + @Autowired + private HostAllocatorManager hostAllocatorMgr; + @Autowired + protected VmInstanceExtensionPointEmitter extEmitter; + @Autowired + private ThreadFacade thdf; + @Autowired + private VmFactoryManager vmFactoryManager; + @Autowired + private AccountManager acntMgr; + + void handle(final CreateVmInstanceMsg msg) { + if (msg.getZoneUuid() == null && !CollectionUtils.isEmpty(msg.getL3NetworkSpecs())) { + String l3Uuid = VmNicSpec.getL3UuidsOfSpec(msg.getL3NetworkSpecs()).get(0); + String zoneUuid = Q.New(L3NetworkVO.class) + .select(L3NetworkVO_.zoneUuid) + .eq(L3NetworkVO_.uuid, l3Uuid) + .findValue(); + msg.setZoneUuid(zoneUuid); + } + + doCreateVmInstance(msg, null, new ReturnValueCompletion(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); + } + }); + } + + void handle(final APICreateVmInstanceMsg msg) { + doCreateVmInstance(VmInstanceUtils.fromAPICreateVmInstanceMsg(msg), msg, new ReturnValueCompletion(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); + } + }); + } + + void handle(final APIGetVmsCapabilitiesMsg msg) { + APIGetVmsCapabilitiesReply reply = new APIGetVmsCapabilitiesReply(); + Map vmsCaps = Maps.newConcurrentMap(); + msg.getVmUuids() + .parallelStream() + .forEach(v -> { + vmsCaps.put(v, new VmCapabilitiesJudger().judge(v)); + }); + + reply.setVmsCaps(vmsCaps); + bus.reply(msg, reply); + } + + void handle(final APIUpdatePriorityConfigMsg msg) { + final APIUpdatePriorityConfigEvent evt = new APIUpdatePriorityConfigEvent(msg.getId()); + + VmPriorityOperator.PriorityStruct struct = new VmPriorityOperator.PriorityStruct(); + struct.setCpuShares(msg.getCpuShares()); + struct.setOomScoreAdj(msg.getOomScoreAdj()); + VmPriorityConfigVO vmPriorityConfigVO = new VmPriorityOperator().updatePriorityConfig(msg.getUuid(), struct); + if (vmPriorityConfigVO != null) { + for (UpdatePriorityConfigExtensionPoint exp : pluginRgty.getExtensionList(UpdatePriorityConfigExtensionPoint.class)) { + exp.afterUpdatePriorityConfig(vmPriorityConfigVO); + } + } + bus.publish(evt); + } + + void handle(APIGetSpiceCertificatesMsg msg) { + APIGetSpiceCertificatesReply reply = new APIGetSpiceCertificatesReply(); + String certificateStr = new JsonLabel().get("spiceCA", String.class); + if (StringUtils.isNotEmpty(certificateStr)) { + reply.setCertificateStr(certificateStr); + } else { + reply.setError(operr(ORG_ZSTACK_COMPUTE_VM_10232, "Spice certificate does not exist, Please check if spice tls is enabled")); + } + bus.reply(msg, reply); + } + + @Transactional(readOnly = true) + 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 q = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); + q.setParameter("isoUuid", msg.getIsoUuid()); + List bss = q.getResultList(); + if (bss.isEmpty()) { + reply.setInventories(new ArrayList<>()); + bus.reply(msg, reply); + return; + } + + List psUuids = new ArrayList<>(); + List psTypes = new ArrayList<>(); + for (BackupStorageVO bs : bss) { + BackupStorageType bsType = BackupStorageType.valueOf(bs.getType()); + List lst = bsType.findRelatedPrimaryStorage(bs.getUuid()); + if (lst != null) { + psUuids.addAll(lst); + } else { + psTypes.addAll(hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics(bs.getType())); + } + } + + List 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 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 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()); + } + + List result = VmInstanceInventory.valueOf(vms); + + for (VmAttachIsoExtensionPoint ext : pluginRgty.getExtensionList(VmAttachIsoExtensionPoint.class)) { + ext.filtCandidateVms(msg.getIsoUuid(), result); + } + reply.setInventories(result); + bus.reply(msg, reply); + } + + void handle(APIGetInterdependentL3NetworksImagesMsg msg) { + final String accountUuid = msg.getSession().getAccountUuid(); + if (msg.getImageUuid() != null) { + thdf.singleFlightSubmit(new SingleFlightTask(msg) + .setSyncSignature(String.format("get-interdependent-l3-by-image-%s-in-zone-%s", + msg.getImageUuid(), + msg.getZoneUuid())) + .run((completion) -> completion.success(getInterdependentL3NetworksByImageUuid(msg, accountUuid))) + .done(((result) -> { + APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); + if (!result.isSuccess()) { + reply.setError(result.getErrorCode()); + } else { + reply.setInventories((List) result.getResult()); + } + + bus.reply(msg, reply); + }))); + } else if (msg.getL3NetworkUuids() != null) { + getInterdependentImagesByL3NetworkUuids(msg); + } else { + thdf.singleFlightSubmit(new SingleFlightTask(msg) + .setSyncSignature(String.format("get-interdependent-l3-by-zone-%s", + msg.getZoneUuid())) + .run((completion) -> completion.success(getInterdependentL3NetworksByImageUuid(msg, accountUuid))) + .done(((result) -> { + APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); + if (!result.isSuccess()) { + reply.setError(result.getErrorCode()); + } else { + reply.setInventories((List) result.getResult()); + } + + bus.reply(msg, reply); + }))); + } + } + + void handle(APIGetInterdependentL3NetworksBackupStoragesMsg msg) { + final String accountUuid = msg.getSession().getAccountUuid(); + APIGetInterdependentL3NetworksBackupStoragesReply reply = + new APIGetInterdependentL3NetworksBackupStoragesReply(); + if (msg.getBackupStorageUuid() != null) { + BackupStorageVO bsvo = Q.New(BackupStorageVO.class) + .eq(BackupStorageVO_.uuid, msg.getBackupStorageUuid()) + .find(); + if (bsvo == null) { + reply.setInventories(new ArrayList<>()); + } else { + reply.setInventories(getInterdependentL3NetworksByBackupStorageUuids(Collections.singletonList(bsvo), + msg.getZoneUuid(), accountUuid, false)); + } + } else { + reply.setInventories(getInterdependentBackupStoragesByL3NetworkUuids(msg.getL3NetworkUuids())); + } + + bus.reply(msg, reply); + } + + void handle(APIGetCandidateZonesClustersHostsForCreatingVmMsg msg) { + DesignatedAllocateHostMsg amsg = new DesignatedAllocateHostMsg(); + + ImageVO image = dbf.findByUuid(msg.getImageUuid(), ImageVO.class); + amsg.setImage(ImageInventory.valueOf(image)); + amsg.setZoneUuid(msg.getZoneUuid()); + amsg.setClusterUuid(msg.getClusterUuid()); + + InstanceOfferingVO insvo = null; + if (msg.getInstanceOfferingUuid() == null) { + amsg.setCpuCapacity(msg.getCpuNum()); + amsg.setMemoryCapacity(msg.getMemorySize()); + } else { + insvo = dbf.findByUuid(msg.getInstanceOfferingUuid(), InstanceOfferingVO.class); + amsg.setCpuCapacity(insvo.getCpuNum()); + amsg.setMemoryCapacity(insvo.getMemorySize()); + } + + long diskSize = 0; + List diskOfferings = new ArrayList<>(); + if (msg.getDataDiskOfferingUuids() != null) { + SimpleQuery dq = dbf.createQuery(DiskOfferingVO.class); + dq.add(DiskOfferingVO_.uuid, Op.IN, msg.getDataDiskOfferingUuids()); + List dvos = dq.list(); + diskOfferings.addAll(DiskOfferingInventory.valueOf(dvos)); + } + + if (image.getMediaType() == ImageMediaType.ISO) { + if (msg.getRootDiskOfferingUuid() == null) { + diskSize = msg.getRootDiskSize(); + } else { + 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(ORG_ZSTACK_COMPUTE_VM_10237, "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.setImageUuid(image.getUuid()); + if (insvo == null) { + vm.setCpuNum(msg.getCpuNum()); + vm.setMemorySize(msg.getMemorySize()); + } else { + vm.setInstanceOfferingUuid(insvo.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); + if (msg.getSystemTags() != null && !msg.getSystemTags().isEmpty()) { + amsg.setSystemTags(new ArrayList(msg.getSystemTags())); + } + + 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()); + + Set clusterUuids = re.getHosts().stream(). + map(HostInventory::getClusterUuid).collect(Collectors.toSet()); + List clusters = ClusterInventory.valueOf(dbf.listByPrimaryKeys(clusterUuids, ClusterVO.class)); + areply.setClusters(clusters); + + Set zoneUuids = clusters.stream(). + map(ClusterInventory::getZoneUuid).collect(Collectors.toSet()); + 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); + } + }); + } + + void handle(APIGetCandidatePrimaryStoragesForCreatingVmMsg msg) { + APIGetCandidatePrimaryStoragesForCreatingVmReply reply = new APIGetCandidatePrimaryStoragesForCreatingVmReply(); + List msgs = new ArrayList<>(); + + Set psTypes = new HashSet<>(); + List clusterUuids = new ArrayList<>(); + List dataOfferings = new ArrayList<>(); + ImageInventory imageInv = new SQLBatchWithReturn() { + + @Override + protected ImageInventory scripts() { + List dataOfferingUuids = msg.getDataDiskOfferingUuids() == null ? new ArrayList<>() : + msg.getDataDiskOfferingUuids(); + + sql("select bs.type from BackupStorageVO bs, ImageBackupStorageRefVO ref" + + " where ref.imageUuid =:imageUuid" + + " and bs.uuid = ref.backupStorageUuid", String.class) + .param("imageUuid", msg.getImageUuid()) + .list().forEach(it -> + psTypes.addAll(hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics((String) it) + )); + + clusterUuids.addAll(sql("select distinct ref.clusterUuid" + + " from L2NetworkClusterRefVO ref, L3NetworkVO l3" + + " where l3.uuid in (:l3Uuids)" + + " and ref.l2NetworkUuid = l3.l2NetworkUuid", String.class) + .param("l3Uuids", msg.getL3NetworkUuids()) + .list()); + + for (String diskUuid : dataOfferingUuids) { + dataOfferings.add(DiskOfferingInventory.valueOf( + (DiskOfferingVO) q(DiskOfferingVO.class) + .eq(DiskOfferingVO_.uuid, diskUuid) + .find() + )); + } + + ImageVO imageVO = q(ImageVO.class).eq(ImageVO_.uuid, msg.getImageUuid()).find(); + return ImageInventory.valueOf(imageVO); + } + }.execute(); + + // allocate ps for root volume + AllocatePrimaryStorageMsg rmsg = new AllocatePrimaryStorageMsg(); + rmsg.setDryRun(true); + rmsg.setImageUuid(msg.getImageUuid()); + rmsg.setRequiredClusterUuids(clusterUuids); + if (ImageMediaType.ISO.toString().equals(imageInv.getMediaType())) { + if (msg.getRootDiskOfferingUuid() == null) { + rmsg.setSize(msg.getRootDiskSize()); + } else { + Tuple t = Q.New(DiskOfferingVO.class).eq(DiskOfferingVO_.uuid, msg.getRootDiskOfferingUuid()) + .select(DiskOfferingVO_.diskSize, DiskOfferingVO_.allocatorStrategy).findTuple(); + + rmsg.setSize((long) t.get(0)); + rmsg.setAllocationStrategy((String) t.get(1)); + rmsg.setDiskOfferingUuid(msg.getRootDiskOfferingUuid()); + } + } else { + rmsg.setSize(imageInv.getSize()); + } + + if (msg.getRootDiskOfferingUuid() != null && DiskOfferingSystemTags.DISK_OFFERING_USER_CONFIG.hasTag(msg.getRootDiskOfferingUuid())) { + DiskOfferingUserConfig config = OfferingUserConfigUtils.getDiskOfferingConfig(msg.getRootDiskOfferingUuid(), DiskOfferingUserConfig.class); + if (config.getAllocate() != null && config.getAllocate().getPrimaryStorage() != null) { + String psUuid = config.getAllocate().getPrimaryStorage().getUuid(); + rmsg.setRequiredPrimaryStorageUuid(psUuid); + } + } + + if (msg.getInstanceOfferingUuid() != null && InstanceOfferingSystemTags.INSTANCE_OFFERING_USER_CONFIG.hasTag(msg.getInstanceOfferingUuid())) { + InstanceOfferingUserConfig config = OfferingUserConfigUtils.getInstanceOfferingConfig(msg.getInstanceOfferingUuid(), InstanceOfferingUserConfig.class); + if (config.getAllocate() != null && config.getAllocate().getPrimaryStorage() != null) { + String psUuid = config.getAllocate().getPrimaryStorage().getUuid(); + rmsg.setRequiredPrimaryStorageUuid(psUuid); + } + } + + rmsg.setPurpose(PrimaryStorageAllocationPurpose.CreateNewVm.toString()); + rmsg.setPossiblePrimaryStorageTypes(new ArrayList<>(psTypes)); + bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID); + msgs.add(rmsg); + + // allocate ps for data volumes + for (DiskOfferingInventory dinv : dataOfferings) { + AllocatePrimaryStorageMsg amsg = new AllocatePrimaryStorageMsg(); + amsg.setDryRun(true); + amsg.setSize(dinv.getDiskSize()); + amsg.setRequiredClusterUuids(clusterUuids); + amsg.setAllocationStrategy(dinv.getAllocatorStrategy()); + amsg.setDiskOfferingUuid(dinv.getUuid()); + if (DiskOfferingSystemTags.DISK_OFFERING_USER_CONFIG.hasTag(dinv.getUuid())) { + DiskOfferingUserConfig config = OfferingUserConfigUtils.getDiskOfferingConfig(dinv.getUuid(), DiskOfferingUserConfig.class); + if (config.getAllocate() != null && config.getAllocate().getPrimaryStorage() != null) { + String psUuid = config.getAllocate().getPrimaryStorage().getUuid(); + amsg.setRequiredPrimaryStorageUuid(psUuid); + } + } + + bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID); + msgs.add(amsg); + } + + if (msg.getDataDiskSizes() != null) { + for (Long size : msg.getDataDiskSizes()) { + AllocatePrimaryStorageMsg amsg = new AllocatePrimaryStorageMsg(); + amsg.setDryRun(true); + amsg.setSize(size); + amsg.setRequiredClusterUuids(clusterUuids); + bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID); + msgs.add(amsg); + } + } + + new While<>(msgs).all((amsg, completion) -> { + bus.send(amsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply r) { + if (r.isSuccess()) { + AllocatePrimaryStorageDryRunReply re = r.castReply(); + if (amsg.getImageUuid() != null) { + reply.setRootVolumePrimaryStorages(re.getPrimaryStorageInventories()); + } else if (amsg.getDiskOfferingUuid() != null) { + reply.getDataVolumePrimaryStorages().put(amsg.getDiskOfferingUuid(), re.getPrimaryStorageInventories()); + } else { + reply.getDataVolumePrimaryStorages().put(String.valueOf(amsg.getSize()), re.getPrimaryStorageInventories()); + } + } + completion.done(); + } + }); + + }).run(new WhileDoneCompletion(msg) { + @Override + public void done(ErrorCodeList errorCodeList) { + bus.reply(msg, reply); + } + }); + } + + // --- Private helper methods --- + + private List listIntersection(List a, List b) { + List 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 List getInterdependentBackupStoragesByL3NetworkUuids(List l3s) { + List> bss = new ArrayList<>(); + for (String l3uuid : l3s) { + 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 psq = dbf.getEntityManager().createQuery(sql, PrimaryStorageVO.class); + psq.setParameter("l3uuid", l3uuid); + List pss = psq.getResultList(); + + List lst = new ArrayList<>(); + for (PrimaryStorageVO ps : pss) { + PrimaryStorageType psType = PrimaryStorageType.valueOf(ps.getType()); + List bsUuids = psType.findBackupStorage(ps.getUuid()); + + if (!bsUuids.isEmpty()) { + // the primary storage has bound backup storage, e.g. ceph + sql = "select bs from BackupStorageVO bs where bs.uuid in (:uuids)"; + TypedQuery 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 selectedBss = new ArrayList<>(); + for (List lst : bss) { + selectedBss.addAll(lst); + } + + for (List l : bss) { + selectedBss = listIntersection(selectedBss, l); + } + + return BackupStorageInventory.valueOf(selectedBss); + } + + @Transactional(readOnly = true) + private void getInterdependentImagesByL3NetworkUuids(APIGetInterdependentL3NetworksImagesMsg msg) { + APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); + + List bss = getInterdependentBackupStoragesByL3NetworkUuids(msg.getL3NetworkUuids()); + + if (bss.isEmpty()) { + reply.setInventories(new ArrayList<>()); + bus.reply(msg, reply); + return; + } + + List bsUuids = bss.stream().map(BackupStorageInventory::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 iq = dbf.getEntityManager().createQuery(sql, ImageVO.class); + iq.setParameter("uuids", bsUuids); + iq.setParameter("zoneUuid", msg.getZoneUuid()); + List vos = iq.getResultList(); + reply.setInventories(ImageInventory.valueOf(vos)); + bus.reply(msg, reply); + } + + @Transactional(readOnly = true) + private List getInterdependentL3NetworksByImageUuid(APIGetInterdependentL3NetworksImagesMsg msg, String accountUuid) { + List bss = null; + if (msg.getImageUuid() != null) { + 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 bsq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); + bsq.setParameter("imgUuid", msg.getImageUuid()); + bsq.setParameter("zoneUuid", msg.getZoneUuid()); + bss = bsq.getResultList(); + } else { + String sql = "select bs" + + " from BackupStorageVO bs, BackupStorageZoneRefVO zref" + + " where bs.uuid = zref.backupStorageUuid" + + " and zref.zoneUuid = :zoneUuid"; + TypedQuery bsq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); + bsq.setParameter("zoneUuid", msg.getZoneUuid()); + bss = bsq.getResultList(); + } + + if (bss.isEmpty()) { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10233, "the image[uuid:%s] is not on any backup storage that has been attached to the zone[uuid:%s]", + msg.getImageUuid(), msg.getZoneUuid())); + } + + List l3s = getInterdependentL3NetworksByBackupStorageUuids(bss, msg.getZoneUuid(), accountUuid, msg.getRaiseException()); + + List bsUuids = bss.stream().map(BackupStorageVO::getUuid).collect(Collectors.toList()); + for (GetInterdependentL3NetworksExtensionPoint ext : pluginRgty.getExtensionList(GetInterdependentL3NetworksExtensionPoint.class)) { + l3s = ext.afterFilterByImage(l3s, bsUuids, msg.getImageUuid()); + } + + return l3s; + } + + @Transactional(readOnly = true) + private List getInterdependentL3NetworksByBackupStorageUuids(List bss, String zoneUuid, String accountUuid, boolean raiseException) { + List psUuids = new ArrayList<>(); + List l3s = new ArrayList<>(); + for (BackupStorageVO bs : bss) { + BackupStorageType bsType = BackupStorageType.valueOf(bs.getType()); + List relatedPrimaryStorageUuids = bsType.findRelatedPrimaryStorage(bs.getUuid()); + if (relatedPrimaryStorageUuids == null) { + List psTypes = hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics(bs.getType()); + psUuids.addAll(Q.New(PrimaryStorageVO.class) + .select(PrimaryStorageVO_.uuid) + .in(PrimaryStorageVO_.type, psTypes) + .eq(PrimaryStorageVO_.zoneUuid, zoneUuid) + .listValues()); + l3s.addAll(SQL.New("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") + .param("psTypes", psTypes) + .param("zoneUuid", zoneUuid) + .list()); + } else if (!relatedPrimaryStorageUuids.isEmpty()) { + psUuids.addAll(Q.New(PrimaryStorageVO.class) + .select(PrimaryStorageVO_.uuid) + .in(PrimaryStorageVO_.uuid, relatedPrimaryStorageUuids) + .eq(PrimaryStorageVO_.zoneUuid, zoneUuid) + .listValues()); + l3s.addAll(SQL.New("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") + .param("psUuids", relatedPrimaryStorageUuids) + .param("zoneUuid", zoneUuid) + .list()); + } 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())); + } + } + + if (l3s.isEmpty()) { + if (psUuids.isEmpty()) { + if (raiseException) { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10234, "no primary storage accessible to the backup storage[uuid:%s, type:%s] is found", + bss.get(0).getUuid(), bss.get(0).getType())); + } + logger.warn(String.format("no primary storage accessible to the backup storage[uuid:%s, type:%s] is found", + bss.get(0).getUuid(), bss.get(0).getType())); + return new ArrayList<>(); + } + + Long clusterNum = SQL.New("select count(distinct cl)" + + " from ClusterVO cl, PrimaryStorageClusterRefVO psref, PrimaryStorageVO ps" + + " where cl.uuid = psref.clusterUuid" + + " and psref.primaryStorageUuid in (:psUuids)" + + " and ps.zoneUuid = cl.zoneUuid" + + " and cl.zoneUuid = :zoneUuid" + + " group by cl.uuid", Long.class) + .param("psUuids", psUuids) + .param("zoneUuid", zoneUuid) + .find(); + + if (clusterNum == null || clusterNum == 0) { + if (raiseException) { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10235, "the primary storages[uuids:%s] has not attached any cluster on the zone[uuid:%s]", + psUuids, zoneUuid)); + } + logger.warn(String.format("the primary storages[uuids:%s] has not attached any cluster on the zone[uuid:%s]", psUuids, zoneUuid)); + return new ArrayList<>(); + } + + Long l2Num = SQL.New("select count(distinct l2)" + + " from L2NetworkVO l2, L2NetworkClusterRefVO l2ref, PrimaryStorageClusterRefVO psref, PrimaryStorageVO ps" + + " where l2.uuid = l2ref.l2NetworkUuid" + + " and psref.primaryStorageUuid in (:psUuids)" + + " and l2ref.clusterUuid = psref.clusterUuid" + + " and ps.zoneUuid = l2.zoneUuid" + + " and l2.zoneUuid = :zoneUuid", Long.class) + .param("psUuids", psUuids) + .param("zoneUuid", zoneUuid) + .find(); + if (l2Num == null || l2Num == 0) { + if (raiseException) { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10236, "no l2Networks found in clusters that have attached to primary storages[uuids:%s]", + psUuids)); + } + logger.warn(String.format("no l2Networks found in clusters that have attached to primary storages[uuids:%s]", psUuids)); + return new ArrayList<>(); + } + } + + List l3UuidListOfCurrentAccount; + if (!org.zstack.header.identity.AccountConstant.isAdminPermission(accountUuid)) { + l3UuidListOfCurrentAccount = acntMgr.getResourceUuidsCanAccessByAccount(accountUuid, L3NetworkVO.class); + } else { + l3UuidListOfCurrentAccount = null; + } + + if (l3UuidListOfCurrentAccount == null) { + return L3NetworkInventory.valueOf(l3s); + } + return L3NetworkInventory.valueOf(l3s.stream() + .filter(vo -> l3UuidListOfCurrentAccount.contains(vo.getUuid())) + .collect(Collectors.toList())); + } + + protected void doCreateVmInstance(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, ReturnValueCompletion completion) { + pluginRgty.getExtensionList(VmInstanceCreateExtensionPoint.class).forEach(extensionPoint -> { + extensionPoint.preCreateVmInstance(msg); + }); + + final ImageVO image = Q.New(ImageVO.class).eq(ImageVO_.uuid, msg.getImageUuid()).find(); + 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.setImageUuid(msg.getImageUuid()); + vo.setInstanceOfferingUuid(msg.getInstanceOfferingUuid()); + vo.setState(VmInstanceState.Created); + vo.setZoneUuid(msg.getZoneUuid()); + vo.setInternalId(dbf.generateSequenceNumber(VmInstanceSequenceNumberVO.class)); + vo.setDefaultL3NetworkUuid(msg.getDefaultL3NetworkUuid()); + vo.setCpuNum(msg.getCpuNum()); + vo.setCpuSpeed(msg.getCpuSpeed()); + vo.setMemorySize(msg.getMemorySize()); + vo.setReservedMemorySize(msg.getReservedMemorySize()); + vo.setAllocatorStrategy(msg.getAllocatorStrategy()); + vo.setPlatform(msg.getPlatform() != null ? msg.getPlatform() : image.getPlatform().toString()); + vo.setGuestOsType(msg.getGuestOsType() != null ? msg.getGuestOsType() : image.getGuestOsType()); + vo.setArchitecture(msg.getArchitecture() != null ? msg.getArchitecture() : image.getArchitecture()); + String vmType = msg.getType() == null ? VmInstanceConstant.USER_VM_TYPE : msg.getType(); + VmInstanceType type = VmInstanceType.valueOf(vmType); + VmInstanceFactory factory = vmFactoryManager.getVmInstanceFactory(type.toString()); + + VmInstanceVO finalVo = vo; + vo = new SQLBatchWithReturn() { + @Override + protected VmInstanceVO scripts() { + finalVo.setAccountUuid(msg.getAccountUuid()); + factory.createVmInstance(finalVo, msg); + + return reload(finalVo); + } + }.execute(); + + FlowChain chain = FlowChainBuilder.newShareFlowChain(); + chain.setName(String.format("do-create-vmInstance-%s", vo.getUuid())); + chain.then(new ShareFlow() { + VmInstanceInventory instantiateVm; + List otherDisks = new ArrayList<>(); + boolean attachOtherDisk = false; + + @Override + public void setup() { + if (!CollectionUtils.isEmpty(msg.getDiskAOs())) { + otherDisks = msg.getDiskAOs().stream().filter(diskAO -> !diskAO.isBoot()).collect(Collectors.toList()); + setDiskAOsName(otherDisks); + attachOtherDisk = !otherDisks.isEmpty(); + } + + flow(new Flow() { + List errorCodes = new ArrayList<>(); + String __name__ = String.format("instantiate-systemTag-for-vm-%s", finalVo.getUuid()); + + @Override + public void run(FlowTrigger trigger, Map data) { + try { + instantiateTagsForCreateMessage(msg, cmsg, finalVo); + } catch (Exception e) { + errorCodes.add(operr(ORG_ZSTACK_COMPUTE_VM_10240, "instantiate system tag for vm failed because %s", e.getMessage())); + } + if (!errorCodes.isEmpty()) { + trigger.fail(errorCodes.get(0)); + return; + } + + errorCodes = extEmitterHandleSystemTag(msg, cmsg, finalVo); + if (!errorCodes.isEmpty()) { + trigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10241, "handle system tag fail when creating vm because [%s]", + StringUtils.join(errorCodes.stream().map(ErrorCode::getDescription).collect(Collectors.toList()), + ", "))); + return; + } + trigger.next(); + } + + @Override + public void rollback(FlowRollback trigger, Map data) { + if (Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, finalVo.getUuid()).isExists()) { + dbf.removeByPrimaryKey(finalVo.getUuid(), VmInstanceVO.class); + } + trigger.rollback(); + } + }); + + flow(new Flow() { + List errorCodes = Collections.emptyList(); + String __name__ = String.format("instantiate-ssh-key-pair-for-vm-%s", finalVo.getUuid()); + + @Override + public void run(FlowTrigger trigger, Map data) { + errorCodes = extEmitterHandleSshKeyPair(msg, cmsg, finalVo); + if (!errorCodes.isEmpty()) { + trigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10242, "handle sshkeypair fail when creating vm because [%s]", + StringUtils.join(errorCodes.stream().map(ErrorCode::getDetails).collect(Collectors.toList()), + ", "))); + return; + } + trigger.next(); + } + + @Override + public void rollback(FlowRollback trigger, Map data) { + if (Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, finalVo.getUuid()).isExists()) { + dbf.removeByPrimaryKey(finalVo.getUuid(), VmInstanceVO.class); + } + trigger.rollback(); + } + }); + + flow(new Flow() { + String __name__ = "instantiate-new-created-vmInstance"; + + @Override + public void run(FlowTrigger trigger, Map data) { + InstantiateNewCreatedVmInstanceMsg smsg = new InstantiateNewCreatedVmInstanceMsg(); + smsg.setDisableL3Networks(msg.getDisableL3Networks()); + smsg.setHostUuid(msg.getHostUuid()); + List temporaryDiskOfferingUuids = createDiskOfferingUuidsFromDataDiskSizes(msg, finalVo.getUuid()); + smsg.setDataDiskOfferingUuids(merge(msg.getDataDiskOfferingUuids(), temporaryDiskOfferingUuids)); + smsg.setDataVolumeTemplateUuids(msg.getDataVolumeTemplateUuids()); + smsg.setDataVolumeFromTemplateSystemTags(msg.getDataVolumeFromTemplateSystemTags()); + smsg.setL3NetworkUuids(msg.getL3NetworkSpecs()); + + if (msg.getRootDiskOfferingUuid() != null) { + smsg.setRootDiskOfferingUuid(msg.getRootDiskOfferingUuid()); + } else if (msg.getRootDiskSize() > 0) { + DiskOfferingVO dvo = getDiskOfferingVO(); + dbf.persist(dvo); + smsg.setRootDiskOfferingUuid(dvo.getUuid()); + temporaryDiskOfferingUuids.add(dvo.getUuid()); + } + + smsg.setVmInstanceInventory(VmInstanceInventory.valueOf(finalVo)); + smsg.setCandidatePrimaryStorageUuidsForDataVolume(msg.getCandidatePrimaryStorageUuidsForDataVolume()); + smsg.setCandidatePrimaryStorageUuidsForRootVolume(msg.getCandidatePrimaryStorageUuidsForRootVolume()); + if (Objects.equals(msg.getStrategy(), VmCreationStrategy.InstantStart.toString()) && attachOtherDisk) { + smsg.setStrategy(VmCreationStrategy.CreateStopped.toString()); + } else { + smsg.setStrategy(msg.getStrategy()); + } + + smsg.setTimeout(msg.getTimeout()); + smsg.setRootVolumeSystemTags(msg.getRootVolumeSystemTags()); + smsg.setDataVolumeSystemTags(msg.getDataVolumeSystemTags()); + smsg.setDataVolumeSystemTagsOnIndex(msg.getDataVolumeSystemTagsOnIndex()); + smsg.setDiskAOs(msg.getDiskAOs()); + bus.makeTargetServiceIdByResourceUuid(smsg, VmInstanceConstant.SERVICE_ID, finalVo.getUuid()); + bus.send(smsg, new CloudBusCallBack(smsg) { + @Override + public void run(MessageReply reply) { + if (!temporaryDiskOfferingUuids.isEmpty()) { + dbf.removeByPrimaryKeys(temporaryDiskOfferingUuids, DiskOfferingVO.class); + } + + if (reply.isSuccess()) { + InstantiateNewCreatedVmInstanceReply r = (InstantiateNewCreatedVmInstanceReply) reply; + instantiateVm = r.getVmInventory(); + data.put(VmInstanceInventory.class.getSimpleName(), instantiateVm); + trigger.next(); + return; + } + trigger.fail(reply.getError()); + } + }); + } + + @NotNull + private DiskOfferingVO getDiskOfferingVO() { + DiskOfferingVO dvo = new DiskOfferingVO(); + dvo.setUuid(Platform.getUuid()); + dvo.setAccountUuid(msg.getAccountUuid()); + dvo.setDiskSize(msg.getRootDiskSize()); + dvo.setName("for-create-vm-" + finalVo.getUuid()); + dvo.setType("TemporaryDiskOfferingType"); + dvo.setState(DiskOfferingState.Enabled); + dvo.setAllocatorStrategy(PrimaryStorageConstant.DEFAULT_PRIMARY_STORAGE_ALLOCATION_STRATEGY_TYPE); + return dvo; + } + + @Override + public void rollback(FlowRollback chain, Map data) { + if (instantiateVm == null) { + chain.rollback(); + return; + } + DestroyVmInstanceMsg dmsg = new DestroyVmInstanceMsg(); + dmsg.setVmInstanceUuid(finalVo.getUuid()); + dmsg.setDeletionPolicy(VmInstanceDeletionPolicy.Direct); + bus.makeTargetServiceIdByResourceUuid(dmsg, VmInstanceConstant.SERVICE_ID, finalVo.getUuid()); + bus.send(dmsg, new CloudBusCallBack(null) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + logger.warn(String.format("failed to delete vm [%s]", instantiateVm.getUuid())); + } + chain.rollback(); + } + }); + } + }); + + + if (!CollectionUtils.isEmpty(otherDisks)) { + otherDisks.forEach(diskAO -> flow(new VmInstantiateOtherDiskFlow(diskAO))); + } + + if (Objects.equals(msg.getStrategy(), VmCreationStrategy.InstantStart.toString()) && attachOtherDisk) { + // DEBT: NoRollbackFlow -- start VM after attaching other disks during creation. Tracked: TODO + flow(new NoRollbackFlow() { + String __name__ = "start-vm"; + + @Override + public void run(FlowTrigger trigger, Map data) { + StartVmInstanceMsg smsg = new StartVmInstanceMsg(); + smsg.setVmInstanceUuid(instantiateVm.getUuid()); + smsg.setHostUuid(instantiateVm.getLastHostUuid()); + bus.makeTargetServiceIdByResourceUuid(smsg, VmInstanceConstant.SERVICE_ID, finalVo.getUuid()); + bus.send(smsg, new CloudBusCallBack(trigger) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + trigger.fail(reply.getError()); + return; + } + trigger.next(); + } + }); + } + }); + } + + done(new FlowDoneHandler(completion) { + @Override + public void handle(Map data) { + completion.success(instantiateVm); + } + }); + + error(new FlowErrorHandler(completion) { + @Override + public void handle(ErrorCode errCode, Map data) { + completion.fail(errCode); + } + }); + } + + private void setDiskAOsName(List diskAOs) { + AtomicInteger count = new AtomicInteger(1); + diskAOs.stream().filter(diskAO -> diskAO.getSourceUuid() == null).filter(diskAO -> diskAO.getName() == null) + .forEach(diskAO -> { + diskAO.setName(String.format("DATA-for-%s-%d", finalVo.getName(), count.get())); + count.getAndIncrement(); + }); + } + }).start(); + } + + private List createDiskOfferingUuidsFromDataDiskSizes(final CreateVmInstanceMsg msg, String vmUuid) { + if (CollectionUtils.isEmpty(msg.getDataDiskSizes())) { + return new ArrayList(); + } + List diskOfferingUuids = new ArrayList<>(); + List diskOfferingVos = new ArrayList<>(); + List volumeSizes = msg.getDataDiskSizes().stream().distinct().collect(Collectors.toList()); + Map sizeDiskOfferingMap = new HashMap<>(); + for (Long size : volumeSizes) { + DiskOfferingVO dvo = new DiskOfferingVO(); + dvo.setUuid(Platform.getUuid()); + dvo.setAccountUuid(msg.getAccountUuid()); + dvo.setDiskSize(size); + dvo.setName(String.format("create-data-volume-for-vm-%s", vmUuid)); + dvo.setType("TemporaryDiskOfferingType"); + dvo.setState(DiskOfferingState.Enabled); + dvo.setAllocatorStrategy(PrimaryStorageConstant.DEFAULT_PRIMARY_STORAGE_ALLOCATION_STRATEGY_TYPE); + diskOfferingVos.add(dvo); + sizeDiskOfferingMap.put(size, dvo.getUuid()); + } + msg.getDataDiskSizes().forEach(size -> diskOfferingUuids.add(sizeDiskOfferingMap.get(size))); + dbf.persistCollection(diskOfferingVos); + return diskOfferingUuids; + } + + private void instantiateTagsForCreateMessage(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, VmInstanceVO finalVo) { + if (cmsg != null) { + tagMgr.createTagsFromAPICreateMessage(cmsg, finalVo.getUuid(), VmInstanceVO.class.getSimpleName()); + } else { + tagMgr.createTags(msg.getSystemTags(), msg.getUserTags(), finalVo.getUuid(), VmInstanceVO.class.getSimpleName()); + } + + boolean isVirtio = false; + if (!CollectionUtils.isEmpty(msg.getDiskAOs())) { + isVirtio = msg.getVirtio(); + } else { + if (Q.New(ImageVO.class).eq(ImageVO_.uuid, msg.getImageUuid()).eq(ImageVO_.virtio, true).isExists()) { + isVirtio = true; + } + } + if (isVirtio) { + SystemTagCreator creator = VmSystemTags.VIRTIO.newSystemTagCreator(finalVo.getUuid()); + creator.recreate = true; + creator.inherent = false; + creator.tag = VmSystemTags.VIRTIO.getTagFormat(); + creator.create(); + } + + if (finalVo.getInstanceOfferingUuid() != null) { + tagMgr.copySystemTag( + finalVo.getInstanceOfferingUuid(), + InstanceOfferingVO.class.getSimpleName(), + finalVo.getUuid(), + VmInstanceVO.class.getSimpleName(), false); + } + + if (msg.getImageUuid() != null) { + tagMgr.copySystemTag( + msg.getImageUuid(), + ImageVO.class.getSimpleName(), + finalVo.getUuid(), + VmInstanceVO.class.getSimpleName(), false); + } + + if (ImageArchitecture.aarch64.toString().equals(finalVo.getArchitecture())) { + SystemTagCreator creator = VmSystemTags.MACHINE_TYPE.newSystemTagCreator(finalVo.getUuid()); + creator.setTagByTokens(map(e(VmSystemTags.MACHINE_TYPE_TOKEN, VmMachineType.virt.toString()))); + creator.recreate = true; + creator.create(); + } + + SystemTagCreator creator = VmSystemTags.SYNC_PORTS.newSystemTagCreator(finalVo.getUuid()); + creator.recreate = true; + creator.setTagByTokens(map(e(VmSystemTags.SYNC_PORTS_TOKEN, finalVo.getUuid()))); + creator.create(); + } + + private List extEmitterHandleSystemTag(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, VmInstanceVO finalVo) { + List result = Collections.emptyList(); + if (msg == null) { + result.add(operr(ORG_ZSTACK_COMPUTE_VM_10238, "CreateVmInstanceMsg cannot be null")); + return result; + } else if (cmsg != null && cmsg.getSystemTags() != null && !cmsg.getSystemTags().isEmpty()) { + return extEmitter.handleSystemTag(finalVo.getUuid(), cmsg.getSystemTags()); + } else if (cmsg == null && msg.getSystemTags() != null && !msg.getSystemTags().isEmpty()) { + return extEmitter.handleSystemTag(finalVo.getUuid(), msg.getSystemTags()); + } + return result; + } + + private List extEmitterHandleSshKeyPair(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, VmInstanceVO finalVo) { + List result = Collections.emptyList(); + if (msg == null) { + result.add(operr(ORG_ZSTACK_COMPUTE_VM_10239, "CreateVmInstanceMsg cannot be null")); + return result; + } else if (msg.getSshKeyPairUuids() != null && !msg.getSshKeyPairUuids().isEmpty()) { + return extEmitter.associateSshKeyPair(finalVo.getUuid(), msg.getSshKeyPairUuids()); + } + return result; + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmExpungeSubManager.java b/compute/src/main/java/org/zstack/compute/vm/VmExpungeSubManager.java new file mode 100644 index 0000000000..b538584035 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmExpungeSubManager.java @@ -0,0 +1,202 @@ +package org.zstack.compute.vm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.core.cloudbus.CloudBus; +import org.zstack.core.cloudbus.CloudBusListCallBack; +import org.zstack.core.cloudbus.ResourceDestinationMaker; +import org.zstack.core.config.GlobalConfig; +import org.zstack.core.config.GlobalConfigUpdateExtensionPoint; +import org.zstack.core.db.DatabaseFacade; +import org.zstack.core.db.SimpleQuery; +import org.zstack.core.db.SimpleQuery.Op; +import org.zstack.core.thread.CancelablePeriodicTask; +import org.zstack.core.thread.ThreadFacade; +import org.zstack.header.Component; +import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint; +import org.zstack.header.message.MessageReply; +import org.zstack.header.vm.*; +import org.zstack.header.vm.VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +import javax.persistence.Tuple; +import java.sql.Timestamp; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static org.zstack.utils.CollectionUtils.transformToList; + +/** + * Manages VM expunge lifecycle — periodic task that expunges destroyed VMs + * after their retention period expires. + * Extracted from VmInstanceManagerImpl to reduce God Class complexity. + */ +public class VmExpungeSubManager implements ManagementNodeReadyExtensionPoint, Component { + private static final CLogger logger = Utils.getLogger(VmExpungeSubManager.class); + + @Autowired + private ThreadFacade thdf; + @Autowired + private DatabaseFacade dbf; + @Autowired + private VmInstanceDeletionPolicyManager deletionPolicyMgr; + @Autowired + private CloudBus bus; + @Autowired + private ResourceDestinationMaker destMaker; + + private Future expungeVmTask; + + @Override + public void managementNodeReady() { + startVmExpungeTask(); + } + + @Override + public boolean start() { + installGlobalConfigUpdater(); + return true; + } + + @Override + public boolean stop() { + return true; + } + + 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 synchronized void startVmExpungeTask() { + if (expungeVmTask != null) { + expungeVmTask.cancel(true); + } + + expungeVmTask = thdf.submitCancelablePeriodicTask(new CancelablePeriodicTask() { + + private List 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 ret = new java.util.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 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 vms = getVmDeletedStateManagedByUs(); + if (vms.isEmpty()) { + logger.debug("[VM Expunging Task]: no vm to expunge"); + return false; + } + + final Timestamp current = dbf.getCurrentSqlTime(); + + final List msgs = transformToList(vms, new org.zstack.utils.function.Function() { + @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 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))); + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmExtensionPointAdapter.java b/compute/src/main/java/org/zstack/compute/vm/VmExtensionPointAdapter.java new file mode 100644 index 0000000000..f7fe9c8c1a --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmExtensionPointAdapter.java @@ -0,0 +1,255 @@ +package org.zstack.compute.vm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.core.cloudbus.CloudBus; +import org.zstack.core.cloudbus.CloudBusCallBack; +import org.zstack.core.asyncbatch.While; +import org.zstack.core.db.*; +import org.zstack.core.db.SimpleQuery.Op; +import org.zstack.directory.ResourceDirectoryRefVO; +import org.zstack.header.core.NopeWhileDoneCompletion; +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.errorcode.ErrorCodeList; +import org.zstack.header.host.*; +import org.zstack.header.identity.AccountResourceRefInventory; +import org.zstack.header.identity.ResourceOwnerAfterChangeExtensionPoint; +import org.zstack.header.message.MessageReply; +import org.zstack.header.network.l3.*; +import org.zstack.header.tag.PatternedSystemTag; +import org.zstack.header.vm.*; +import org.zstack.header.vm.cdrom.VmCdRomVO; +import org.zstack.header.vm.cdrom.VmCdRomVO_; +import org.zstack.identity.AccountManager; +import org.zstack.resourceconfig.ResourceConfig; +import org.zstack.resourceconfig.ResourceConfigFacade; +import org.zstack.tag.SystemTagUtils; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; + +import javax.persistence.Tuple; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static java.util.Arrays.asList; + +/** + * Adapter for extension point implementations previously embedded in VmInstanceManagerImpl. + * Handles: L3NetworkDelete, ResourceOwnerAfterChange, AfterChangeHostStatus, + * VmInstanceMigrate, VmInstanceBeforeStart extension points. + * Extracted from VmInstanceManagerImpl to reduce God Class complexity. + */ +public class VmExtensionPointAdapter implements + L3NetworkDeleteExtensionPoint, + ResourceOwnerAfterChangeExtensionPoint, + AfterChangeHostStatusExtensionPoint, + VmInstanceMigrateExtensionPoint, + VmInstanceBeforeStartExtensionPoint { + + private static final CLogger logger = Utils.getLogger(VmExtensionPointAdapter.class); + + @Autowired + private CloudBus bus; + @Autowired + private DatabaseFacade dbf; + @Autowired + private AccountManager acntMgr; + @Autowired + private ResourceConfigFacade rcf; + + // --- L3NetworkDeleteExtensionPoint --- + + @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()); + } + + // --- ResourceOwnerAfterChangeExtensionPoint --- + + @Override + public void resourceOwnerAfterChange(AccountResourceRefInventory ref, String newOwnerUuid) { + if (!VmInstanceVO.class.getSimpleName().equals(ref.getResourceType())) { + return; + } + + // change root volume + SimpleQuery 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 sq = dbf.createQuery(VmNicVO.class); + sq.select(VmNicVO_.uuid); + sq.add(VmNicVO_.vmInstanceUuid, Op.EQ, ref.getResourceUuid()); + List vmnics = sq.listValue(); + if (vmnics.isEmpty()) { + return; + } + for (String vmnicUuid : vmnics) { + acntMgr.changeResourceOwner(vmnicUuid, newOwnerUuid); + } + + changeVmCdRomsOwner(ref.getResourceUuid(), newOwnerUuid); + } + + private void changeVmCdRomsOwner(String vmInstanceUuid, String newOwnerUuid) { + List vmCdRomUuids = Q.New(VmCdRomVO.class) + .select(VmCdRomVO_.uuid) + .eq(VmCdRomVO_.vmInstanceUuid, vmInstanceUuid) + .listValues(); + if (vmCdRomUuids.isEmpty()) { + return; + } + + for (String uuid : vmCdRomUuids) { + acntMgr.changeResourceOwner(uuid, newOwnerUuid); + } + } + + // --- AfterChangeHostStatusExtensionPoint --- + + @Override + public void afterChangeHostStatus(String hostUuid, HostStatus before, HostStatus next) { + if (next == HostStatus.Disconnected) { + List vms = Q.New(VmInstanceVO.class).select(VmInstanceVO_.uuid, VmInstanceVO_.state) + .eq(VmInstanceVO_.hostUuid, hostUuid) + .listTuple(); + if (vms.isEmpty()) { + return; + } + + new While<>(vms).step((vm, completion) -> { + String vmUuid = vm.get(0, String.class); + String vmState = vm.get(1, VmInstanceState.class).toString(); + 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()) { + logger.warn(String.format("the host[uuid:%s] disconnected, but the vm[uuid:%s] fails to " + + "change it's state to Unknown, %s", hostUuid, vmUuid, reply.getError())); + logger.warn(String.format("create an unknowngc job for vm[uuid:%s]", vmUuid)); + + UnknownVmGC gc = new UnknownVmGC(); + gc.NAME = UnknownVmGC.getGCName(vmUuid); + gc.vmUuid = vmUuid; + gc.vmState = vmState; + gc.hostUuid = hostUuid; + if (gc.existedAndNotCompleted()) { + logger.debug(String.format("There is already a UnknownVmGC of vm[uuid:%s] " + + "on host[uuid:%s], skip.", vmUuid, hostUuid)); + } else { + gc.submit(VmGlobalConfig.UNKNOWN_GC_INTERVAL.value(Long.class), TimeUnit.SECONDS); + } + } else { + logger.debug(String.format("the host[uuid:%s] disconnected, change the VM[uuid:%s]' state to Unknown", hostUuid, vmUuid)); + } + completion.done(); + } + }); + }, 20).run(new NopeWhileDoneCompletion()); + } + } + + // --- VmInstanceMigrateExtensionPoint --- + + @Override + public void beforeMigrateVm(VmInstanceInventory inv, String destHostUuid) { + } + + @Override + public void afterMigrateVm(VmInstanceInventory inv, String srcHostUuid) { + if (!inv.getHypervisorType().equals(VmInstanceConstant.KVM_HYPERVISOR_TYPE)) { + return; + } + + VmPriorityLevel level = new VmPriorityOperator().getVmPriority(inv.getUuid()); + VmPriorityConfigVO priorityVO = Q.New(VmPriorityConfigVO.class).eq(VmPriorityConfigVO_.level, level).find(); + + UpdateVmPriorityMsg msg = new UpdateVmPriorityMsg(); + msg.setPriorityConfigStructs(asList(new PriorityConfigStruct(priorityVO, inv.getUuid()))); + msg.setHostUuid(inv.getHostUuid()); + bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, inv.getHostUuid()); + bus.send(msg, new CloudBusCallBack(msg) { + @Override + public void run(MessageReply reply) { + UpdateVmPriorityReply r = new UpdateVmPriorityReply(); + if (!reply.isSuccess()) { + logger.warn(String.format("update vm[%s] priority to [%s] failed,because %s", + inv.getUuid(), level.toString(), reply.getError())); + } + } + }); + } + + @Override + public void failedToMigrateVm(VmInstanceInventory inv, String destHostUuid, ErrorCode reason) { + } + + // --- VmInstanceBeforeStartExtensionPoint --- + + @Override + public ErrorCode handleSystemTag(String vmUuid, List tags) { + ErrorCode errorCode = handleResourceDirectorySystemTag(vmUuid, tags); + if (errorCode != null) { + return errorCode; + } + + errorCode = handleNumaSystemTag(vmUuid, tags); + + if (errorCode != null) { + return errorCode; + } + + return null; + } + + private ErrorCode handleNumaSystemTag(String vmUuid, List tags) { + if (!VmSystemTags.NUMA.hasTag(vmUuid)) { + return null; + } + ResourceConfig rc = rcf.getResourceConfig(VmGlobalConfig.NUMA.getIdentity()); + rc.updateValue(vmUuid, Boolean.TRUE.toString()); + VmSystemTags.NUMA.delete(vmUuid); + + return null; + } + + //todo move to directory + private ErrorCode handleResourceDirectorySystemTag(String vmUuid, List tags) { + PatternedSystemTag tag = VmSystemTags.DIRECTORY_UUID; + String token = VmSystemTags.DIRECTORY_UUID_TOKEN; + + String directoryUuid = SystemTagUtils.findTagValue(tags, tag, token); + if (org.apache.commons.lang.StringUtils.isEmpty(directoryUuid)) { + return null; + } + ResourceDirectoryRefVO refVO = new ResourceDirectoryRefVO(); + refVO.setResourceUuid(vmUuid); + refVO.setDirectoryUuid(directoryUuid); + refVO.setResourceType(VmInstanceVO.class.getSimpleName()); + refVO.setLastOpDate(new Timestamp(new Date().getTime())); + refVO.setCreateDate(new Timestamp(new Date().getTime())); + dbf.persist(refVO); + return null; + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmFlowChainRegistry.java b/compute/src/main/java/org/zstack/compute/vm/VmFlowChainRegistry.java new file mode 100644 index 0000000000..ad7ba213d6 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmFlowChainRegistry.java @@ -0,0 +1,159 @@ +package org.zstack.compute.vm; + +import org.zstack.core.workflow.FlowChainBuilder; +import org.zstack.header.core.workflow.FlowChain; +import org.zstack.header.exception.CloudConfigureFailException; +import org.zstack.header.vm.VmInstanceInventory; + +import java.util.List; + +/** + * Registry for all VM lifecycle FlowChainBuilders. + * Extracted from VmInstanceManagerImpl to reduce God Class complexity. + */ +public class VmFlowChainRegistry { + private List createVmWorkFlowElements; + private List stopVmWorkFlowElements; + private List rebootVmWorkFlowElements; + private List startVmWorkFlowElements; + private List destroyVmWorkFlowElements; + private List migrateVmWorkFlowElements; + private List attachIsoWorkFlowElements; + private List detachIsoWorkFlowElements; + private List attachVolumeWorkFlowElements; + private List expungeVmWorkFlowElements; + private List pauseVmWorkFlowElements; + private List 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; + + public void buildFlowChains() { + try { + 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(); + } catch (Exception e) { + throw new CloudConfigureFailException(VmFlowChainRegistry.class, e.getMessage(), e); + } + } + + // --- FlowChain getters --- + + public FlowChain getCreateVmWorkFlowChain(VmInstanceInventory inv) { + return createVmFlowBuilder.build(); + } + + public FlowChain getStopVmWorkFlowChain(VmInstanceInventory inv) { + return stopVmFlowBuilder.build(); + } + + public FlowChain getRebootVmWorkFlowChain(VmInstanceInventory inv) { + return rebootVmFlowBuilder.build(); + } + + public FlowChain getStartVmWorkFlowChain(VmInstanceInventory inv) { + return startVmFlowBuilder.build(); + } + + public FlowChain getDestroyVmWorkFlowChain(VmInstanceInventory inv) { + return destroyVmFlowBuilder.build(); + } + + public FlowChain getMigrateVmWorkFlowChain(VmInstanceInventory inv) { + return migrateVmFlowBuilder.build(); + } + + public FlowChain getAttachUninstantiatedVolumeWorkFlowChain(VmInstanceInventory inv) { + return attachVolumeFlowBuilder.build(); + } + + public FlowChain getAttachIsoWorkFlowChain(VmInstanceInventory inv) { + return attachIsoFlowBuilder.build(); + } + + public FlowChain getDetachIsoWorkFlowChain(VmInstanceInventory inv) { + return detachIsoFlowBuilder.build(); + } + + public FlowChain getExpungeVmWorkFlowChain(VmInstanceInventory inv) { + return expungeVmFlowBuilder.build(); + } + + public FlowChain getPauseWorkFlowChain(VmInstanceInventory inv) { + return pauseVmFlowBuilder.build(); + } + + public FlowChain getResumeVmWorkFlowChain(VmInstanceInventory inv) { + return resumeVmFlowBuilder.build(); + } + + // --- Setters for Spring XML property injection --- + + public void setCreateVmWorkFlowElements(List createVmWorkFlowElements) { + this.createVmWorkFlowElements = createVmWorkFlowElements; + } + + public void setStopVmWorkFlowElements(List stopVmWorkFlowElements) { + this.stopVmWorkFlowElements = stopVmWorkFlowElements; + } + + public void setRebootVmWorkFlowElements(List rebootVmWorkFlowElements) { + this.rebootVmWorkFlowElements = rebootVmWorkFlowElements; + } + + public void setStartVmWorkFlowElements(List startVmWorkFlowElements) { + this.startVmWorkFlowElements = startVmWorkFlowElements; + } + + public void setDestroyVmWorkFlowElements(List destroyVmWorkFlowElements) { + this.destroyVmWorkFlowElements = destroyVmWorkFlowElements; + } + + public void setMigrateVmWorkFlowElements(List migrateVmWorkFlowElements) { + this.migrateVmWorkFlowElements = migrateVmWorkFlowElements; + } + + public void setAttachVolumeWorkFlowElements(List attachVolumeWorkFlowElements) { + this.attachVolumeWorkFlowElements = attachVolumeWorkFlowElements; + } + + public void setAttachIsoWorkFlowElements(List attachIsoWorkFlowElements) { + this.attachIsoWorkFlowElements = attachIsoWorkFlowElements; + } + + public void setDetachIsoWorkFlowElements(List detachIsoWorkFlowElements) { + this.detachIsoWorkFlowElements = detachIsoWorkFlowElements; + } + + public void setExpungeVmWorkFlowElements(List expungeVmWorkFlowElements) { + this.expungeVmWorkFlowElements = expungeVmWorkFlowElements; + } + + public void setPauseVmWorkFlowElements(List pauseVmWorkFlowElements) { + this.pauseVmWorkFlowElements = pauseVmWorkFlowElements; + } + + public void setResumeVmWorkFlowElements(List resumeVmWorkFlowElements) { + this.resumeVmWorkFlowElements = resumeVmWorkFlowElements; + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java b/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java index 321c7b3325..4a912c23e8 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java @@ -1,142 +1,69 @@ package org.zstack.compute.vm; -import com.google.common.collect.Maps; -import org.apache.commons.lang.StringUtils; import org.apache.commons.validator.routines.DomainValidator; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; -import org.zstack.compute.allocator.HostAllocatorManager; -import org.zstack.compute.vm.quota.*; -import org.zstack.configuration.DiskOfferingSystemTags; -import org.zstack.configuration.InstanceOfferingSystemTags; -import org.zstack.configuration.OfferingUserConfigUtils; -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.GlobalConfigBeforeUpdateExtensionPoint; -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.thread.*; -import org.zstack.core.workflow.FlowChainBuilder; -import org.zstack.core.workflow.ShareFlow; -import org.zstack.directory.ResourceDirectoryRefVO; 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.*; -import org.zstack.header.configuration.userconfig.DiskOfferingUserConfig; -import org.zstack.header.configuration.userconfig.InstanceOfferingUserConfig; -import org.zstack.header.core.*; -import org.zstack.header.core.workflow.*; +import org.zstack.header.core.workflow.FlowChain; import org.zstack.header.errorcode.ErrorCode; -import org.zstack.header.errorcode.ErrorCodeList; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.exception.CloudConfigureFailException; import org.zstack.header.exception.CloudRuntimeException; -import org.zstack.header.host.*; import org.zstack.header.identity.*; import org.zstack.header.identity.Quota.QuotaPair; -import org.zstack.header.identity.quota.QuotaMessageHandler; -import org.zstack.header.image.*; -import org.zstack.header.image.ImageConstant.ImageMediaType; -import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint; +import org.zstack.header.image.ImageBootMode; import org.zstack.header.message.*; -import org.zstack.header.network.l3.*; -import org.zstack.header.storage.backup.BackupStorageInventory; -import org.zstack.header.storage.backup.BackupStorageType; -import org.zstack.header.storage.backup.BackupStorageVO; -import org.zstack.header.storage.backup.BackupStorageVO_; -import org.zstack.header.storage.primary.*; 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.vm.cdrom.VmCdRomVO; -import org.zstack.header.vm.cdrom.VmCdRomVO_; -import org.zstack.header.volume.*; -import org.zstack.header.zone.ZoneInventory; -import org.zstack.header.zone.ZoneVO; +import org.zstack.header.volume.VolumeType; +import org.zstack.header.volume.VolumeVO; +import org.zstack.header.volume.VolumeVO_; import org.zstack.identity.AccountManager; import org.zstack.identity.QuotaUtil; -import org.zstack.network.l3.L3NetworkManager; -import org.zstack.resourceconfig.*; -import org.zstack.tag.*; -import org.zstack.utils.*; -import org.zstack.utils.function.Function; +import org.zstack.tag.PatternedSystemTag; +import org.zstack.tag.SystemTagUtils; +import org.zstack.tag.TagManager; +import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; -import org.zstack.utils.network.IPv6NetworkUtils; -import org.zstack.utils.network.NetworkUtils; +import org.zstack.utils.function.Function; -import javax.persistence.PersistenceException; import javax.persistence.Tuple; import javax.persistence.TypedQuery; -import java.sql.SQLIntegrityConstraintViolationException; -import java.sql.Timestamp; import java.util.*; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import static java.lang.Integer.parseInt; -import static java.lang.Integer.valueOf; import static java.util.Arrays.asList; -import static org.zstack.core.Platform.*; -import static org.zstack.utils.CollectionDSL.*; -import static org.zstack.utils.CollectionUtils.merge; -import static org.zstack.utils.CollectionUtils.transformToList; +import static org.zstack.core.Platform.argerr; +import static org.zstack.core.Platform.operr; import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*; +/** + * Thin facade for VmInstance service. + * Delegates to sub-managers extracted during God Class refactoring: + * - VmFlowChainRegistry: FlowChain builders and element lists + * - VmCreationSubManager: VM creation and candidate query handlers + * - VmNicApiSubManager: NIC create/delete/query handlers + * - VmQuotaSubManager: Quota definitions (registered as separate bean) + * - VmExpungeSubManager: Expunge periodic task (registered as separate bean) + * - VmExtensionPointAdapter: Extension point implementations (registered as separate bean) + */ public class VmInstanceManagerImpl extends AbstractService implements VmInstanceManager, - ReportQuotaExtensionPoint, ManagementNodeReadyExtensionPoint, - L3NetworkDeleteExtensionPoint, - ResourceOwnerAfterChangeExtensionPoint, - GlobalApiMessageInterceptor, - AfterChangeHostStatusExtensionPoint, - VmInstanceMigrateExtensionPoint, - VmInstanceBeforeStartExtensionPoint { + GlobalApiMessageInterceptor { private static final CLogger logger = Utils.getLogger(VmInstanceManagerImpl.class); - private List createVmWorkFlowElements; - private List stopVmWorkFlowElements; - private List rebootVmWorkFlowElements; - private List startVmWorkFlowElements; - private List destroyVmWorkFlowElements; - private List migrateVmWorkFlowElements; - private List attachIsoWorkFlowElements; - private List detachIsoWorkFlowElements; - private List attachVolumeWorkFlowElements; - private List expungeVmWorkFlowElements; - private List pauseVmWorkFlowElements; - private List 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 allowedMessageAfterSoftDeletion = new HashSet<>(); - private Future expungeVmTask; static { allowedMessageAfterSoftDeletion.add(VmInstanceDeletionMsg.class); @@ -149,34 +76,28 @@ public class VmInstanceManagerImpl extends AbstractService implements @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 HostAllocatorManager hostAllocatorMgr; - @Autowired - protected VmInstanceExtensionPointEmitter extEmitter; + private VmFactoryManager vmFactoryManager; @Autowired - protected L3NetworkManager l3nm; + protected EventFacade evtf; + + // --- Sub-managers injected via Spring XML --- @Autowired - private ResourceConfigFacade rcf; + private VmFlowChainRegistry flowChainRegistry; @Autowired - private VmFactoryManager vmFactoryManager; + private VmCreationSubManager creationSubManager; @Autowired - protected EventFacade evtf; + private VmNicApiSubManager nicApiSubManager; private List vmExtensionManagers = new ArrayList<>(); + // ==================== Message Routing ==================== + @Override public void handleMessage(Message msg) { VmInstanceExtensionManager extensionManager = vmExtensionManagers.stream().filter(it -> it.getMessageClasses() @@ -210,7 +131,7 @@ void passThrough(VmInstanceMessage msg) { private void handleLocalMessage(Message msg) { if (msg instanceof CreateVmInstanceMsg) { - handle((CreateVmInstanceMsg) msg); + creationSubManager.handle((CreateVmInstanceMsg) msg); } else if (msg instanceof VmInstanceMessage) { passThrough((VmInstanceMessage) msg); } else { @@ -220,29 +141,29 @@ private void handleLocalMessage(Message msg) { private void handleApiMessage(APIMessage msg) { if (msg instanceof APICreateVmInstanceMsg) { - handle((APICreateVmInstanceMsg) msg); - } else if(msg instanceof APICreateVmNicMsg) { - handle((APICreateVmNicMsg) msg); + creationSubManager.handle((APICreateVmInstanceMsg) msg); + } else if (msg instanceof APICreateVmNicMsg) { + nicApiSubManager.handle((APICreateVmNicMsg) msg); } else if (msg instanceof APIGetVmNicAttachedNetworkServiceMsg) { - handle((APIGetVmNicAttachedNetworkServiceMsg) msg); + nicApiSubManager.handle((APIGetVmNicAttachedNetworkServiceMsg) msg); } else if (msg instanceof APIDeleteVmNicMsg) { - handle((APIDeleteVmNicMsg) msg); + nicApiSubManager.handle((APIDeleteVmNicMsg) msg); } else if (msg instanceof APIGetCandidateZonesClustersHostsForCreatingVmMsg) { - handle((APIGetCandidateZonesClustersHostsForCreatingVmMsg) msg); + creationSubManager.handle((APIGetCandidateZonesClustersHostsForCreatingVmMsg) msg); } else if (msg instanceof APIGetCandidatePrimaryStoragesForCreatingVmMsg) { - handle((APIGetCandidatePrimaryStoragesForCreatingVmMsg) msg); + creationSubManager.handle((APIGetCandidatePrimaryStoragesForCreatingVmMsg) msg); } else if (msg instanceof APIGetInterdependentL3NetworksImagesMsg) { - handle((APIGetInterdependentL3NetworksImagesMsg) msg); + creationSubManager.handle((APIGetInterdependentL3NetworksImagesMsg) msg); } else if (msg instanceof APIGetInterdependentL3NetworksBackupStoragesMsg) { - handle((APIGetInterdependentL3NetworksBackupStoragesMsg) msg); + creationSubManager.handle((APIGetInterdependentL3NetworksBackupStoragesMsg) msg); } else if (msg instanceof APIGetCandidateVmForAttachingIsoMsg) { - handle((APIGetCandidateVmForAttachingIsoMsg) msg); + creationSubManager.handle((APIGetCandidateVmForAttachingIsoMsg) msg); } else if (msg instanceof APIUpdatePriorityConfigMsg) { - handle((APIUpdatePriorityConfigMsg) msg); + creationSubManager.handle((APIUpdatePriorityConfigMsg) msg); } else if (msg instanceof APIGetSpiceCertificatesMsg) { - handle((APIGetSpiceCertificatesMsg) msg); + creationSubManager.handle((APIGetSpiceCertificatesMsg) msg); } else if (msg instanceof APIGetVmsCapabilitiesMsg) { - handle((APIGetVmsCapabilitiesMsg) msg); + creationSubManager.handle((APIGetVmsCapabilitiesMsg) msg); } else if (msg instanceof VmInstanceMessage) { passThrough((VmInstanceMessage) msg); } else { @@ -250,1638 +171,397 @@ private void handleApiMessage(APIMessage msg) { } } - private void handle(APIGetInterdependentL3NetworksBackupStoragesMsg msg) { - final String accountUuid = msg.getSession().getAccountUuid(); - APIGetInterdependentL3NetworksBackupStoragesReply reply = - new APIGetInterdependentL3NetworksBackupStoragesReply(); - if (msg.getBackupStorageUuid() != null) { - BackupStorageVO bsvo = Q.New(BackupStorageVO.class) - .eq(BackupStorageVO_.uuid, msg.getBackupStorageUuid()) - .find(); - if (bsvo == null) { - reply.setInventories(new ArrayList<>()); - } else { - reply.setInventories(getInterdependentL3NetworksByBackupStorageUuids(Collections.singletonList(bsvo), - msg.getZoneUuid(), accountUuid, false)); - } - } else { - reply.setInventories(getInterdependentBackupStoragesByL3NetworkUuids(msg.getL3NetworkUuids())); + // ==================== Service Lifecycle ==================== + + @Override + public String getId() { + return bus.makeLocalServiceId(VmInstanceConstant.SERVICE_ID); + } + + @Override + public boolean start() { + try { + flowChainRegistry.buildFlowChains(); + installSystemTagValidator(); + installGlobalConfigUpdater(); + vmExtensionManagers.addAll(pluginRgty.getExtensionList(VmInstanceExtensionManager.class)); + + bus.installBeforeDeliveryMessageInterceptor(new AbstractBeforeDeliveryMessageInterceptor() { + @Override + public void beforeDeliveryMessage(Message msg) { + if (msg instanceof NeedQuotaCheckMessage) { + if (((NeedQuotaCheckMessage) msg).getAccountUuid() == null || + ((NeedQuotaCheckMessage) msg).getAccountUuid().equals("")) { + // skip admin scheduler + return; + } + List quotas = acntMgr.getMessageQuotaMap().get(msg.getClass()); + if (quotas == null || quotas.size() == 0) { + return; + } + Map pairs = new QuotaUtil(). + makeQuotaPairs(((NeedQuotaCheckMessage) msg).getAccountUuid()); + for (Quota quota : quotas) { + quota.getOperator().checkQuota((NeedQuotaCheckMessage) msg, pairs); + } + } + } + }, StartVmInstanceMsg.class); + + deleteMigrateSystemTagWhenVmStateChangedToRunning(); + pluginRgty.saveExtensionAsMap(VmAttachOtherDiskExtensionPoint.class, new Function() { + @Override + public Object call(VmAttachOtherDiskExtensionPoint arg) { + return arg.getDiskType(); + } + }); + + return true; + } catch (Exception e) { + throw new CloudConfigureFailException(VmInstanceManagerImpl.class, e.getMessage(), e); } + } - bus.reply(msg, reply); + @Override + public boolean stop() { + return true; } - private void handle(final APIGetVmsCapabilitiesMsg msg) { - APIGetVmsCapabilitiesReply reply = new APIGetVmsCapabilitiesReply(); - Map vmsCaps = Maps.newConcurrentMap(); - msg.getVmUuids() - .parallelStream() - .forEach(v -> { - vmsCaps.put(v, new VmCapabilitiesJudger().judge(v)); - }); - - reply.setVmsCaps(vmsCaps); - bus.reply(msg, reply); + @Override + @AsyncThread + public void managementNodeReady() { + // Expunge task now handled by VmExpungeSubManager } - private void handle(final APIUpdatePriorityConfigMsg msg) { - final APIUpdatePriorityConfigEvent evt = new APIUpdatePriorityConfigEvent(msg.getId()); + // ==================== GlobalApiMessageInterceptor ==================== - VmPriorityOperator.PriorityStruct struct = new VmPriorityOperator.PriorityStruct(); - struct.setCpuShares(msg.getCpuShares()); - struct.setOomScoreAdj(msg.getOomScoreAdj()); - VmPriorityConfigVO vmPriorityConfigVO = new VmPriorityOperator().updatePriorityConfig(msg.getUuid(), struct); - if (vmPriorityConfigVO != null) { - for (UpdatePriorityConfigExtensionPoint exp : pluginRgty.getExtensionList(UpdatePriorityConfigExtensionPoint.class)) { - exp.afterUpdatePriorityConfig(vmPriorityConfigVO); - } - } - bus.publish(evt); + @Override + public List getMessageClassToIntercept() { + return asList(APIChangeResourceOwnerMsg.class); } - private void handle(APIGetSpiceCertificatesMsg msg) { - APIGetSpiceCertificatesReply reply = new APIGetSpiceCertificatesReply(); - String certificateStr = new JsonLabel().get("spiceCA", String.class); - if (StringUtils.isNotEmpty(certificateStr)) { - reply.setCertificateStr(certificateStr); - } else { - reply.setError(operr(ORG_ZSTACK_COMPUTE_VM_10232, "Spice certificate does not exist, Please check if spice tls is enabled")); - } - bus.reply(msg, reply); + @Override + public InterceptorPosition getPosition() { + return InterceptorPosition.END; } - @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 q = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); - q.setParameter("isoUuid", msg.getIsoUuid()); - List bss = q.getResultList(); - if (bss.isEmpty()) { - reply.setInventories(new ArrayList<>()); - bus.reply(msg, reply); - return; + @Override + public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException { + if (msg instanceof APIChangeResourceOwnerMsg) { + validateAPIChangeResourceOwnerMsg((APIChangeResourceOwnerMsg) msg); } - List psUuids = new ArrayList<>(); - List psTypes = new ArrayList<>(); - for (BackupStorageVO bs : bss) { - BackupStorageType bsType = BackupStorageType.valueOf(bs.getType()); - List lst = bsType.findRelatedPrimaryStorage(bs.getUuid()); - if (lst != null) { - psUuids.addAll(lst); - } else { - psTypes.addAll(hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics(bs.getType())); - } - } + return msg; + } - List 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 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()); - } + private void validateAPIChangeResourceOwnerMsg(APIChangeResourceOwnerMsg msg) { + SimpleQuery q = dbf.createQuery(AccountResourceRefVO.class); + q.add(AccountResourceRefVO_.resourceUuid, Op.EQ, msg.getResourceUuid()); + AccountResourceRefVO ref = q.find(); - 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 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()); + if (ref == null || !VolumeVO.class.getSimpleName().equals(ref.getResourceType())) { + return; } - List result = VmInstanceInventory.valueOf(vms); - - for (VmAttachIsoExtensionPoint ext : pluginRgty.getExtensionList(VmAttachIsoExtensionPoint.class)) { - ext.filtCandidateVms(msg.getIsoUuid(), result); + SimpleQuery 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(ORG_ZSTACK_COMPUTE_VM_10263, "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())); } - reply.setInventories(result); - bus.reply(msg, reply); } - private void handle(APIGetInterdependentL3NetworksImagesMsg msg) { - final String accountUuid = msg.getSession().getAccountUuid(); - if (msg.getImageUuid() != null) { - thdf.singleFlightSubmit(new SingleFlightTask(msg) - .setSyncSignature(String.format("get-interdependent-l3-by-image-%s-in-zone-%s", - msg.getImageUuid(), - msg.getZoneUuid())) - .run((completion) -> completion.success(getInterdependentL3NetworksByImageUuid(msg, accountUuid))) - .done(((result) -> { - APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); - if (!result.isSuccess()) { - reply.setError(result.getErrorCode()); - } else { - reply.setInventories((List) result.getResult()); - } - - bus.reply(msg, reply); - }))); - } else if (msg.getL3NetworkUuids() != null) { - getInterdependentImagesByL3NetworkUuids(msg); - } else { - thdf.singleFlightSubmit(new SingleFlightTask(msg) - .setSyncSignature(String.format("get-interdependent-l3-by-zone-%s", - msg.getZoneUuid())) - .run((completion) -> completion.success(getInterdependentL3NetworksByImageUuid(msg, accountUuid))) - .done(((result) -> { - APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); - if (!result.isSuccess()) { - reply.setError(result.getErrorCode()); - } else { - reply.setInventories((List) result.getResult()); - } + // ==================== VmInstanceManager Interface (delegates to registries/factories) ==================== - bus.reply(msg, reply); - }))); + @Override + public VmInstanceFactory getVmInstanceFactory(VmInstanceType type) { + VmInstanceFactory factory = vmFactoryManager.getVmInstanceFactory(type.toString()); + if (factory == null) { + throw new CloudRuntimeException(String.format("No VmInstanceFactory of type[%s] found", type)); } + return factory; } - private List listIntersection(List a, List b) { - List ret = new ArrayList<>(); - for (BackupStorageVO s : a) { - if (b.stream().filter(it -> it.getUuid().equals(s.getUuid())).findAny().isPresent()) { - ret.add(s); - } - } + @Override + public VmInstanceBaseExtensionFactory getVmInstanceBaseExtensionFactory(Message msg) { + return vmFactoryManager.getVmInstanceBaseExtensionFactory(msg.getClass()); + } - return ret; + @Override + public VmInstanceNicFactory getVmInstanceNicFactory(VmNicType type) { + VmInstanceNicFactory factory = vmFactoryManager.getVmInstanceNicFactory(type.toString()); + if (factory == null) { + throw new CloudRuntimeException(String.format("No VmInstanceNicFactory of type[%s] found", type)); + } + return factory; } - @Transactional(readOnly = true) - private List getInterdependentBackupStoragesByL3NetworkUuids(List l3s) { - List> bss = new ArrayList<>(); - for (String l3uuid : l3s) { - 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 psq = dbf.getEntityManager().createQuery(sql, PrimaryStorageVO.class); - psq.setParameter("l3uuid", l3uuid); - List pss = psq.getResultList(); - - List lst = new ArrayList<>(); - for (PrimaryStorageVO ps : pss) { - PrimaryStorageType psType = PrimaryStorageType.valueOf(ps.getType()); - List bsUuids = psType.findBackupStorage(ps.getUuid()); - - if (!bsUuids.isEmpty()) { - // the primary storage has bound backup storage, e.g. ceph - sql = "select bs from BackupStorageVO bs where bs.uuid in (:uuids)"; - TypedQuery 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())); - } - } + @Override + public VmNicQosConfigBackend getVmNicQosConfigBackend(String type) { + return vmFactoryManager.getVmNicQosConfigBackend(type); + } - bss.add(lst); - } + // --- FlowChain getters delegate to VmFlowChainRegistry --- - List selectedBss = new ArrayList<>(); - for (List lst : bss) { - selectedBss.addAll(lst); - } + @Override + public FlowChain getCreateVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getCreateVmWorkFlowChain(inv); + } - for (List l : bss) { - selectedBss = listIntersection(selectedBss, l); - } + @Override + public FlowChain getStopVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getStopVmWorkFlowChain(inv); + } - return BackupStorageInventory.valueOf(selectedBss); + @Override + public FlowChain getRebootVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getRebootVmWorkFlowChain(inv); } - @Transactional(readOnly = true) - private void getInterdependentImagesByL3NetworkUuids(APIGetInterdependentL3NetworksImagesMsg msg) { - APIGetInterdependentL3NetworkImageReply reply = new APIGetInterdependentL3NetworkImageReply(); + @Override + public FlowChain getStartVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getStartVmWorkFlowChain(inv); + } - List bss = getInterdependentBackupStoragesByL3NetworkUuids(msg.getL3NetworkUuids()); + @Override + public FlowChain getDestroyVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getDestroyVmWorkFlowChain(inv); + } - if (bss.isEmpty()) { - reply.setInventories(new ArrayList<>()); - bus.reply(msg, reply); - return; - } + @Override + public FlowChain getMigrateVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getMigrateVmWorkFlowChain(inv); + } - List bsUuids = bss.stream().map(BackupStorageInventory::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 iq = dbf.getEntityManager().createQuery(sql, ImageVO.class); - iq.setParameter("uuids", bsUuids); - iq.setParameter("zoneUuid", msg.getZoneUuid()); - List vos = iq.getResultList(); - reply.setInventories(ImageInventory.valueOf(vos)); - bus.reply(msg, reply); + @Override + public FlowChain getAttachUninstantiatedVolumeWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getAttachUninstantiatedVolumeWorkFlowChain(inv); } - @Transactional(readOnly = true) - private List getInterdependentL3NetworksByImageUuid(APIGetInterdependentL3NetworksImagesMsg msg, String accountUuid) { - List bss = null; - if (msg.getImageUuid() != null) { - 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 bsq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); - bsq.setParameter("imgUuid", msg.getImageUuid()); - bsq.setParameter("zoneUuid", msg.getZoneUuid()); - bss = bsq.getResultList(); - } else { - String sql = "select bs" + - " from BackupStorageVO bs, BackupStorageZoneRefVO zref" + - " where bs.uuid = zref.backupStorageUuid" + - " and zref.zoneUuid = :zoneUuid"; - TypedQuery bsq = dbf.getEntityManager().createQuery(sql, BackupStorageVO.class); - bsq.setParameter("zoneUuid", msg.getZoneUuid()); - bss = bsq.getResultList(); - } + @Override + public FlowChain getAttachIsoWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getAttachIsoWorkFlowChain(inv); + } - if (bss.isEmpty()) { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10233, "the image[uuid:%s] is not on any backup storage that has been attached to the zone[uuid:%s]", - msg.getImageUuid(), msg.getZoneUuid())); - } + @Override + public FlowChain getDetachIsoWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getDetachIsoWorkFlowChain(inv); + } - List l3s = getInterdependentL3NetworksByBackupStorageUuids(bss, msg.getZoneUuid(), accountUuid, msg.getRaiseException()); + @Override + public FlowChain getExpungeVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getExpungeVmWorkFlowChain(inv); + } - List bsUuids = bss.stream().map(BackupStorageVO::getUuid).collect(Collectors.toList()); - for (GetInterdependentL3NetworksExtensionPoint ext : pluginRgty.getExtensionList(GetInterdependentL3NetworksExtensionPoint.class)) { - l3s = ext.afterFilterByImage(l3s, bsUuids, msg.getImageUuid()); - } + public FlowChain getPauseWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getPauseWorkFlowChain(inv); + } - return l3s; + public FlowChain getResumeVmWorkFlowChain(VmInstanceInventory inv) { + return flowChainRegistry.getResumeVmWorkFlowChain(inv); } - @Transactional(readOnly = true) - private List getInterdependentL3NetworksByBackupStorageUuids(List bss, String zoneUuid, String accountUuid, boolean raiseException) { - List psUuids = new ArrayList<>(); - List l3s = new ArrayList<>(); - for (BackupStorageVO bs : bss) { - BackupStorageType bsType = BackupStorageType.valueOf(bs.getType()); - List relatedPrimaryStorageUuids = bsType.findRelatedPrimaryStorage(bs.getUuid()); - if (relatedPrimaryStorageUuids == null) { - // the backup storage has no strongly-bound primary storage - List psTypes = hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics(bs.getType()); - psUuids.addAll(Q.New(PrimaryStorageVO.class) - .select(PrimaryStorageVO_.uuid) - .in(PrimaryStorageVO_.type, psTypes) - .eq(PrimaryStorageVO_.zoneUuid, zoneUuid) - .listValues()); - l3s.addAll(SQL.New("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") - .param("psTypes", psTypes) - .param("zoneUuid", zoneUuid) - .list()); - } else if (!relatedPrimaryStorageUuids.isEmpty()) { - // the backup storage has strongly-bound primary storage, e.g. ceph - psUuids.addAll(Q.New(PrimaryStorageVO.class) - .select(PrimaryStorageVO_.uuid) - .in(PrimaryStorageVO_.uuid, relatedPrimaryStorageUuids) - .eq(PrimaryStorageVO_.zoneUuid, zoneUuid) - .listValues()); - l3s.addAll(SQL.New("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") - .param("psUuids", relatedPrimaryStorageUuids) - .param("zoneUuid", zoneUuid) - .list()); - } else { - // relatedPrimaryStorageUuids is not null, but size is 0 - 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())); - } - } + // ==================== GlobalConfig Updaters ==================== - if (l3s.isEmpty()) { - if (psUuids.isEmpty()) { - if (raiseException) { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10234, "no primary storage accessible to the backup storage[uuid:%s, type:%s] is found", - bss.get(0).getUuid(), bss.get(0).getType())); + private void installGlobalConfigUpdater() { + VmGlobalConfig.MULTI_VNIC_SUPPORT.installBeforeUpdateExtension(new GlobalConfigBeforeUpdateExtensionPoint() { + @Override + public void beforeUpdateExtensionPoint(GlobalConfig oldConfig, String newValue) { + if (!oldConfig.value(Boolean.class) || "true".equalsIgnoreCase(newValue)) { + return; } - logger.warn(String.format("no primary storage accessible to the backup storage[uuid:%s, type:%s] is found", - bss.get(0).getUuid(), bss.get(0).getType())); - return new ArrayList<>(); - } - Long clusterNum = SQL.New("select count(distinct cl)" + - " from ClusterVO cl, PrimaryStorageClusterRefVO psref, PrimaryStorageVO ps" + - " where cl.uuid = psref.clusterUuid" + - " and psref.primaryStorageUuid in (:psUuids)" + - " and ps.zoneUuid = cl.zoneUuid" + - " and cl.zoneUuid = :zoneUuid" + - " group by cl.uuid", Long.class) - .param("psUuids", psUuids) - .param("zoneUuid", zoneUuid) - .find(); - - if (clusterNum == null || clusterNum == 0) { - if (raiseException) { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10235, "the primary storages[uuids:%s] has not attached any cluster on the zone[uuid:%s]", - psUuids, zoneUuid)); + List tuples; + String sql = "select vmInstanceUuid, l3NetworkUuid, count(*) from VmNicVO group by vmInstanceUuid, l3NetworkUuid"; + TypedQuery q = dbf.getEntityManager().createQuery(sql, Tuple.class); + tuples = q.getResultList(); + if (tuples == null || tuples.isEmpty()) { + return; } - logger.warn(String.format("the primary storages[uuids:%s] has not attached any cluster on the zone[uuid:%s]", psUuids, zoneUuid)); - return new ArrayList<>(); - } - - Long l2Num = SQL.New("select count(distinct l2)" + - " from L2NetworkVO l2, L2NetworkClusterRefVO l2ref, PrimaryStorageClusterRefVO psref, PrimaryStorageVO ps" + - " where l2.uuid = l2ref.l2NetworkUuid" + - " and psref.primaryStorageUuid in (:psUuids)" + - " and l2ref.clusterUuid = psref.clusterUuid" + - " and ps.zoneUuid = l2.zoneUuid" + - " and l2.zoneUuid = :zoneUuid", Long.class) - .param("psUuids", psUuids) - .param("zoneUuid", zoneUuid) - .find(); - if (l2Num == null || l2Num == 0) { - if (raiseException) { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10236, "no l2Networks found in clusters that have attached to primary storages[uuids:%s]", - psUuids)); + for (Tuple tuple: tuples) { + if (tuple.get(2, Long.class) > 1) { + throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_VM_10243, "unable to enable this function. There are multi nics of L3 network[uuid:%s] in the vm[uuid: %s]", + tuple.get(0, String.class), tuple.get(1, String.class))); + } } - logger.warn(String.format("no l2Networks found in clusters that have attached to primary storages[uuids:%s]", psUuids)); - return new ArrayList<>(); } - } - - List l3UuidListOfCurrentAccount; - if (!AccountConstant.isAdminPermission(accountUuid)) { - l3UuidListOfCurrentAccount = acntMgr.getResourceUuidsCanAccessByAccount(accountUuid, L3NetworkVO.class); - } else { - l3UuidListOfCurrentAccount = null; - } + }); - if (l3UuidListOfCurrentAccount == null) { - return L3NetworkInventory.valueOf(l3s); - } - return L3NetworkInventory.valueOf(l3s.stream() - .filter(vo -> l3UuidListOfCurrentAccount.contains(vo.getUuid())) - .collect(Collectors.toList())); + // Note: VM_RESOURCE_BINGDING tag is silently deprecated (ZSTAC-75428) + // The tag data is preserved but no longer read or written by the new ResourceBindingAllocatorFlow } - private void handle(APIGetCandidateZonesClustersHostsForCreatingVmMsg msg) { - DesignatedAllocateHostMsg amsg = new DesignatedAllocateHostMsg(); + // ==================== System Tag Validators ==================== - ImageVO image = dbf.findByUuid(msg.getImageUuid(), ImageVO.class); - amsg.setImage(ImageInventory.valueOf(image)); - amsg.setZoneUuid(msg.getZoneUuid()); - amsg.setClusterUuid(msg.getClusterUuid()); - - InstanceOfferingVO insvo = null; - if (msg.getInstanceOfferingUuid() == null) { - amsg.setCpuCapacity(msg.getCpuNum()); - amsg.setMemoryCapacity(msg.getMemorySize()); - } else { - insvo = dbf.findByUuid(msg.getInstanceOfferingUuid(), InstanceOfferingVO.class); - amsg.setCpuCapacity(insvo.getCpuNum()); - amsg.setMemoryCapacity(insvo.getMemorySize()); - } - - long diskSize = 0; - List diskOfferings = new ArrayList<>(); - if (msg.getDataDiskOfferingUuids() != null) { - SimpleQuery q = dbf.createQuery(DiskOfferingVO.class); - q.add(DiskOfferingVO_.uuid, Op.IN, msg.getDataDiskOfferingUuids()); - List dvos = q.list(); - diskOfferings.addAll(DiskOfferingInventory.valueOf(dvos)); - } + private void installSystemTagValidator() { + installHostnameValidator(); + installUserdataValidator(); + installBootModeValidator(); + installCleanTrafficValidator(); + installMachineTypeValidator(); + installUsbRedirectValidator(); + installL3NetworkSecurityGroupValidator(); + installSeDeviceValidator(); + installHygonSeDeviceValidator(); + new StaticIpOperator().installStaticIpValidator(); + } - if (image.getMediaType() == ImageMediaType.ISO) { - if (msg.getRootDiskOfferingUuid() == null) { - diskSize = msg.getRootDiskSize(); - } else { - DiskOfferingVO rootDiskOffering = dbf.findByUuid(msg.getRootDiskOfferingUuid(), DiskOfferingVO.class); - diskOfferings.add(DiskOfferingInventory.valueOf(rootDiskOffering)); + private void installHostnameValidator() { + 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(ORG_ZSTACK_COMPUTE_VM_10244, "hostname[%s] specified in system tag[%s] is not a valid domain name", hostname, tag)); + } } - } 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(ORG_ZSTACK_COMPUTE_VM_10237, "zoneUuid must be set because the image[name:%s, uuid:%s] is on multiple backup storage", - image.getName(), image.getUuid())); - } + @Override + public void validateSystemTagInCreateMessage(APICreateMessage cmsg) { + final NewVmInstanceMessage msg = (NewVmInstanceMessage) cmsg; - ImageBackupStorageSelector selector = new ImageBackupStorageSelector(); - selector.setZoneUuid(msg.getZoneUuid()); - selector.setImageUuid(image.getUuid()); - amsg.setRequiredBackupStorageUuid(selector.select()); - } + int hostnameCount = 0; + for (String sysTag : msg.getSystemTags()) { + if (VmSystemTags.HOSTNAME.isMatch(sysTag)) { + if (++hostnameCount > 1) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10245, "only one hostname system tag is allowed, but %s got", hostnameCount)); + } - VmInstanceInventory vm = new VmInstanceInventory(); - vm.setUuid(Platform.FAKE_UUID); - vm.setImageUuid(image.getUuid()); - if (insvo == null) { - vm.setCpuNum(msg.getCpuNum()); - vm.setMemorySize(msg.getMemorySize()); - } else { - vm.setInstanceOfferingUuid(insvo.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); - if (msg.getSystemTags() != null && !msg.getSystemTags().isEmpty()) { - amsg.setSystemTags(new ArrayList(msg.getSystemTags())); - } + String hostname = VmSystemTags.HOSTNAME.getTokenByTag(sysTag, VmSystemTags.HOSTNAME_TOKEN); - 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()); - - Set clusterUuids = re.getHosts().stream(). - map(HostInventory::getClusterUuid).collect(Collectors.toSet()); - List clusters = ClusterInventory.valueOf(dbf.listByPrimaryKeys(clusterUuids, ClusterVO.class)); - areply.setClusters(clusters); - - Set zoneUuids = clusters.stream(). - map(ClusterInventory::getZoneUuid).collect(Collectors.toSet()); - areply.setZones(ZoneInventory.valueOf(dbf.listByPrimaryKeys(zoneUuids, ZoneVO.class))); - } else { - areply.setHosts(new ArrayList<>()); - areply.setClusters(new ArrayList<>()); - areply.setZones(new ArrayList<>()); + validateHostname(sysTag, hostname); } } - - bus.reply(msg, areply); } - }); - } - - private void handle(APIGetCandidatePrimaryStoragesForCreatingVmMsg msg) { - APIGetCandidatePrimaryStoragesForCreatingVmReply reply = new APIGetCandidatePrimaryStoragesForCreatingVmReply(); - List msgs = new ArrayList<>(); - - Set psTypes = new HashSet<>(); - List clusterUuids = new ArrayList<>(); - List dataOfferings = new ArrayList<>(); - ImageInventory imageInv = new SQLBatchWithReturn(){ - - @Override - protected ImageInventory scripts() { - List dataOfferingUuids = msg.getDataDiskOfferingUuids() == null ? new ArrayList<>() : - msg.getDataDiskOfferingUuids(); - - sql("select bs.type from BackupStorageVO bs, ImageBackupStorageRefVO ref" + - " where ref.imageUuid =:imageUuid" + - " and bs.uuid = ref.backupStorageUuid", String.class) - .param("imageUuid", msg.getImageUuid()) - .list().forEach(it -> - psTypes.addAll(hostAllocatorMgr.getPrimaryStorageTypesByBackupStorageTypeFromMetrics((String)it) - )); - - clusterUuids.addAll(sql("select distinct ref.clusterUuid" + - " from L2NetworkClusterRefVO ref, L3NetworkVO l3" + - " where l3.uuid in (:l3Uuids)" + - " and ref.l2NetworkUuid = l3.l2NetworkUuid", String.class) - .param("l3Uuids", msg.getL3NetworkUuids()) - .list()); - - for (String diskUuid : dataOfferingUuids){ - dataOfferings.add(DiskOfferingInventory.valueOf( - (DiskOfferingVO) q(DiskOfferingVO.class) - .eq(DiskOfferingVO_.uuid, diskUuid) - .find() - )); - } - ImageVO imageVO = q(ImageVO.class).eq(ImageVO_.uuid, msg.getImageUuid()).find(); - return ImageInventory.valueOf(imageVO); - } - }.execute(); - - - // allocate ps for root volume - AllocatePrimaryStorageMsg rmsg = new AllocatePrimaryStorageMsg(); - rmsg.setDryRun(true); - rmsg.setImageUuid(msg.getImageUuid()); - rmsg.setRequiredClusterUuids(clusterUuids); - if (ImageMediaType.ISO.toString().equals(imageInv.getMediaType())) { - if (msg.getRootDiskOfferingUuid() == null) { - rmsg.setSize(msg.getRootDiskSize()); - } else { - Tuple t = Q.New(DiskOfferingVO.class).eq(DiskOfferingVO_.uuid, msg.getRootDiskOfferingUuid()) - .select(DiskOfferingVO_.diskSize, DiskOfferingVO_.allocatorStrategy).findTuple(); - - rmsg.setSize((long) t.get(0)); - rmsg.setAllocationStrategy((String) t.get(1)); - rmsg.setDiskOfferingUuid(msg.getRootDiskOfferingUuid()); - } - } else { - rmsg.setSize(imageInv.getSize()); - } - - if (msg.getRootDiskOfferingUuid() != null && DiskOfferingSystemTags.DISK_OFFERING_USER_CONFIG.hasTag(msg.getRootDiskOfferingUuid())) { - DiskOfferingUserConfig config = OfferingUserConfigUtils.getDiskOfferingConfig(msg.getRootDiskOfferingUuid(), DiskOfferingUserConfig.class); - if (config.getAllocate() != null && config.getAllocate().getPrimaryStorage() != null) { - String psUuid = config.getAllocate().getPrimaryStorage().getUuid(); - rmsg.setRequiredPrimaryStorageUuid(psUuid); - } - } - - if (msg.getInstanceOfferingUuid() != null && InstanceOfferingSystemTags.INSTANCE_OFFERING_USER_CONFIG.hasTag(msg.getInstanceOfferingUuid())) { - InstanceOfferingUserConfig config = OfferingUserConfigUtils.getInstanceOfferingConfig(msg.getInstanceOfferingUuid(), InstanceOfferingUserConfig.class); - if (config.getAllocate() != null && config.getAllocate().getPrimaryStorage() != null) { - String psUuid = config.getAllocate().getPrimaryStorage().getUuid(); - rmsg.setRequiredPrimaryStorageUuid(psUuid); - } - } - - rmsg.setPurpose(PrimaryStorageAllocationPurpose.CreateNewVm.toString()); - rmsg.setPossiblePrimaryStorageTypes(new ArrayList<>(psTypes)); - bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID); - msgs.add(rmsg); - - // allocate ps for data volumes - for (DiskOfferingInventory dinv : dataOfferings) { - AllocatePrimaryStorageMsg amsg = new AllocatePrimaryStorageMsg(); - amsg.setDryRun(true); - amsg.setSize(dinv.getDiskSize()); - amsg.setRequiredClusterUuids(clusterUuids); - amsg.setAllocationStrategy(dinv.getAllocatorStrategy()); - amsg.setDiskOfferingUuid(dinv.getUuid()); - if (DiskOfferingSystemTags.DISK_OFFERING_USER_CONFIG.hasTag(dinv.getUuid())) { - DiskOfferingUserConfig config = OfferingUserConfigUtils.getDiskOfferingConfig(dinv.getUuid(), DiskOfferingUserConfig.class); - if (config.getAllocate() != null && config.getAllocate().getPrimaryStorage() != null) { - String psUuid = config.getAllocate().getPrimaryStorage().getUuid(); - amsg.setRequiredPrimaryStorageUuid(psUuid); - } + @Transactional(readOnly = true) + private List querySystemTagsByL3(String tag, 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 q = dbf.getEntityManager().createQuery(sql, SystemTagVO.class); + q.setParameter("l3Uuid", l3Uuid); + q.setParameter("sysTag", tag); + return q.getResultList(); } - bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID); - msgs.add(amsg); - } - - if (msg.getDataDiskSizes() != null) { - for (Long size : msg.getDataDiskSizes()) { - AllocatePrimaryStorageMsg amsg = new AllocatePrimaryStorageMsg(); - amsg.setDryRun(true); - amsg.setSize(size); - amsg.setRequiredClusterUuids(clusterUuids); - bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID); - msgs.add(amsg); - } - } + private void validateHostNameOnDefaultL3Network(String tag, String hostname, String l3Uuid) { + List vos = querySystemTagsByL3(tag, l3Uuid); - new While<>(msgs).all((amsg, completion) ->{ - bus.send(amsg, new CloudBusCallBack(completion) { - @Override - public void run(MessageReply r) { - if (r.isSuccess()){ - AllocatePrimaryStorageDryRunReply re = r.castReply(); - if (amsg.getImageUuid() != null){ - reply.setRootVolumePrimaryStorages(re.getPrimaryStorageInventories()); - } else if (amsg.getDiskOfferingUuid() != null) { - reply.getDataVolumePrimaryStorages().put(amsg.getDiskOfferingUuid(), re.getPrimaryStorageInventories()); - } else { - reply.getDataVolumePrimaryStorages().put(String.valueOf(amsg.getSize()), re.getPrimaryStorageInventories()); - } - } - completion.done(); + if (!vos.isEmpty()) { + SystemTagVO sameTag = vos.get(0); + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10246, "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)); } - }); - - }).run(new WhileDoneCompletion(msg) { - @Override - public void done(ErrorCodeList errorCodeList) { - bus.reply(msg, reply); } - }); - } - - private void handle(APICreateVmNicMsg msg) { - final APICreateVmNicEvent evt = new APICreateVmNicEvent(msg.getId()); - VmNicInventory nic = new VmNicInventory(); - VmNicVO nicVO = new VmNicVO(); - List ips = new ArrayList<>(); - - FlowChain flowChain = FlowChainBuilder.newSimpleFlowChain(); - flowChain.setName(String.format("create-nic-on-l3-network-%s", msg.getL3NetworkUuid())); - flowChain.then(new NoRollbackFlow() { - String __name__ = "create-nic-and-presist-to-db"; @Override - public void run(FlowTrigger trigger, Map data) { - int deviceId = 1; - String mac = MacOperator.generateMacWithDeviceId((short) deviceId); - nic.setUuid(Platform.getUuid()); - nic.setMac(mac); - nic.setDeviceId(deviceId); - nic.setType(VmInstanceConstant.VIRTUAL_NIC_TYPE); - for (NicManageExtensionPoint ext : pluginRgty.getExtensionList(NicManageExtensionPoint.class)) { - ext.beforeCreateNic(nic, msg); - } - - nicVO.setUuid(nic.getUuid()); - nicVO.setDeviceId(deviceId); - nicVO.setMac(nic.getMac()); - nicVO.setAccountUuid(msg.getSession().getAccountUuid()); - nicVO.setType(nic.getType()); + 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); - int tries = 5; - while (tries-- > 0) { - try { - new SQLBatch() { - @Override - protected void scripts() { - persist(nicVO); - } - }.execute(); - break; - } catch (PersistenceException e) { - if (ExceptionDSL.isCausedBy(e, SQLIntegrityConstraintViolationException.class, "Duplicate entry")) { - logger.debug(String.format("Concurrent mac allocation. Mac[%s] has been allocated, try allocating another one. " + - "The error[Duplicate entry] printed by jdbc.spi.SqlExceptionHelper is no harm, " + - "we will try finding another mac", nicVO.getMac())); - logger.trace("", e); - nicVO.setMac(MacOperator.generateMacWithDeviceId((short) nicVO.getDeviceId())); - } else { - throw e; - } - } + SimpleQuery q = dbf.createQuery(VmInstanceVO.class); + q.select(VmInstanceVO_.defaultL3NetworkUuid); + q.add(VmInstanceVO_.uuid, Op.EQ, resourceUuid); + String defaultL3Uuid = q.findValue(); + } else if (VmSystemTags.BOOT_ORDER.isMatch(systemTag)) { + validateBootOrder(systemTag); } - - trigger.next(); - } - }).then(new Flow() { - String __name__ = "allocate-nic-ip-and-mac"; - - @Override - public void run(FlowTrigger trigger, Map data) { - List errors = new ArrayList<>(); - L3NetworkVO l3NetworkVO = dbf.findByUuid(msg.getL3NetworkUuid(), L3NetworkVO.class); - new While<>(l3NetworkVO.getIpVersions()).each((version, wcomp) -> { - AllocateIpMsg allocateIpMsg = new AllocateIpMsg(); - allocateIpMsg.setL3NetworkUuid(msg.getL3NetworkUuid()); - allocateIpMsg.setRequiredIp(msg.getIp()); - if (msg.getIp() == null) { - allocateIpMsg.setRequiredIp(nic.getIp()); - } - allocateIpMsg.setIpVersion(version); - l3nm.updateIpAllocationMsg(allocateIpMsg, nic.getMac()); - bus.makeTargetServiceIdByResourceUuid(allocateIpMsg, L3NetworkConstant.SERVICE_ID, msg.getL3NetworkUuid()); - - bus.send(allocateIpMsg, new CloudBusCallBack(wcomp) { - @Override - public void run(MessageReply reply) { - if (!reply.isSuccess()) { - errors.add(reply.getError()); - wcomp.allDone(); - return; - } - - AllocateIpReply aReply = reply.castReply(); - UsedIpInventory ipInventory = aReply.getIpInventory(); - ips.add(ipInventory); - for (VmNicExtensionPoint ext : pluginRgty.getExtensionList(VmNicExtensionPoint.class)) { - ext.afterAddIpAddress(nic.getUuid(), ipInventory.getUuid()); - } - - if (nic.getL3NetworkUuid() == null) { - nic.setL3NetworkUuid(aReply.getIpInventory().getL3NetworkUuid()); - } - if (nic.getUsedIpUuid() == null) { - nic.setUsedIpUuid(aReply.getIpInventory().getUuid()); - } - /* TODO, to support nic driver type*/ - wcomp.done(); - } - }); - }).run(new WhileDoneCompletion(trigger) { - @Override - public void done(ErrorCodeList errorCodeList) { - if (errors.size() > 0) { - trigger.fail(errors.get(0)); - } else { - trigger.next(); - } - } - }); - } - @Override - public void rollback(FlowRollback trigger, Map data) { - if (!ips.isEmpty()) { - List rmsgs = new ArrayList<>(); - for (UsedIpInventory ip : ips) { - ReturnIpMsg rmsg = new ReturnIpMsg(); - rmsg.setL3NetworkUuid(ip.getL3NetworkUuid()); - rmsg.setUsedIpUuid(ip.getUuid()); - bus.makeTargetServiceIdByResourceUuid(rmsg, L3NetworkConstant.SERVICE_ID, ip.getL3NetworkUuid()); - rmsgs.add(rmsg); + 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(ORG_ZSTACK_COMPUTE_VM_10247, "invalid boot device[%s] in boot order[%s]", o, order)); } - - new While<>(rmsgs).step((rmsg, wcomp) -> { - bus.send(rmsg, new CloudBusCallBack(wcomp) { - @Override - public void run(MessageReply reply) { - wcomp.done(); - - } - }); - }, 2).run(new WhileDoneCompletion(trigger) { - @Override - public void done(ErrorCodeList errorCodeList) { - dbf.removeByPrimaryKey(nic.getUuid(), VmNicVO.class); - trigger.rollback(); - } - }); - } else { - dbf.removeByPrimaryKey(nic.getUuid(), VmNicVO.class); - trigger.rollback(); } } - }); - - flowChain.done(new FlowDoneHandler(msg) { - @Override - public void handle(Map data) { - tagMgr.createTagsFromAPICreateMessage(msg, nic.getUuid(), VmNicVO.class.getSimpleName()); - evt.setInventory(VmNicInventory.valueOf(dbf.reload(nicVO))); - bus.publish(evt); - } - }).error(new FlowErrorHandler(msg) { - @Override - public void handle(ErrorCode errCode, Map data) { - evt.setError(errCode); - bus.publish(evt); - } - }).start(); - } - - private void handle(APIGetVmNicAttachedNetworkServiceMsg msg) { - APIGetVmNicAttachedNetworkServiceReply reply = new APIGetVmNicAttachedNetworkServiceReply(); - List networkServices = new ArrayList<>(); - VmNicVO nicVO = Q.New(VmNicVO.class).eq(VmNicVO_.uuid, msg.getVmNicUuid()).find(); - for (VmNicChangeNetworkExtensionPoint extension : pluginRgty.getExtensionList(VmNicChangeNetworkExtensionPoint.class)) { - Map ret = extension.getVmNicAttachedNetworkService(VmNicInventory.valueOf(nicVO)); - if (ret == null) { - continue; - } - networkServices.addAll(ret.keySet()); } - reply.setNetworkServices(networkServices); - bus.reply(msg, reply); - } - private void instantiateTagsForCreateMessage(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, VmInstanceVO finalVo) { - if (cmsg != null) { - tagMgr.createTagsFromAPICreateMessage(cmsg, finalVo.getUuid(), VmInstanceVO.class.getSimpleName()); - } else { - tagMgr.createTags(msg.getSystemTags(), msg.getUserTags(), finalVo.getUuid(), VmInstanceVO.class.getSimpleName()); - } + HostNameValidator hostnameValidator = new HostNameValidator(); + tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), hostnameValidator); + VmSystemTags.HOSTNAME.installValidator(hostnameValidator); - boolean isVirtio = false; - if (!CollectionUtils.isEmpty(msg.getDiskAOs())) { - isVirtio = msg.getVirtio(); - } else { - if (Q.New(ImageVO.class).eq(ImageVO_.uuid, msg.getImageUuid()).eq(ImageVO_.virtio, true).isExists()) { - isVirtio = true; + // TODO: system tags should support token format validation + VmHardwareSystemTags.CPU_SOCKETS.installValidator((resourceUuid, resourceType, systemTag) -> { + String sockets = VmHardwareSystemTags.CPU_SOCKETS.getTokenByTag(systemTag, VmHardwareSystemTags.CPU_SOCKETS_TOKEN); + try { + Integer.valueOf(sockets); + } catch (NumberFormatException e) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10248, "cpuSockets must be an integer")); } - } - if (isVirtio) { - SystemTagCreator creator = VmSystemTags.VIRTIO.newSystemTagCreator(finalVo.getUuid()); - creator.recreate = true; - creator.inherent = false; - creator.tag = VmSystemTags.VIRTIO.getTagFormat(); - creator.create(); - } - - if (finalVo.getInstanceOfferingUuid() != null) { - tagMgr.copySystemTag( - finalVo.getInstanceOfferingUuid(), - InstanceOfferingVO.class.getSimpleName(), - finalVo.getUuid(), - VmInstanceVO.class.getSimpleName(), false); - } - - if (msg.getImageUuid() != null) { - tagMgr.copySystemTag( - msg.getImageUuid(), - ImageVO.class.getSimpleName(), - finalVo.getUuid(), - VmInstanceVO.class.getSimpleName(), false); - } - - if (ImageArchitecture.aarch64.toString().equals(finalVo.getArchitecture())) { - SystemTagCreator creator = VmSystemTags.MACHINE_TYPE.newSystemTagCreator(finalVo.getUuid()); - creator.setTagByTokens(map(e(VmSystemTags.MACHINE_TYPE_TOKEN, VmMachineType.virt.toString()))); - creator.recreate = true; - creator.create(); - } - - SystemTagCreator creator = VmSystemTags.SYNC_PORTS.newSystemTagCreator(finalVo.getUuid()); - creator.recreate = true; - creator.setTagByTokens(map(e(VmSystemTags.SYNC_PORTS_TOKEN, finalVo.getUuid()))); - creator.create(); - } - - private List extEmitterHandleSystemTag(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, VmInstanceVO finalVo) { - List result = Collections.emptyList(); - if (msg == null) { - result.add(operr(ORG_ZSTACK_COMPUTE_VM_10238, "CreateVmInstanceMsg cannot be null")); - return result; - } else if (cmsg != null && cmsg.getSystemTags() != null && !cmsg.getSystemTags().isEmpty()) { - return extEmitter.handleSystemTag(finalVo.getUuid(), cmsg.getSystemTags()); - } else if (cmsg == null && msg.getSystemTags() != null && !msg.getSystemTags().isEmpty()) { - return extEmitter.handleSystemTag(finalVo.getUuid(), msg.getSystemTags()); - } - return result; - } - - private List extEmitterHandleSshKeyPair(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, VmInstanceVO finalVo) { - List result = Collections.emptyList(); - if (msg == null) { - result.add(operr(ORG_ZSTACK_COMPUTE_VM_10239, "CreateVmInstanceMsg cannot be null")); - return result; - } else if (msg.getSshKeyPairUuids() != null && !msg.getSshKeyPairUuids().isEmpty()) { - return extEmitter.associateSshKeyPair(finalVo.getUuid(), msg.getSshKeyPairUuids()); - } - return result; - } - - protected void doCreateVmInstance(final CreateVmInstanceMsg msg, final APICreateMessage cmsg, ReturnValueCompletion completion) { - pluginRgty.getExtensionList(VmInstanceCreateExtensionPoint.class).forEach(extensionPoint -> { - extensionPoint.preCreateVmInstance(msg); }); - final ImageVO image = Q.New(ImageVO.class).eq(ImageVO_.uuid, msg.getImageUuid()).find(); - 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.setImageUuid(msg.getImageUuid()); - vo.setInstanceOfferingUuid(msg.getInstanceOfferingUuid()); - vo.setState(VmInstanceState.Created); - vo.setZoneUuid(msg.getZoneUuid()); - vo.setInternalId(dbf.generateSequenceNumber(VmInstanceSequenceNumberVO.class)); - vo.setDefaultL3NetworkUuid(msg.getDefaultL3NetworkUuid()); - vo.setCpuNum(msg.getCpuNum()); - vo.setCpuSpeed(msg.getCpuSpeed()); - vo.setMemorySize(msg.getMemorySize()); - vo.setReservedMemorySize(msg.getReservedMemorySize()); - vo.setAllocatorStrategy(msg.getAllocatorStrategy()); - vo.setPlatform(msg.getPlatform() != null ? msg.getPlatform() : image.getPlatform().toString()); - vo.setGuestOsType(msg.getGuestOsType() != null ? msg.getGuestOsType() : image.getGuestOsType()); - vo.setArchitecture(msg.getArchitecture() != null ? msg.getArchitecture() : image.getArchitecture()); - 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() { - @Override - protected VmInstanceVO scripts() { - finalVo.setAccountUuid(msg.getAccountUuid()); - factory.createVmInstance(finalVo, msg); - - return reload(finalVo); - } - }.execute(); - - FlowChain chain = FlowChainBuilder.newShareFlowChain(); - chain.setName(String.format("do-create-vmInstance-%s", vo.getUuid())); - chain.then(new ShareFlow() { - VmInstanceInventory instantiateVm; - List otherDisks = new ArrayList<>(); - boolean attachOtherDisk = false; - - @Override - public void setup() { - if (!CollectionUtils.isEmpty(msg.getDiskAOs())) { - otherDisks = msg.getDiskAOs().stream().filter(diskAO -> !diskAO.isBoot()).collect(Collectors.toList()); - setDiskAOsName(otherDisks); - attachOtherDisk = !otherDisks.isEmpty(); - } - - flow(new Flow() { - List errorCodes = new ArrayList<>(); - String __name__ = String.format("instantiate-systemTag-for-vm-%s", finalVo.getUuid()); - - @Override - public void run(FlowTrigger trigger, Map data) { - try { - instantiateTagsForCreateMessage(msg, cmsg, finalVo); - } catch (Exception e) { - errorCodes.add(operr(ORG_ZSTACK_COMPUTE_VM_10240, "instantiate system tag for vm failed because %s", e.getMessage())); - } - if (!errorCodes.isEmpty()) { - trigger.fail(errorCodes.get(0)); - return; - } - - errorCodes = extEmitterHandleSystemTag(msg, cmsg, finalVo); - if (!errorCodes.isEmpty()) { - trigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10241, "handle system tag fail when creating vm because [%s]", - StringUtils.join(errorCodes.stream().map(ErrorCode::getDescription).collect(Collectors.toList()), - ", "))); - return; - } - trigger.next(); - } - - @Override - public void rollback(FlowRollback trigger, Map data) { - if (Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, finalVo.getUuid()).isExists()) { - dbf.removeByPrimaryKey(finalVo.getUuid(), VmInstanceVO.class); - } - trigger.rollback(); - } - }); - - flow(new Flow() { - List errorCodes = Collections.emptyList(); - String __name__ = String.format("instantiate-ssh-key-pair-for-vm-%s", finalVo.getUuid()); - - @Override - public void run(FlowTrigger trigger, Map data) { - errorCodes = extEmitterHandleSshKeyPair(msg, cmsg, finalVo); - if (!errorCodes.isEmpty()) { - trigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10242, "handle sshkeypair fail when creating vm because [%s]", - StringUtils.join(errorCodes.stream().map(ErrorCode::getDetails).collect(Collectors.toList()), - ", "))); - return; - } - trigger.next(); - } - - @Override - public void rollback(FlowRollback trigger, Map data) { - if (Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, finalVo.getUuid()).isExists()) { - dbf.removeByPrimaryKey(finalVo.getUuid(), VmInstanceVO.class); - } - trigger.rollback(); - } - }); - - flow(new Flow() { - String __name__ = "instantiate-new-created-vmInstance"; - - @Override - public void run(FlowTrigger trigger, Map data) { - InstantiateNewCreatedVmInstanceMsg smsg = new InstantiateNewCreatedVmInstanceMsg(); - smsg.setDisableL3Networks(msg.getDisableL3Networks()); - smsg.setHostUuid(msg.getHostUuid()); - List temporaryDiskOfferingUuids = createDiskOfferingUuidsFromDataDiskSizes(msg, finalVo.getUuid()); - smsg.setDataDiskOfferingUuids(merge(msg.getDataDiskOfferingUuids(), temporaryDiskOfferingUuids)); - smsg.setDataVolumeTemplateUuids(msg.getDataVolumeTemplateUuids()); - smsg.setDataVolumeFromTemplateSystemTags(msg.getDataVolumeFromTemplateSystemTags()); - smsg.setL3NetworkUuids(msg.getL3NetworkSpecs()); - - if (msg.getRootDiskOfferingUuid() != null) { - smsg.setRootDiskOfferingUuid(msg.getRootDiskOfferingUuid()); - } else if (msg.getRootDiskSize() > 0) { - DiskOfferingVO dvo = getDiskOfferingVO(); - dbf.persist(dvo); - smsg.setRootDiskOfferingUuid(dvo.getUuid()); - temporaryDiskOfferingUuids.add(dvo.getUuid()); - } - - smsg.setVmInstanceInventory(VmInstanceInventory.valueOf(finalVo)); - smsg.setCandidatePrimaryStorageUuidsForDataVolume(msg.getCandidatePrimaryStorageUuidsForDataVolume()); - smsg.setCandidatePrimaryStorageUuidsForRootVolume(msg.getCandidatePrimaryStorageUuidsForRootVolume()); - if (Objects.equals(msg.getStrategy(), VmCreationStrategy.InstantStart.toString()) && attachOtherDisk) { - smsg.setStrategy(VmCreationStrategy.CreateStopped.toString()); - } else { - smsg.setStrategy(msg.getStrategy()); - } - - smsg.setTimeout(msg.getTimeout()); - smsg.setRootVolumeSystemTags(msg.getRootVolumeSystemTags()); - smsg.setDataVolumeSystemTags(msg.getDataVolumeSystemTags()); - smsg.setDataVolumeSystemTagsOnIndex(msg.getDataVolumeSystemTagsOnIndex()); - smsg.setDiskAOs(msg.getDiskAOs()); - bus.makeTargetServiceIdByResourceUuid(smsg, VmInstanceConstant.SERVICE_ID, finalVo.getUuid()); - bus.send(smsg, new CloudBusCallBack(smsg) { - @Override - public void run(MessageReply reply) { - if (!temporaryDiskOfferingUuids.isEmpty()) { - dbf.removeByPrimaryKeys(temporaryDiskOfferingUuids, DiskOfferingVO.class); - } - - if (reply.isSuccess()) { - InstantiateNewCreatedVmInstanceReply r = (InstantiateNewCreatedVmInstanceReply) reply; - instantiateVm = r.getVmInventory(); - data.put(VmInstanceInventory.class.getSimpleName(), instantiateVm); - trigger.next(); - return; - } - trigger.fail(reply.getError()); - } - }); - } - - @NotNull - private DiskOfferingVO getDiskOfferingVO() { - DiskOfferingVO dvo = new DiskOfferingVO(); - dvo.setUuid(Platform.getUuid()); - dvo.setAccountUuid(msg.getAccountUuid()); - dvo.setDiskSize(msg.getRootDiskSize()); - dvo.setName("for-create-vm-" + finalVo.getUuid()); - dvo.setType("TemporaryDiskOfferingType"); - dvo.setState(DiskOfferingState.Enabled); - dvo.setAllocatorStrategy(PrimaryStorageConstant.DEFAULT_PRIMARY_STORAGE_ALLOCATION_STRATEGY_TYPE); - return dvo; - } - - @Override - public void rollback(FlowRollback chain, Map data) { - if (instantiateVm == null) { - chain.rollback(); - return; - } - DestroyVmInstanceMsg dmsg = new DestroyVmInstanceMsg(); - dmsg.setVmInstanceUuid(finalVo.getUuid()); - dmsg.setDeletionPolicy(VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy.Direct); - bus.makeTargetServiceIdByResourceUuid(dmsg, VmInstanceConstant.SERVICE_ID, finalVo.getUuid()); - bus.send(dmsg, new CloudBusCallBack(null) { - @Override - public void run(MessageReply reply) { - if (!reply.isSuccess()) { - logger.warn(String.format("failed to delete vm [%s]", instantiateVm.getUuid())); - } - chain.rollback(); - } - }); - } - }); - - - if (!CollectionUtils.isEmpty(otherDisks)) { - otherDisks.forEach(diskAO -> flow(new VmInstantiateOtherDiskFlow(diskAO))); - } - - if (Objects.equals(msg.getStrategy(), VmCreationStrategy.InstantStart.toString()) && attachOtherDisk) { - flow(new NoRollbackFlow() { - String __name__ = "start-vm"; - - @Override - public void run(FlowTrigger trigger, Map data) { - StartVmInstanceMsg smsg = new StartVmInstanceMsg(); - smsg.setVmInstanceUuid(instantiateVm.getUuid()); - smsg.setHostUuid(instantiateVm.getLastHostUuid()); - bus.makeTargetServiceIdByResourceUuid(smsg, VmInstanceConstant.SERVICE_ID, finalVo.getUuid()); - bus.send(smsg, new CloudBusCallBack(trigger) { - @Override - public void run(MessageReply reply) { - if (!reply.isSuccess()) { - trigger.fail(reply.getError()); - return; - } - trigger.next(); - } - }); - } - }); - } - - done(new FlowDoneHandler(completion) { - @Override - public void handle(Map data) { - completion.success(instantiateVm); - } - }); - - error(new FlowErrorHandler(completion) { - @Override - public void handle(ErrorCode errCode, Map data) { - completion.fail(errCode); - } - }); - } - - private void setDiskAOsName(List diskAOs) { - AtomicInteger count = new AtomicInteger(1); - diskAOs.stream().filter(diskAO -> diskAO.getSourceUuid() == null).filter(diskAO -> diskAO.getName() == null) - .forEach(diskAO -> { - diskAO.setName(String.format("DATA-for-%s-%d", finalVo.getName(), count.get())); - count.getAndIncrement(); - }); - } - }).start(); - } - - private List createDiskOfferingUuidsFromDataDiskSizes(final CreateVmInstanceMsg msg, String vmUuid) { - if (CollectionUtils.isEmpty(msg.getDataDiskSizes())){ - return new ArrayList(); - } - List diskOfferingUuids = new ArrayList<>(); - List diskOfferingVos = new ArrayList<>(); - List volumeSizes = msg.getDataDiskSizes().stream().distinct().collect(Collectors.toList()); - Map sizeDiskOfferingMap = new HashMap<>(); - for (Long size : volumeSizes) { - DiskOfferingVO dvo = new DiskOfferingVO(); - dvo.setUuid(Platform.getUuid()); - dvo.setAccountUuid(msg.getAccountUuid()); - dvo.setDiskSize(size); - dvo.setName(String.format("create-data-volume-for-vm-%s", vmUuid)); - dvo.setType("TemporaryDiskOfferingType"); - dvo.setState(DiskOfferingState.Enabled); - dvo.setAllocatorStrategy(PrimaryStorageConstant.DEFAULT_PRIMARY_STORAGE_ALLOCATION_STRATEGY_TYPE); - diskOfferingVos.add(dvo); - sizeDiskOfferingMap.put(size, dvo.getUuid()); - } - msg.getDataDiskSizes().forEach(size -> diskOfferingUuids.add(sizeDiskOfferingMap.get(size))); - dbf.persistCollection(diskOfferingVos); - return diskOfferingUuids; - } - - private void handle(final CreateVmInstanceMsg msg) { - if(msg.getZoneUuid() == null && !CollectionUtils.isEmpty(msg.getL3NetworkSpecs())){ - String l3Uuid = VmNicSpec.getL3UuidsOfSpec(msg.getL3NetworkSpecs()).get(0); - String zoneUuid = Q.New(L3NetworkVO.class) - .select(L3NetworkVO_.zoneUuid) - .eq(L3NetworkVO_.uuid, l3Uuid) - .findValue(); - msg.setZoneUuid(zoneUuid); - } - - doCreateVmInstance(msg, null, new ReturnValueCompletion(msg) { - @Override - public void success(VmInstanceInventory inv) { - CreateVmInstanceReply reply = new CreateVmInstanceReply(); - reply.setInventory(inv); - bus.reply(msg, reply); + VmHardwareSystemTags.CPU_CORES.installValidator((resourceUuid, resourceType, systemTag) -> { + String cores = VmHardwareSystemTags.CPU_CORES.getTokenByTag(systemTag, VmHardwareSystemTags.CPU_CORES_TOKEN); + try { + Integer.valueOf(cores); + } catch (NumberFormatException e) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10249, "cpuCores must be an integer")); } + }); - @Override - public void fail(ErrorCode errorCode) { - CreateVmInstanceReply r = new CreateVmInstanceReply(); - r.setError(errorCode); - bus.reply(msg, r); + VmHardwareSystemTags.CPU_THREADS.installValidator((resourceUuid, resourceType, systemTag) -> { + String threads = VmHardwareSystemTags.CPU_THREADS.getTokenByTag(systemTag, VmHardwareSystemTags.CPU_THREADS_TOKEN); + try { + Integer.valueOf(threads); + } catch (NumberFormatException e) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10250, "cpuThreads must be an integer")); } }); } - private void handle(final APICreateVmInstanceMsg msg) { - doCreateVmInstance(VmInstanceUtils.fromAPICreateVmInstanceMsg(msg), msg, new ReturnValueCompletion(msg) { - APICreateVmInstanceEvent evt = new APICreateVmInstanceEvent(msg.getId()); + private void installUserdataValidator() { + class UserDataValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - @Override - public void success(VmInstanceInventory inv) { - evt.setInventory(inv); - bus.publish(evt); + private void check(String resourceUuid, Class resourceType) { + int existUserdataTagCount = VmSystemTags.USERDATA.getTags(resourceUuid, resourceType).size(); + if (existUserdataTagCount > 0) { + throw new OperationFailureException(argerr( + ORG_ZSTACK_COMPUTE_VM_10251, "Already have one userdata systemTag for vm[uuid: %s].", + resourceUuid)); + } } - @Override - public void fail(ErrorCode errorCode) { - evt.setError(errorCode); - bus.publish(evt); + private void checkUserdataDecode(String systemTag) { + String userdata = VmSystemTags.USERDATA.getTokenByTag(systemTag, VmSystemTags.USERDATA_TOKEN); + Base64.getDecoder().decode(userdata.getBytes()); } - }); - } - private void doDeleteVmNic(VmNicInventory nic, Completion completion) { - thdf.chainSubmit(new ChainTask(completion) { - @Override - public String getSyncSignature() { - return getVmNicSyncSignature(nic.getUuid()); + private void validUserdataFormat(String systemTag) { + VmSystemTags.UserdataTagOutputHandler handler = new VmSystemTags.UserdataTagOutputHandler(); + handler.desensitizeTag(VmSystemTags.USERDATA, systemTag); } - @Override - public void run(SyncTaskChain chain) { - if (nic.getVmInstanceUuid() == null) { - FlowChain fchain = FlowChainBuilder.newSimpleFlowChain(); - fchain.setName(String.format("detach-network-service-from-vmnic-%s", nic.getUuid())); - for (ReleaseNetworkServiceOnDeletingNicExtensionPoint ext : pluginRgty.getExtensionList(ReleaseNetworkServiceOnDeletingNicExtensionPoint.class)) { - fchain.then(new NoRollbackFlow() { - @Override - public void run(FlowTrigger trigger, Map data) { - ext.releaseNetworkServiceOnDeletingNic(nic, new NoErrorCompletion(trigger) { - @Override - public void done() { - trigger.next(); - } - }); - } - }); - } - fchain.then(new NoRollbackFlow() { - @Override - public void run(FlowTrigger trigger, Map data) { - List msgs = new ArrayList<>(); - for (UsedIpInventory ip : nic.getUsedIps()) { - ReturnIpMsg returnIpMsg = new ReturnIpMsg(); - returnIpMsg.setUsedIpUuid(ip.getUuid()); - returnIpMsg.setL3NetworkUuid(ip.getL3NetworkUuid()); - bus.makeTargetServiceIdByResourceUuid(returnIpMsg, L3NetworkConstant.SERVICE_ID, ip.getL3NetworkUuid()); - msgs.add(returnIpMsg); - } - new While<>(msgs).all((msg, com) -> bus.send(msg, new CloudBusCallBack(com) { - @Override - public void run(MessageReply reply) { - if (!reply.isSuccess()) { - logger.warn(String.format("failed to return ip address[uuid: %s]", msg.getUsedIpUuid())); - } - com.done(); - } - })).run(new WhileDoneCompletion(trigger) { - @Override - public void done(ErrorCodeList errorCodeList) { - for (NicManageExtensionPoint ext : pluginRgty.getExtensionList(NicManageExtensionPoint.class)) { - ext.beforeDeleteNic(nic); - } - dbf.removeByPrimaryKey(nic.getUuid(), VmNicVO.class); - trigger.next(); - } - }); - } - }); - fchain.done(new FlowDoneHandler(completion) { - @Override - public void handle(Map data) { - completion.success(); - } - }).error(new FlowErrorHandler(completion) { - @Override - public void handle(ErrorCode errCode, Map data) { - completion.fail(errCode); - } - }).start(); + @Override + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + if (!VmSystemTags.USERDATA.isMatch(systemTag)) { return; } - DetachNicFromVmMsg detachNicFromVmMsg = new DetachNicFromVmMsg(); - detachNicFromVmMsg.setVmInstanceUuid(nic.getVmInstanceUuid()); - detachNicFromVmMsg.setVmNicUuid(nic.getUuid()); - bus.makeTargetServiceIdByResourceUuid(detachNicFromVmMsg, VmInstanceConstant.SERVICE_ID, nic.getVmInstanceUuid()); - bus.send(detachNicFromVmMsg, new CloudBusCallBack(completion) { - @Override - public void run(MessageReply reply) { - if (!reply.isSuccess()) { - completion.fail(reply.getError()); - } else { - completion.success(); - } - } - }); - } - @Override - public String getName() { - return String.format("delete-vmNic-%s", nic.getUuid()); - } - }); - } - - private void handle(final APIDeleteVmNicMsg msg) { - APIDeleteVmNicEvent evt = new APIDeleteVmNicEvent(msg.getId()); - - VmNicVO nicVO = Q.New(VmNicVO.class).eq(VmNicVO_.uuid, msg.getUuid()).find(); - doDeleteVmNic(VmNicInventory.valueOf(nicVO), new Completion(msg) { - @Override - public void success() { - bus.publish(evt); + check(resourceUuid, resourceType); + checkUserdataDecode(systemTag); + validUserdataFormat(systemTag); } @Override - public void fail(ErrorCode errorCode) { - evt.setError(errorCode); - bus.publish(evt); - } - }); - } - - private String getVmNicSyncSignature(String nicUuid) { - return String.format("vmNic-%s", nicUuid); - } - - @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(); - } - - @Override - public boolean start() { - try { - createVmFlowChainBuilder(); - installSystemTagValidator(); - installGlobalConfigUpdater(); - vmExtensionManagers.addAll(pluginRgty.getExtensionList(VmInstanceExtensionManager.class)); - - bus.installBeforeDeliveryMessageInterceptor(new AbstractBeforeDeliveryMessageInterceptor() { - @Override - public void beforeDeliveryMessage(Message msg) { - if (msg instanceof NeedQuotaCheckMessage) { - if (((NeedQuotaCheckMessage) msg).getAccountUuid() == null || - ((NeedQuotaCheckMessage) msg).getAccountUuid().equals("")) { - // skip admin scheduler - return; - } - List quotas = acntMgr.getMessageQuotaMap().get(msg.getClass()); - if (quotas == null || quotas.size() == 0) { - return; - } - Map pairs = new QuotaUtil(). - makeQuotaPairs(((NeedQuotaCheckMessage) msg).getAccountUuid()); - for (Quota quota : quotas) { - quota.getOperator().checkQuota((NeedQuotaCheckMessage) msg, pairs); - } - } - } - }, StartVmInstanceMsg.class); - - deleteMigrateSystemTagWhenVmStateChangedToRunning(); - pluginRgty.saveExtensionAsMap(VmAttachOtherDiskExtensionPoint.class, new Function() { - @Override - public Object call(VmAttachOtherDiskExtensionPoint arg) { - return arg.getDiskType(); - } - }); - - 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(); - } - }); - VmGlobalConfig.MULTI_VNIC_SUPPORT.installBeforeUpdateExtension(new GlobalConfigBeforeUpdateExtensionPoint() { - @Override - public void beforeUpdateExtensionPoint(GlobalConfig oldConfig, String newValue) { - if (!oldConfig.value(Boolean.class) || "true".equalsIgnoreCase(newValue)) { - return; - } - - List tuples; - String sql = "select vmInstanceUuid, l3NetworkUuid, count(*) from VmNicVO group by vmInstanceUuid, l3NetworkUuid"; - TypedQuery q = dbf.getEntityManager().createQuery(sql, Tuple.class); - tuples = q.getResultList(); - if (tuples == null || tuples.isEmpty()) { - return; - } - for (Tuple tuple: tuples) { - if (tuple.get(2, Long.class) > 1) { - throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_VM_10243, "unable to enable this function. There are multi nics of L3 network[uuid:%s] in the vm[uuid: %s]", - tuple.get(0, String.class), tuple.get(1, String.class))); - } - } - } - }); - - // Note: VM_RESOURCE_BINGDING tag is silently deprecated (ZSTAC-75428) - // The tag data is preserved but no longer read or written by the new ResourceBindingAllocatorFlow - } - - private void installHostnameValidator() { - 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(ORG_ZSTACK_COMPUTE_VM_10244, "hostname[%s] specified in system tag[%s] is not a valid domain name", hostname, tag)); - } - } - - @Override - public void validateSystemTagInCreateMessage(APICreateMessage cmsg) { - final NewVmInstanceMessage msg = (NewVmInstanceMessage) cmsg; - - int hostnameCount = 0; - for (String sysTag : msg.getSystemTags()) { - if (VmSystemTags.HOSTNAME.isMatch(sysTag)) { - if (++hostnameCount > 1) { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10245, "only one hostname system tag is allowed, but %s got", hostnameCount)); - } - - String hostname = VmSystemTags.HOSTNAME.getTokenByTag(sysTag, VmSystemTags.HOSTNAME_TOKEN); - - validateHostname(sysTag, hostname); - } - } - } - - @Transactional(readOnly = true) - private List querySystemTagsByL3(String tag, 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 q = dbf.getEntityManager().createQuery(sql, SystemTagVO.class); - q.setParameter("l3Uuid", l3Uuid); - q.setParameter("sysTag", tag); - return q.getResultList(); - } - - private void validateHostNameOnDefaultL3Network(String tag, String hostname, String l3Uuid) { - List vos = querySystemTagsByL3(tag, l3Uuid); - - if (!vos.isEmpty()) { - SystemTagVO sameTag = vos.get(0); - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10246, "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 q = dbf.createQuery(VmInstanceVO.class); - q.select(VmInstanceVO_.defaultL3NetworkUuid); - q.add(VmInstanceVO_.uuid, Op.EQ, resourceUuid); - String defaultL3Uuid = q.findValue(); - } 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(ORG_ZSTACK_COMPUTE_VM_10247, "invalid boot device[%s] in boot order[%s]", o, order)); - } - } - } - } - - HostNameValidator hostnameValidator = new HostNameValidator(); - tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), hostnameValidator); - VmSystemTags.HOSTNAME.installValidator(hostnameValidator); - - // TODO: system tags should support token format validation - VmHardwareSystemTags.CPU_SOCKETS.installValidator((resourceUuid, resourceType, systemTag) -> { - String sockets = VmHardwareSystemTags.CPU_SOCKETS.getTokenByTag(systemTag, VmHardwareSystemTags.CPU_SOCKETS_TOKEN); - try { - Integer.valueOf(sockets); - } catch (NumberFormatException e) { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10248, "cpuSockets must be an integer")); - } - }); - - VmHardwareSystemTags.CPU_CORES.installValidator((resourceUuid, resourceType, systemTag) -> { - String cores = VmHardwareSystemTags.CPU_CORES.getTokenByTag(systemTag, VmHardwareSystemTags.CPU_CORES_TOKEN); - try { - Integer.valueOf(cores); - } catch (NumberFormatException e) { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10249, "cpuCores must be an integer")); - } - }); - - VmHardwareSystemTags.CPU_THREADS.installValidator((resourceUuid, resourceType, systemTag) -> { - String threads = VmHardwareSystemTags.CPU_THREADS.getTokenByTag(systemTag, VmHardwareSystemTags.CPU_THREADS_TOKEN); - try { - Integer.valueOf(threads); - } catch (NumberFormatException e) { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10250, "cpuThreads must be an integer")); - } - }); - } - - private void installUserdataValidator() { - class UserDataValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - - private void check(String resourceUuid, Class resourceType) { - int existUserdataTagCount = VmSystemTags.USERDATA.getTags(resourceUuid, resourceType).size(); - if (existUserdataTagCount > 0) { - throw new OperationFailureException(argerr( - ORG_ZSTACK_COMPUTE_VM_10251, "Already have one userdata systemTag for vm[uuid: %s].", - resourceUuid)); - } - } - - private void checkUserdataDecode(String systemTag) { - String userdata = VmSystemTags.USERDATA.getTokenByTag(systemTag, VmSystemTags.USERDATA_TOKEN); - Base64.getDecoder().decode(userdata.getBytes()); - } - - private void validUserdataFormat(String systemTag) { - VmSystemTags.UserdataTagOutputHandler handler = new VmSystemTags.UserdataTagOutputHandler(); - handler.desensitizeTag(VmSystemTags.USERDATA, systemTag); - } - - - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - if (!VmSystemTags.USERDATA.isMatch(systemTag)) { - return; - } - check(resourceUuid, resourceType); - checkUserdataDecode(systemTag); - validUserdataFormat(systemTag); - } - - @Override - public void validateSystemTagInCreateMessage(APICreateMessage msg) { - int userdataTagCount = 0; - for (String sysTag : msg.getSystemTags()) { - if (VmSystemTags.USERDATA.isMatch(sysTag)) { - if (userdataTagCount > 0) { - throw new OperationFailureException(argerr( - ORG_ZSTACK_COMPUTE_VM_10252, "Shouldn't be more than one userdata systemTag for one vm.")); - } - userdataTagCount++; + public void validateSystemTagInCreateMessage(APICreateMessage msg) { + int userdataTagCount = 0; + for (String sysTag : msg.getSystemTags()) { + if (VmSystemTags.USERDATA.isMatch(sysTag)) { + if (userdataTagCount > 0) { + throw new OperationFailureException(argerr( + ORG_ZSTACK_COMPUTE_VM_10252, "Shouldn't be more than one userdata systemTag for one vm.")); + } + userdataTagCount++; check(msg.getResourceUuid(), VmInstanceVO.class); checkUserdataDecode(sysTag); @@ -1890,910 +570,231 @@ public void validateSystemTagInCreateMessage(APICreateMessage msg) { return; } - validUserdataFormat(tagValue); - - } - } - } - } - - UserDataValidator userDataValidator = new UserDataValidator(); - tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), userDataValidator); - VmSystemTags.USERDATA.installValidator(userDataValidator); - } - - private void installBootModeValidator() { - class BootModeValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - if (!VmSystemTags.BOOT_MODE.isMatch(systemTag)) { - return; - } - - String bootMode = VmSystemTags.BOOT_MODE.getTokenByTag(systemTag, VmSystemTags.BOOT_MODE_TOKEN); - validateBootMode(systemTag, bootMode); - } - - @Override - public void validateSystemTagInCreateMessage(APICreateMessage msg) { - if (msg.getSystemTags() == null || msg.getSystemTags().isEmpty()) { - return; - } - - int bootModeCount = 0; - for (String systemTag : msg.getSystemTags()) { - if (VmSystemTags.BOOT_MODE.isMatch(systemTag)) { - if (++bootModeCount > 1) { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10253, "only one bootMode system tag is allowed, but %d got", bootModeCount)); - } - - String bootMode = VmSystemTags.BOOT_MODE.getTokenByTag(systemTag, VmSystemTags.BOOT_MODE_TOKEN); - validateBootMode(systemTag, bootMode); - } - } - } - - private void validateBootMode(String systemTag, String bootMode) { - boolean valid = false; - for (ImageBootMode bm : ImageBootMode.values()) { - if (bm.name().equalsIgnoreCase(bootMode)) { - valid = true; - break; - } - } - if (!valid) { - throw new ApiMessageInterceptionException(argerr( - ORG_ZSTACK_COMPUTE_VM_10254, "[%s] specified in system tag [%s] is not a valid boot mode", bootMode, systemTag) - ); - } - } - } - - BootModeValidator validator = new BootModeValidator(); - tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); - VmSystemTags.BOOT_MODE.installValidator(validator); - } - - private void installCleanTrafficValidator() { - class CleanTrafficValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - @Override - public void validateSystemTagInCreateMessage(APICreateMessage msg) { - if (msg instanceof APICreateVmInstanceMsg) { - Optional.ofNullable(msg.getSystemTags()).ifPresent(it -> { - if (it.stream().anyMatch(tag -> VmSystemTags.CLEAN_TRAFFIC.isMatch(tag))) { - validateVmType(null, ((APICreateVmInstanceMsg) msg).getType()); - } - }); - } - } - - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - validateVmType(resourceUuid, null); - } - - private void validateVmType(String vmUuid, String vmType) { - if (vmType == null) { - vmType = Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, vmUuid).select(VmInstanceVO_.type).findValue(); - } - - if (!VmInstanceConstant.USER_VM_TYPE.equals(vmType)) { - throw new ApiMessageInterceptionException(argerr( - ORG_ZSTACK_COMPUTE_VM_10255, "clean traffic is not supported for vm type [%s]", vmType) - ); - } - } - } - - CleanTrafficValidator validator = new CleanTrafficValidator(); - tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); - VmSystemTags.CLEAN_TRAFFIC.installValidator(validator); - } - - private void installMachineTypeValidator() { - class MachineTypeValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - @Override - public void validateSystemTagInCreateMessage(APICreateMessage msg) { - Optional.ofNullable(msg.getSystemTags()).ifPresent(it -> it.forEach(this::validateMachineType)); - } - - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - validateMachineType(systemTag); - } - - private void validateMachineType(String systemTag) { - if (!VmSystemTags.MACHINE_TYPE.isMatch(systemTag)) { - return; - } - - String type = VmSystemTags.MACHINE_TYPE.getTokenByTag(systemTag, VmSystemTags.MACHINE_TYPE_TOKEN); - if (VmMachineType.get(type) == null) { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10256, "vm machine type requires [q35, pc, virt], but get [%s]", type)); - } - } - } - - MachineTypeValidator validator = new MachineTypeValidator(); - tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); - VmSystemTags.MACHINE_TYPE.installValidator(validator); - } - - private void installL3NetworkSecurityGroupValidator() { - class L3NetworkSecurityGroupValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - - @Override - public void validateSystemTagInCreateMessage(APICreateMessage msg) { - if (msg.getSystemTags() == null || msg.getSystemTags().isEmpty()) { - return; - } - - for (String systemTag : msg.getSystemTags()) { - validateL3NetworkSecurityGroup(systemTag); - } - } - - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - validateL3NetworkSecurityGroup(systemTag); - } - - private void validateL3NetworkSecurityGroup(String systemTag) { - if (!VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF.isMatch(systemTag)) { - return; - } - - String l3Uuid = VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF.getTokenByTag(systemTag, VmSystemTags.L3_UUID_TOKEN); - List securityGroupUuids = asList(VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF - .getTokenByTag(systemTag, VmSystemTags.SECURITY_GROUP_UUIDS_TOKEN).split(",")); - - validateL3NetworkAttachSecurityGroup(l3Uuid, securityGroupUuids); - } - - private void validateL3NetworkAttachSecurityGroup(String l3Uuid, List securityGroupUuids) { - pluginRgty.getExtensionList(ValidateL3SecurityGroupExtensionPoint.class) - .forEach(ext -> ext.validateSystemtagL3SecurityGroup(l3Uuid, securityGroupUuids)); - } - } - - L3NetworkSecurityGroupValidator validator = new L3NetworkSecurityGroupValidator(); - tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); - VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF.installValidator(validator); - } - - private void installBooleanTagValidator(PatternedSystemTag tag, String tokenName, String tagDescription) { - tag.installValidator(new SystemTagValidator() { - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - String tokenValue = null; - if (tag.isMatch(systemTag)) { - tokenValue = tag.getTokenByTag(systemTag, tokenName); - } else { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10257, "invalid %s tag[%s]", tagDescription, systemTag)); - } - if (!isBoolean(tokenValue)) { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10258, "invalid %s[%s], value [%s] is not boolean", tagDescription, systemTag, tokenValue)); - } - } - private boolean isBoolean(String param) { - return "true".equalsIgnoreCase(param) || "false".equalsIgnoreCase(param); - } - }); - } - - private void installSeDeviceValidator() { - installBooleanTagValidator(VmSystemTags.SECURITY_ELEMENT_ENABLE, - VmSystemTags.SECURITY_ELEMENT_ENABLE_TOKEN, - "securityElementEnable"); - } - - private void installHygonSeDeviceValidator() { - installBooleanTagValidator(VmSystemTags.HYGON_SECURITY_ELEMENT_ENABLE, - VmSystemTags.HYGON_SECURITY_ELEMENT_ENABLE_TOKEN, - "hygonSecurityElementEnable"); - } - - private void installSystemTagValidator() { - installHostnameValidator(); - installUserdataValidator(); - installBootModeValidator(); - installCleanTrafficValidator(); - installMachineTypeValidator(); - installUsbRedirectValidator(); - installL3NetworkSecurityGroupValidator(); - installSeDeviceValidator(); - installHygonSeDeviceValidator(); - new StaticIpOperator().installStaticIpValidator(); - } - private void installUsbRedirectValidator() { - VmSystemTags.USB_REDIRECT.installValidator(new SystemTagValidator() { - @Override - public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { - String usbRedirectTokenByTag = null; - if (VmSystemTags.USB_REDIRECT.isMatch(systemTag)) { - usbRedirectTokenByTag = VmSystemTags.USB_REDIRECT.getTokenByTag(systemTag, VmSystemTags.USB_REDIRECT_TOKEN); - } else { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10259, "invalid usbRedirect[%s], %s is not usbRedirect tag", systemTag, usbRedirectTokenByTag)); - } - if (!isBoolean(usbRedirectTokenByTag)) { - throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10260, "invalid usbRedirect[%s], %s is not boolean class", systemTag, usbRedirectTokenByTag)); - } - } - private boolean isBoolean(String param) { - return "true".equalsIgnoreCase(param) || "false".equalsIgnoreCase(param); - } - }); - } - - @Override - public boolean stop() { - return true; - } - - @Override - public VmInstanceFactory getVmInstanceFactory(VmInstanceType type) { - VmInstanceFactory factory = vmFactoryManager.getVmInstanceFactory(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 vmFactoryManager.getVmInstanceBaseExtensionFactory(msg.getClass()); - } - - @Override - public VmInstanceNicFactory getVmInstanceNicFactory(VmNicType type) { - VmInstanceNicFactory factory = vmFactoryManager.getVmInstanceNicFactory(type.toString()); - if (factory == null) { - throw new CloudRuntimeException(String.format("No VmInstanceNicFactory of type[%s] found", type)); - } - return factory; - } - - @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 createVmWorkFlowElements) { - this.createVmWorkFlowElements = createVmWorkFlowElements; - } - - public void setStopVmWorkFlowElements(List stopVmWorkFlowElements) { - this.stopVmWorkFlowElements = stopVmWorkFlowElements; - } - - public void setRebootVmWorkFlowElements(List rebootVmWorkFlowElements) { - this.rebootVmWorkFlowElements = rebootVmWorkFlowElements; - } - - public void setStartVmWorkFlowElements(List startVmWorkFlowElements) { - this.startVmWorkFlowElements = startVmWorkFlowElements; - } - - public void setDestroyVmWorkFlowElements(List destroyVmWorkFlowElements) { - this.destroyVmWorkFlowElements = destroyVmWorkFlowElements; - } - - public void setMigrateVmWorkFlowElements(List migrateVmWorkFlowElements) { - this.migrateVmWorkFlowElements = migrateVmWorkFlowElements; - } - - public void setAttachVolumeWorkFlowElements(List attachVolumeWorkFlowElements) { - this.attachVolumeWorkFlowElements = attachVolumeWorkFlowElements; - } - - public void setAttachIsoWorkFlowElements(List attachIsoWorkFlowElements) { - this.attachIsoWorkFlowElements = attachIsoWorkFlowElements; - } - - public void setDetachIsoWorkFlowElements(List detachIsoWorkFlowElements) { - this.detachIsoWorkFlowElements = detachIsoWorkFlowElements; - } - - public void setExpungeVmWorkFlowElements(List expungeVmWorkFlowElements) { - this.expungeVmWorkFlowElements = expungeVmWorkFlowElements; - } - - public void setPauseVmWorkFlowElements(List pauseVmWorkFlowElements) { - this.pauseVmWorkFlowElements = pauseVmWorkFlowElements; - } - - public void setResumeVmWorkFlowElements(List resumeVmWorkFlowElements) { - this.resumeVmWorkFlowElements = resumeVmWorkFlowElements; - } - - @Override - public List reportQuota() { - Quota quota = new Quota(); - quota.defineQuota(new VmTotalNumQuotaDefinition()); - quota.defineQuota(new VmRunningNumQuotaDefinition()); - quota.defineQuota(new VmRunningCpuNumQuotaDefinition()); - quota.defineQuota(new VmRunningMemoryNumQuotaDefinition()); - quota.defineQuota(new DataVolumeNumQuotaDefinition()); - quota.defineQuota(new VolumeSizeQuotaDefinition()); - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APICreateVmInstanceMsg.class) - .addCounterQuota(VmQuotaConstant.VM_TOTAL_NUM) - .addCounterQuota(VmQuotaConstant.VM_RUNNING_NUM) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> { - if (msg.getCpuNum() != null) { - return Integer.toUnsignedLong(msg.getCpuNum()); - } - - Integer cpuNum = Q.New(InstanceOfferingVO.class) - .select(InstanceOfferingVO_.cpuNum) - .eq(InstanceOfferingVO_.uuid, msg.getInstanceOfferingUuid()) - .findValue(); - return Integer.toUnsignedLong(cpuNum); - }).addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> { - if (msg.getMemorySize() != null) { - return msg.getMemorySize(); - } - - return Q.New(InstanceOfferingVO.class) - .select(InstanceOfferingVO_.memorySize) - .eq(InstanceOfferingVO_.uuid, msg.getInstanceOfferingUuid()) - .findValue(); - }).addMessageRequiredQuotaHandler(VmQuotaConstant.DATA_VOLUME_NUM, (msg) -> { - if (msg.getDataDiskOfferingUuids() == null || msg.getDataDiskOfferingUuids().isEmpty()) { - return 0L; - } - - return (long) (msg.getDataDiskOfferingUuids().size()); - }).addMessageRequiredQuotaHandler(VmQuotaConstant.VOLUME_SIZE, (msg) -> { - long allVolumeSizeAsked = 0; - - String sql; - Long imgSize; - ImageConstant.ImageMediaType imgType = null; - if (msg.getImageUuid() != null) { - sql = "select img.size, img.mediaType" + - " from ImageVO img" + - " where img.uuid = :iuuid"; - TypedQuery iq = dbf.getEntityManager().createQuery(sql, Tuple.class); - iq.setParameter("iuuid", msg.getImageUuid()); - Tuple it = iq.getSingleResult(); - imgSize = it.get(0, Long.class); - imgType = it.get(1, ImageConstant.ImageMediaType.class); - } else { - imgSize = 0L; - } - - List diskOfferingUuids = new ArrayList<>(); - if (msg.getDataDiskOfferingUuids() != null && !msg.getDataDiskOfferingUuids().isEmpty()) { - diskOfferingUuids.addAll(msg.getDataDiskOfferingUuids()); - } - if (imgType == ImageConstant.ImageMediaType.RootVolumeTemplate) { - if (msg.getRootDiskOfferingUuid() != null) { - diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); - } else { - allVolumeSizeAsked += imgSize; - } - } else if (imgType == ImageConstant.ImageMediaType.ISO) { - if (msg.getRootDiskOfferingUuid() != null) { - diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); - } else if (msg.getRootDiskSize() != null) { - allVolumeSizeAsked += msg.getRootDiskSize(); - } else { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10261, "rootDiskOfferingUuid cannot be null when image mediaType is ISO")); - } - } else { - if (msg.getRootDiskOfferingUuid() != null) { - diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); - } else if (msg.getRootDiskSize() != null) { - allVolumeSizeAsked += msg.getRootDiskSize(); - } else { - throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10262, "rootDiskOfferingUuid cannot be null when create vm without image")); - } - } - - HashMap 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 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); - } - } - - return allVolumeSizeAsked; - }).addCheckCondition((msg) -> !msg.getStrategy().equals(VmCreationStrategy.JustCreate.toString()))); - - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIRecoverVmInstanceMsg.class) - .addCounterQuota(VmQuotaConstant.VM_TOTAL_NUM)); - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIStartVmInstanceMsg.class) - .addCounterQuota(VmQuotaConstant.VM_RUNNING_NUM) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> new VmQuotaUtil().getRequiredCpu(msg.getVmInstanceUuid())) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> new VmQuotaUtil().getRequiredMemory(msg.getVmInstanceUuid()))); - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(StartVmInstanceMsg.class) - .addCounterQuota(VmQuotaConstant.VM_RUNNING_NUM) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> new VmQuotaUtil().getRequiredCpu(msg.getVmInstanceUuid())) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> new VmQuotaUtil().getRequiredMemory(msg.getVmInstanceUuid()))); - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APICreateDataVolumeMsg.class) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VOLUME_SIZE, (msg) -> { - if (msg.getDiskOfferingUuid() == null) { - return msg.getDiskSize(); - } - - String sql = "select diskSize from DiskOfferingVO where uuid = :uuid "; - TypedQuery dq = dbf.getEntityManager().createQuery(sql, Long.class); - dq.setParameter("uuid", msg.getDiskOfferingUuid()); - Long dsize = dq.getSingleResult(); - dsize = dsize == null ? 0 : dsize; - return dsize; - }) - .addCounterQuota(VmQuotaConstant.DATA_VOLUME_NUM)); - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIRecoverDataVolumeMsg.class) - .addCounterQuota(VmQuotaConstant.DATA_VOLUME_NUM)); - - quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIChangeResourceOwnerMsg.class) - .addCheckCondition((msg) -> Q.New(VmInstanceVO.class) - .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) - .notEq(VmInstanceVO_.type, "baremetal2") - .isExists()) - .addCounterQuota(VmQuotaConstant.VM_TOTAL_NUM) - .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> { - VmInstanceState state = Q.New(VmInstanceVO.class) - .select(VmInstanceVO_.state) - .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) - .findValue(); - - // vm is running - if (list(VmInstanceState.Stopped, VmInstanceState.Destroying, - VmInstanceState.Destroyed, VmInstanceState.Created).contains(state)) { - return 0L; - } - - return new VmQuotaUtil().getRequiredCpu(msg.getResourceUuid()); - }).addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> { - VmInstanceState state = Q.New(VmInstanceVO.class) - .select(VmInstanceVO_.state) - .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) - .findValue(); - - // vm is running - if (list(VmInstanceState.Stopped, VmInstanceState.Destroying, - VmInstanceState.Destroyed, VmInstanceState.Created).contains(state)) { - return 0L; - } + validUserdataFormat(tagValue); - return new VmQuotaUtil().getRequiredMemory(msg.getResourceUuid()); - }).addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_NUM, (msg) -> { - VmInstanceState state = Q.New(VmInstanceVO.class) - .select(VmInstanceVO_.state) - .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) - .findValue(); - - // vm is running - if (list(VmInstanceState.Stopped, VmInstanceState.Destroying, - VmInstanceState.Destroyed, VmInstanceState.Created).contains(state)) { - return 0L; } + } + } + } - return 1L; - })); - return list(quota); - } - - @Override - @AsyncThread - public void managementNodeReady() { - //checkUnknownVm(); - startVmExpungeTask(); + UserDataValidator userDataValidator = new UserDataValidator(); + tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), userDataValidator); + VmSystemTags.USERDATA.installValidator(userDataValidator); } - private synchronized void startVmExpungeTask() { - if (expungeVmTask != null) { - expungeVmTask.cancel(true); - } - - expungeVmTask = thdf.submitCancelablePeriodicTask(new CancelablePeriodicTask() { - - private List 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 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 ts = q.listTuple(); - start += qun; - - for (Tuple t : ts) { - String vmUuid = t.get(0, String.class); - if (!destMaker.isManagedByUs(vmUuid)) { - continue; - } - ret.add(t); - } + private void installBootModeValidator() { + class BootModeValidator implements SystemTagCreateMessageValidator, SystemTagValidator { + @Override + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + if (!VmSystemTags.BOOT_MODE.isMatch(systemTag)) { + return; } - return ret; + String bootMode = VmSystemTags.BOOT_MODE.getTokenByTag(systemTag, VmSystemTags.BOOT_MODE_TOKEN); + validateBootMode(systemTag, bootMode); } @Override - public synchronized boolean run() { - final List vms = getVmDeletedStateManagedByUs(); - if (vms.isEmpty()) { - logger.debug("[VM Expunging Task]: no vm to expunge"); - return false; + public void validateSystemTagInCreateMessage(APICreateMessage msg) { + if (msg.getSystemTags() == null || msg.getSystemTags().isEmpty()) { + return; } - final Timestamp current = dbf.getCurrentSqlTime(); - - final List msgs = transformToList(vms, new Function() { - @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; + int bootModeCount = 0; + for (String systemTag : msg.getSystemTags()) { + if (VmSystemTags.BOOT_MODE.isMatch(systemTag)) { + if (++bootModeCount > 1) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10253, "only one bootMode system tag is allowed, but %d got", bootModeCount)); } - } - }); - if (msgs.isEmpty()) { - logger.debug("[VM Expunging Task]: no vm to expunge"); - return false; + String bootMode = VmSystemTags.BOOT_MODE.getTokenByTag(systemTag, VmSystemTags.BOOT_MODE_TOKEN); + validateBootMode(systemTag, bootMode); + } } + } - bus.send(msgs, 100, new CloudBusListCallBack(null) { - @Override - public void run(List 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())); - } - } + private void validateBootMode(String systemTag, String bootMode) { + boolean valid = false; + for (ImageBootMode bm : ImageBootMode.values()) { + if (bm.name().equalsIgnoreCase(bootMode)) { + valid = true; + break; } - }); - - return false; + } + if (!valid) { + throw new ApiMessageInterceptionException(argerr( + ORG_ZSTACK_COMPUTE_VM_10254, "[%s] specified in system tag [%s] is not a valid boot mode", bootMode, systemTag) + ); + } } + } - @Override - public TimeUnit getTimeUnit() { - return TimeUnit.SECONDS; - } + BootModeValidator validator = new BootModeValidator(); + tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); + VmSystemTags.BOOT_MODE.installValidator(validator); + } + private void installCleanTrafficValidator() { + class CleanTrafficValidator implements SystemTagCreateMessageValidator, SystemTagValidator { @Override - public long getInterval() { - return VmGlobalConfig.VM_EXPUNGE_INTERVAL.value(Long.class); + public void validateSystemTagInCreateMessage(APICreateMessage msg) { + if (msg instanceof APICreateVmInstanceMsg) { + Optional.ofNullable(msg.getSystemTags()).ifPresent(it -> { + if (it.stream().anyMatch(tag -> VmSystemTags.CLEAN_TRAFFIC.isMatch(tag))) { + validateVmType(null, ((APICreateVmInstanceMsg) msg).getType()); + } + }); + } } @Override - public String getName() { - return "expunge-vm-task"; + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + validateVmType(resourceUuid, null); } - }); - - 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; - } + private void validateVmType(String vmUuid, String vmType) { + if (vmType == null) { + vmType = Q.New(VmInstanceVO.class).eq(VmInstanceVO_.uuid, vmUuid).select(VmInstanceVO_.type).findValue(); + } - // change root volume - SimpleQuery 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; + if (!VmInstanceConstant.USER_VM_TYPE.equals(vmType)) { + throw new ApiMessageInterceptionException(argerr( + ORG_ZSTACK_COMPUTE_VM_10255, "clean traffic is not supported for vm type [%s]", vmType) + ); + } + } } - acntMgr.changeResourceOwner(rootVolumeUuid, newOwnerUuid); + CleanTrafficValidator validator = new CleanTrafficValidator(); + tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); + VmSystemTags.CLEAN_TRAFFIC.installValidator(validator); + } - // change vmnic(s) - SimpleQuery sq = dbf.createQuery(VmNicVO.class); - sq.select(VmNicVO_.uuid); - sq.add(VmNicVO_.vmInstanceUuid, Op.EQ, ref.getResourceUuid()); - List vmnics = sq.listValue(); - if (vmnics.isEmpty()) { - return; - } - for (String vmnicUuid : vmnics) { - acntMgr.changeResourceOwner(vmnicUuid, newOwnerUuid); - } + private void installMachineTypeValidator() { + class MachineTypeValidator implements SystemTagCreateMessageValidator, SystemTagValidator { + @Override + public void validateSystemTagInCreateMessage(APICreateMessage msg) { + Optional.ofNullable(msg.getSystemTags()).ifPresent(it -> it.forEach(this::validateMachineType)); + } - changeVmCdRomsOwner(ref.getResourceUuid(), newOwnerUuid); - } + @Override + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + validateMachineType(systemTag); + } - private void changeVmCdRomsOwner(String vmInstanceUuid, String newOwnerUuid) { - List vmCdRomUuids = Q.New(VmCdRomVO.class) - .select(VmCdRomVO_.uuid) - .eq(VmCdRomVO_.vmInstanceUuid, vmInstanceUuid) - .listValues(); - if (vmCdRomUuids.isEmpty()) { - return; - } + private void validateMachineType(String systemTag) { + if (!VmSystemTags.MACHINE_TYPE.isMatch(systemTag)) { + return; + } - for (String uuid :vmCdRomUuids) { - acntMgr.changeResourceOwner(uuid, newOwnerUuid); + String type = VmSystemTags.MACHINE_TYPE.getTokenByTag(systemTag, VmSystemTags.MACHINE_TYPE_TOKEN); + if (VmMachineType.get(type) == null) { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10256, "vm machine type requires [q35, pc, virt], but get [%s]", type)); + } + } } - } - @Override - public List getMessageClassToIntercept() { - return asList(APIChangeResourceOwnerMsg.class); + MachineTypeValidator validator = new MachineTypeValidator(); + tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); + VmSystemTags.MACHINE_TYPE.installValidator(validator); } - @Override - public InterceptorPosition getPosition() { - return InterceptorPosition.END; - } + private void installL3NetworkSecurityGroupValidator() { + class L3NetworkSecurityGroupValidator implements SystemTagCreateMessageValidator, SystemTagValidator { - @Override - public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException { - if (msg instanceof APIChangeResourceOwnerMsg) { - validateAPIChangeResourceOwnerMsg((APIChangeResourceOwnerMsg) msg); - } + @Override + public void validateSystemTagInCreateMessage(APICreateMessage msg) { + if (msg.getSystemTags() == null || msg.getSystemTags().isEmpty()) { + return; + } - return msg; - } + for (String systemTag : msg.getSystemTags()) { + validateL3NetworkSecurityGroup(systemTag); + } + } - private void validateAPIChangeResourceOwnerMsg(APIChangeResourceOwnerMsg msg) { - SimpleQuery q = dbf.createQuery(AccountResourceRefVO.class); - q.add(AccountResourceRefVO_.resourceUuid, Op.EQ, msg.getResourceUuid()); - AccountResourceRefVO ref = q.find(); + @Override + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + validateL3NetworkSecurityGroup(systemTag); + } - if (ref == null || !VolumeVO.class.getSimpleName().equals(ref.getResourceType())) { - return; - } + private void validateL3NetworkSecurityGroup(String systemTag) { + if (!VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF.isMatch(systemTag)) { + return; + } - SimpleQuery 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(ORG_ZSTACK_COMPUTE_VM_10263, "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())); - } - } + String l3Uuid = VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF.getTokenByTag(systemTag, VmSystemTags.L3_UUID_TOKEN); + List securityGroupUuids = asList(VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF + .getTokenByTag(systemTag, VmSystemTags.SECURITY_GROUP_UUIDS_TOKEN).split(",")); - @Override - public void afterChangeHostStatus(String hostUuid, HostStatus before, HostStatus next) { - if(next == HostStatus.Disconnected) { - List vms = Q.New(VmInstanceVO.class).select(VmInstanceVO_.uuid, VmInstanceVO_.state) - .eq(VmInstanceVO_.hostUuid, hostUuid) - .listTuple(); - if(vms.isEmpty()){ - return; + validateL3NetworkAttachSecurityGroup(l3Uuid, securityGroupUuids); } - new While<>(vms).step((vm, completion) -> { - String vmUuid = vm.get(0, String.class); - String vmState = vm.get(1, VmInstanceState.class).toString(); - 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()){ - logger.warn(String.format("the host[uuid:%s] disconnected, but the vm[uuid:%s] fails to " + - "change it's state to Unknown, %s", hostUuid, vmUuid, reply.getError())); - logger.warn(String.format("create an unknowngc job for vm[uuid:%s]", vmUuid)); - - UnknownVmGC gc = new UnknownVmGC(); - gc.NAME = UnknownVmGC.getGCName(vmUuid); - gc.vmUuid = vmUuid; - gc.vmState = vmState; - gc.hostUuid = hostUuid; - if (gc.existedAndNotCompleted()) { - logger.debug(String.format("There is already a UnknownVmGC of vm[uuid:%s] " + - "on host[uuid:%s], skip.", vmUuid, hostUuid)); - } else { - gc.submit(VmGlobalConfig.UNKNOWN_GC_INTERVAL.value(Long.class), TimeUnit.SECONDS); - } - } else { - logger.debug(String.format("the host[uuid:%s] disconnected, change the VM[uuid:%s]' state to Unknown", hostUuid, vmUuid)); - } - completion.done(); - } - }); - }, 20).run(new NopeWhileDoneCompletion()); + private void validateL3NetworkAttachSecurityGroup(String l3Uuid, List securityGroupUuids) { + pluginRgty.getExtensionList(ValidateL3SecurityGroupExtensionPoint.class) + .forEach(ext -> ext.validateSystemtagL3SecurityGroup(l3Uuid, securityGroupUuids)); + } } - } - @Override - public void beforeMigrateVm(VmInstanceInventory inv, String destHostUuid) { + L3NetworkSecurityGroupValidator validator = new L3NetworkSecurityGroupValidator(); + tagMgr.installCreateMessageValidator(VmInstanceVO.class.getSimpleName(), validator); + VmSystemTags.L3_NETWORK_SECURITY_GROUP_UUIDS_REF.installValidator(validator); } - @Override - public void afterMigrateVm(VmInstanceInventory inv, String srcHostUuid) { - if (!inv.getHypervisorType().equals(VmInstanceConstant.KVM_HYPERVISOR_TYPE)) { - return; - } - - VmPriorityLevel level = new VmPriorityOperator().getVmPriority(inv.getUuid()); - VmPriorityConfigVO priorityVO = Q.New(VmPriorityConfigVO.class).eq(VmPriorityConfigVO_.level, level).find(); - - UpdateVmPriorityMsg msg = new UpdateVmPriorityMsg(); - msg.setPriorityConfigStructs(asList(new PriorityConfigStruct(priorityVO, inv.getUuid()))); - msg.setHostUuid(inv.getHostUuid()); - bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, inv.getHostUuid()); - bus.send(msg, new CloudBusCallBack(msg) { + private void installBooleanTagValidator(PatternedSystemTag tag, String tokenName, String tagDescription) { + tag.installValidator(new SystemTagValidator() { @Override - public void run(MessageReply reply) { - UpdateVmPriorityReply r = new UpdateVmPriorityReply(); - if (!reply.isSuccess()) { - logger.warn(String.format("update vm[%s] priority to [%s] failed,because %s", - inv.getUuid(), level.toString(), reply.getError())); + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + String tokenValue = null; + if (tag.isMatch(systemTag)) { + tokenValue = tag.getTokenByTag(systemTag, tokenName); + } else { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10257, "invalid %s tag[%s]", tagDescription, systemTag)); + } + if (!isBoolean(tokenValue)) { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10258, "invalid %s[%s], value [%s] is not boolean", tagDescription, systemTag, tokenValue)); } } + private boolean isBoolean(String param) { + return "true".equalsIgnoreCase(param) || "false".equalsIgnoreCase(param); + } }); } - @Override - public void failedToMigrateVm(VmInstanceInventory inv, String destHostUuid, ErrorCode reason) { - - } - - @Override - public VmNicQosConfigBackend getVmNicQosConfigBackend(String type) { - return vmFactoryManager.getVmNicQosConfigBackend(type); + private void installSeDeviceValidator() { + installBooleanTagValidator(VmSystemTags.SECURITY_ELEMENT_ENABLE, + VmSystemTags.SECURITY_ELEMENT_ENABLE_TOKEN, + "securityElementEnable"); } - @Override - public ErrorCode handleSystemTag(String vmUuid, List tags) { - ErrorCode errorCode = handleResourceDirectorySystemTag(vmUuid, tags); - if (errorCode != null) { - return errorCode; - } - - errorCode = handleNumaSystemTag(vmUuid, tags); - - if (errorCode != null) { - return errorCode; - } - - return null; + private void installHygonSeDeviceValidator() { + installBooleanTagValidator(VmSystemTags.HYGON_SECURITY_ELEMENT_ENABLE, + VmSystemTags.HYGON_SECURITY_ELEMENT_ENABLE_TOKEN, + "hygonSecurityElementEnable"); } - private ErrorCode handleNumaSystemTag(String vmUuid, List tags) { - if (!VmSystemTags.NUMA.hasTag(vmUuid)) { - return null; - } - ResourceConfig rc = rcf.getResourceConfig(VmGlobalConfig.NUMA.getIdentity()); - rc.updateValue(vmUuid, Boolean.TRUE.toString()); - VmSystemTags.NUMA.delete(vmUuid); - - return null; - + private void installUsbRedirectValidator() { + VmSystemTags.USB_REDIRECT.installValidator(new SystemTagValidator() { + @Override + public void validateSystemTag(String resourceUuid, Class resourceType, String systemTag) { + String usbRedirectTokenByTag = null; + if (VmSystemTags.USB_REDIRECT.isMatch(systemTag)) { + usbRedirectTokenByTag = VmSystemTags.USB_REDIRECT.getTokenByTag(systemTag, VmSystemTags.USB_REDIRECT_TOKEN); + } else { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10259, "invalid usbRedirect[%s], %s is not usbRedirect tag", systemTag, usbRedirectTokenByTag)); + } + if (!isBoolean(usbRedirectTokenByTag)) { + throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10260, "invalid usbRedirect[%s], %s is not boolean class", systemTag, usbRedirectTokenByTag)); + } + } + private boolean isBoolean(String param) { + return "true".equalsIgnoreCase(param) || "false".equalsIgnoreCase(param); + } + }); } - //todo move to directory - private ErrorCode handleResourceDirectorySystemTag(String vmUuid, List tags) { - PatternedSystemTag tag = VmSystemTags.DIRECTORY_UUID; - String token = VmSystemTags.DIRECTORY_UUID_TOKEN; + // ==================== Event Listeners ==================== - String directoryUuid = SystemTagUtils.findTagValue(tags, tag, token); - if (StringUtils.isEmpty(directoryUuid)) { - return null; - } - ResourceDirectoryRefVO refVO = new ResourceDirectoryRefVO(); - refVO.setResourceUuid(vmUuid); - refVO.setDirectoryUuid(directoryUuid); - refVO.setResourceType(VmInstanceVO.class.getSimpleName()); - refVO.setLastOpDate(new Timestamp(new Date().getTime())); - refVO.setCreateDate(new Timestamp(new Date().getTime())); - dbf.persist(refVO); - return null; - } - public void deleteMigrateSystemTagWhenVmStateChangedToRunning() { evtf.onLocal(VmCanonicalEvents.VM_FULL_STATE_CHANGED_PATH, new EventCallback() { @Override diff --git a/compute/src/main/java/org/zstack/compute/vm/VmNicApiSubManager.java b/compute/src/main/java/org/zstack/compute/vm/VmNicApiSubManager.java new file mode 100644 index 0000000000..0e262aa2e4 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmNicApiSubManager.java @@ -0,0 +1,347 @@ +package org.zstack.compute.vm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.core.asyncbatch.While; +import org.zstack.core.cloudbus.CloudBus; +import org.zstack.core.cloudbus.CloudBusCallBack; +import org.zstack.core.componentloader.PluginRegistry; +import org.zstack.core.db.*; +import org.zstack.core.workflow.FlowChainBuilder; +import org.zstack.header.core.Completion; +import org.zstack.header.core.NoErrorCompletion; +import org.zstack.header.core.workflow.*; +import org.zstack.header.errorcode.ErrorCode; +import org.zstack.header.errorcode.ErrorCodeList; +import org.zstack.header.message.MessageReply; +import org.zstack.header.network.l3.*; +import org.zstack.header.vm.*; +import org.zstack.core.Platform; +import org.zstack.core.thread.ChainTask; +import org.zstack.core.thread.SyncTaskChain; +import org.zstack.core.thread.ThreadFacade; +import org.zstack.tag.TagManager; +import org.zstack.utils.ExceptionDSL; +import org.zstack.utils.Utils; +import org.zstack.utils.logging.CLogger; +import org.zstack.network.l3.L3NetworkManager; + +import javax.persistence.PersistenceException; +import java.sql.SQLIntegrityConstraintViolationException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Handles VM NIC API messages: create, get attached network service, delete. + * Extracted from VmInstanceManagerImpl to reduce God Class complexity. + */ +public class VmNicApiSubManager { + private static final CLogger logger = Utils.getLogger(VmNicApiSubManager.class); + + @Autowired + private CloudBus bus; + @Autowired + private DatabaseFacade dbf; + @Autowired + private PluginRegistry pluginRgty; + @Autowired + private TagManager tagMgr; + @Autowired + private ThreadFacade thdf; + @Autowired + protected L3NetworkManager l3nm; + + void handle(APICreateVmNicMsg msg) { + final APICreateVmNicEvent evt = new APICreateVmNicEvent(msg.getId()); + VmNicInventory nic = new VmNicInventory(); + VmNicVO nicVO = new VmNicVO(); + List ips = new ArrayList<>(); + + FlowChain flowChain = FlowChainBuilder.newSimpleFlowChain(); + flowChain.setName(String.format("create-nic-on-l3-network-%s", msg.getL3NetworkUuid())); + // DEBT: NoRollbackFlow -- create NIC and persist to DB, MAC retry on duplicate. Tracked: TODO + flowChain.then(new NoRollbackFlow() { + String __name__ = "create-nic-and-presist-to-db"; + + @Override + public void run(FlowTrigger trigger, Map data) { + int deviceId = 1; + String mac = MacOperator.generateMacWithDeviceId((short) deviceId); + nic.setUuid(Platform.getUuid()); + nic.setMac(mac); + nic.setDeviceId(deviceId); + nic.setType(VmInstanceConstant.VIRTUAL_NIC_TYPE); + for (NicManageExtensionPoint ext : pluginRgty.getExtensionList(NicManageExtensionPoint.class)) { + ext.beforeCreateNic(nic, msg); + } + + nicVO.setUuid(nic.getUuid()); + nicVO.setDeviceId(deviceId); + nicVO.setMac(nic.getMac()); + nicVO.setAccountUuid(msg.getSession().getAccountUuid()); + nicVO.setType(nic.getType()); + + int tries = 5; + while (tries-- > 0) { + try { + new SQLBatch() { + @Override + protected void scripts() { + persist(nicVO); + } + }.execute(); + break; + } catch (PersistenceException e) { + if (ExceptionDSL.isCausedBy(e, SQLIntegrityConstraintViolationException.class, "Duplicate entry")) { + logger.debug(String.format("Concurrent mac allocation. Mac[%s] has been allocated, try allocating another one. " + + "The error[Duplicate entry] printed by jdbc.spi.SqlExceptionHelper is no harm, " + + "we will try finding another mac", nicVO.getMac())); + logger.trace("", e); + nicVO.setMac(MacOperator.generateMacWithDeviceId((short) nicVO.getDeviceId())); + } else { + throw e; + } + } + } + + trigger.next(); + } + }).then(new Flow() { + String __name__ = "allocate-nic-ip-and-mac"; + + @Override + public void run(FlowTrigger trigger, Map data) { + List errors = new ArrayList<>(); + L3NetworkVO l3NetworkVO = dbf.findByUuid(msg.getL3NetworkUuid(), L3NetworkVO.class); + new While<>(l3NetworkVO.getIpVersions()).each((version, wcomp) -> { + AllocateIpMsg allocateIpMsg = new AllocateIpMsg(); + allocateIpMsg.setL3NetworkUuid(msg.getL3NetworkUuid()); + allocateIpMsg.setRequiredIp(msg.getIp()); + if (msg.getIp() == null) { + allocateIpMsg.setRequiredIp(nic.getIp()); + } + allocateIpMsg.setIpVersion(version); + l3nm.updateIpAllocationMsg(allocateIpMsg, nic.getMac()); + bus.makeTargetServiceIdByResourceUuid(allocateIpMsg, L3NetworkConstant.SERVICE_ID, msg.getL3NetworkUuid()); + + bus.send(allocateIpMsg, new CloudBusCallBack(wcomp) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + errors.add(reply.getError()); + wcomp.allDone(); + return; + } + + AllocateIpReply aReply = reply.castReply(); + UsedIpInventory ipInventory = aReply.getIpInventory(); + ips.add(ipInventory); + for (VmNicExtensionPoint ext : pluginRgty.getExtensionList(VmNicExtensionPoint.class)) { + ext.afterAddIpAddress(nic.getUuid(), ipInventory.getUuid()); + } + + if (nic.getL3NetworkUuid() == null) { + nic.setL3NetworkUuid(aReply.getIpInventory().getL3NetworkUuid()); + } + if (nic.getUsedIpUuid() == null) { + nic.setUsedIpUuid(aReply.getIpInventory().getUuid()); + } + /* TODO, to support nic driver type*/ + wcomp.done(); + } + }); + }).run(new WhileDoneCompletion(trigger) { + @Override + public void done(ErrorCodeList errorCodeList) { + if (errors.size() > 0) { + trigger.fail(errors.get(0)); + } else { + trigger.next(); + } + } + }); + + } + + @Override + public void rollback(FlowRollback trigger, Map data) { + if (!ips.isEmpty()) { + List rmsgs = new ArrayList<>(); + for (UsedIpInventory ip : ips) { + ReturnIpMsg rmsg = new ReturnIpMsg(); + rmsg.setL3NetworkUuid(ip.getL3NetworkUuid()); + rmsg.setUsedIpUuid(ip.getUuid()); + bus.makeTargetServiceIdByResourceUuid(rmsg, L3NetworkConstant.SERVICE_ID, ip.getL3NetworkUuid()); + rmsgs.add(rmsg); + } + + new While<>(rmsgs).step((rmsg, wcomp) -> { + bus.send(rmsg, new CloudBusCallBack(wcomp) { + @Override + public void run(MessageReply reply) { + wcomp.done(); + + } + }); + }, 2).run(new WhileDoneCompletion(trigger) { + @Override + public void done(ErrorCodeList errorCodeList) { + dbf.removeByPrimaryKey(nic.getUuid(), VmNicVO.class); + trigger.rollback(); + } + }); + } else { + dbf.removeByPrimaryKey(nic.getUuid(), VmNicVO.class); + trigger.rollback(); + } + } + }); + + flowChain.done(new FlowDoneHandler(msg) { + @Override + public void handle(Map data) { + tagMgr.createTagsFromAPICreateMessage(msg, nic.getUuid(), VmNicVO.class.getSimpleName()); + evt.setInventory(VmNicInventory.valueOf(dbf.reload(nicVO))); + bus.publish(evt); + } + }).error(new FlowErrorHandler(msg) { + @Override + public void handle(ErrorCode errCode, Map data) { + evt.setError(errCode); + bus.publish(evt); + } + }).start(); + } + + void handle(APIGetVmNicAttachedNetworkServiceMsg msg) { + APIGetVmNicAttachedNetworkServiceReply reply = new APIGetVmNicAttachedNetworkServiceReply(); + List networkServices = new ArrayList<>(); + VmNicVO nicVO = Q.New(VmNicVO.class).eq(VmNicVO_.uuid, msg.getVmNicUuid()).find(); + for (VmNicChangeNetworkExtensionPoint extension : pluginRgty.getExtensionList(VmNicChangeNetworkExtensionPoint.class)) { + Map ret = extension.getVmNicAttachedNetworkService(VmNicInventory.valueOf(nicVO)); + if (ret == null) { + continue; + } + networkServices.addAll(ret.keySet()); + } + reply.setNetworkServices(networkServices); + bus.reply(msg, reply); + } + + void handle(final APIDeleteVmNicMsg msg) { + APIDeleteVmNicEvent evt = new APIDeleteVmNicEvent(msg.getId()); + + VmNicVO nicVO = Q.New(VmNicVO.class).eq(VmNicVO_.uuid, msg.getUuid()).find(); + doDeleteVmNic(VmNicInventory.valueOf(nicVO), new Completion(msg) { + @Override + public void success() { + bus.publish(evt); + } + + @Override + public void fail(ErrorCode errorCode) { + evt.setError(errorCode); + bus.publish(evt); + } + }); + } + + private void doDeleteVmNic(VmNicInventory nic, Completion completion) { + thdf.chainSubmit(new ChainTask(completion) { + @Override + public String getSyncSignature() { + return getVmNicSyncSignature(nic.getUuid()); + } + + @Override + public void run(SyncTaskChain chain) { + if (nic.getVmInstanceUuid() == null) { + FlowChain fchain = FlowChainBuilder.newSimpleFlowChain(); + fchain.setName(String.format("detach-network-service-from-vmnic-%s", nic.getUuid())); + for (ReleaseNetworkServiceOnDeletingNicExtensionPoint ext : pluginRgty.getExtensionList(ReleaseNetworkServiceOnDeletingNicExtensionPoint.class)) { + // DEBT: NoRollbackFlow -- release network service on deleting NIC. Tracked: TODO + fchain.then(new NoRollbackFlow() { + @Override + public void run(FlowTrigger trigger, Map data) { + ext.releaseNetworkServiceOnDeletingNic(nic, new NoErrorCompletion(trigger) { + @Override + public void done() { + trigger.next(); + } + }); + } + }); + } + // DEBT: NoRollbackFlow -- return IP and delete NIC from DB. Tracked: TODO + fchain.then(new NoRollbackFlow() { + @Override + public void run(FlowTrigger trigger, Map data) { + List msgs = new ArrayList<>(); + for (UsedIpInventory ip : nic.getUsedIps()) { + ReturnIpMsg returnIpMsg = new ReturnIpMsg(); + returnIpMsg.setUsedIpUuid(ip.getUuid()); + returnIpMsg.setL3NetworkUuid(ip.getL3NetworkUuid()); + bus.makeTargetServiceIdByResourceUuid(returnIpMsg, L3NetworkConstant.SERVICE_ID, ip.getL3NetworkUuid()); + msgs.add(returnIpMsg); + } + new While<>(msgs).all((rmsg, com) -> bus.send(rmsg, new CloudBusCallBack(com) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + logger.warn(String.format("failed to return ip address[uuid: %s]", rmsg.getUsedIpUuid())); + } + com.done(); + } + })).run(new WhileDoneCompletion(trigger) { + @Override + public void done(ErrorCodeList errorCodeList) { + for (NicManageExtensionPoint ext : pluginRgty.getExtensionList(NicManageExtensionPoint.class)) { + ext.beforeDeleteNic(nic); + } + dbf.removeByPrimaryKey(nic.getUuid(), VmNicVO.class); + trigger.next(); + } + }); + } + }); + fchain.done(new FlowDoneHandler(completion) { + @Override + public void handle(Map data) { + completion.success(); + } + }).error(new FlowErrorHandler(completion) { + @Override + public void handle(ErrorCode errCode, Map data) { + completion.fail(errCode); + } + }).start(); + + return; + } + DetachNicFromVmMsg detachNicFromVmMsg = new DetachNicFromVmMsg(); + detachNicFromVmMsg.setVmInstanceUuid(nic.getVmInstanceUuid()); + detachNicFromVmMsg.setVmNicUuid(nic.getUuid()); + bus.makeTargetServiceIdByResourceUuid(detachNicFromVmMsg, VmInstanceConstant.SERVICE_ID, nic.getVmInstanceUuid()); + bus.send(detachNicFromVmMsg, new CloudBusCallBack(completion) { + @Override + public void run(MessageReply reply) { + if (!reply.isSuccess()) { + completion.fail(reply.getError()); + } else { + completion.success(); + } + } + }); + } + + @Override + public String getName() { + return String.format("delete-vmNic-%s", nic.getUuid()); + } + }); + } + + private String getVmNicSyncSignature(String nicUuid) { + return String.format("vmNic-%s", nicUuid); + } +} diff --git a/compute/src/main/java/org/zstack/compute/vm/VmQuotaSubManager.java b/compute/src/main/java/org/zstack/compute/vm/VmQuotaSubManager.java new file mode 100644 index 0000000000..56f430c1d3 --- /dev/null +++ b/compute/src/main/java/org/zstack/compute/vm/VmQuotaSubManager.java @@ -0,0 +1,221 @@ +package org.zstack.compute.vm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.compute.vm.quota.*; +import org.zstack.core.db.DatabaseFacade; +import org.zstack.core.db.Q; +import org.zstack.header.apimediator.ApiMessageInterceptionException; +import org.zstack.header.configuration.DiskOfferingVO; +import org.zstack.header.configuration.DiskOfferingVO_; +import org.zstack.header.configuration.InstanceOfferingVO; +import org.zstack.header.configuration.InstanceOfferingVO_; +import org.zstack.header.identity.*; +import org.zstack.header.identity.Quota.QuotaPair; +import org.zstack.header.identity.quota.QuotaMessageHandler; +import org.zstack.header.image.ImageConstant; +import org.zstack.header.vm.*; +import org.zstack.header.volume.APICreateDataVolumeMsg; +import org.zstack.header.volume.APIRecoverDataVolumeMsg; + +import javax.persistence.Tuple; +import javax.persistence.TypedQuery; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static org.zstack.core.Platform.argerr; +import static org.zstack.core.Platform.operr; +import static org.zstack.utils.CollectionDSL.list; +import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*; + +/** + * Manages VM quota definitions and quota message handler registration. + * Extracted from VmInstanceManagerImpl to reduce God Class complexity. + */ +public class VmQuotaSubManager implements ReportQuotaExtensionPoint { + + @Autowired + private DatabaseFacade dbf; + + @Override + public List reportQuota() { + Quota quota = new Quota(); + quota.defineQuota(new VmTotalNumQuotaDefinition()); + quota.defineQuota(new VmRunningNumQuotaDefinition()); + quota.defineQuota(new VmRunningCpuNumQuotaDefinition()); + quota.defineQuota(new VmRunningMemoryNumQuotaDefinition()); + quota.defineQuota(new DataVolumeNumQuotaDefinition()); + quota.defineQuota(new VolumeSizeQuotaDefinition()); + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APICreateVmInstanceMsg.class) + .addCounterQuota(VmQuotaConstant.VM_TOTAL_NUM) + .addCounterQuota(VmQuotaConstant.VM_RUNNING_NUM) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> { + if (msg.getCpuNum() != null) { + return Integer.toUnsignedLong(msg.getCpuNum()); + } + + Integer cpuNum = Q.New(InstanceOfferingVO.class) + .select(InstanceOfferingVO_.cpuNum) + .eq(InstanceOfferingVO_.uuid, msg.getInstanceOfferingUuid()) + .findValue(); + return Integer.toUnsignedLong(cpuNum); + }).addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> { + if (msg.getMemorySize() != null) { + return msg.getMemorySize(); + } + + return Q.New(InstanceOfferingVO.class) + .select(InstanceOfferingVO_.memorySize) + .eq(InstanceOfferingVO_.uuid, msg.getInstanceOfferingUuid()) + .findValue(); + }).addMessageRequiredQuotaHandler(VmQuotaConstant.DATA_VOLUME_NUM, (msg) -> { + if (msg.getDataDiskOfferingUuids() == null || msg.getDataDiskOfferingUuids().isEmpty()) { + return 0L; + } + + return (long) (msg.getDataDiskOfferingUuids().size()); + }).addMessageRequiredQuotaHandler(VmQuotaConstant.VOLUME_SIZE, (msg) -> { + long allVolumeSizeAsked = 0; + + String sql; + Long imgSize; + ImageConstant.ImageMediaType imgType = null; + if (msg.getImageUuid() != null) { + sql = "select img.size, img.mediaType" + + " from ImageVO img" + + " where img.uuid = :iuuid"; + TypedQuery iq = dbf.getEntityManager().createQuery(sql, Tuple.class); + iq.setParameter("iuuid", msg.getImageUuid()); + Tuple it = iq.getSingleResult(); + imgSize = it.get(0, Long.class); + imgType = it.get(1, ImageConstant.ImageMediaType.class); + } else { + imgSize = 0L; + } + + List diskOfferingUuids = new ArrayList<>(); + if (msg.getDataDiskOfferingUuids() != null && !msg.getDataDiskOfferingUuids().isEmpty()) { + diskOfferingUuids.addAll(msg.getDataDiskOfferingUuids()); + } + if (imgType == ImageConstant.ImageMediaType.RootVolumeTemplate) { + if (msg.getRootDiskOfferingUuid() != null) { + diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); + } else { + allVolumeSizeAsked += imgSize; + } + } else if (imgType == ImageConstant.ImageMediaType.ISO) { + if (msg.getRootDiskOfferingUuid() != null) { + diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); + } else if (msg.getRootDiskSize() != null) { + allVolumeSizeAsked += msg.getRootDiskSize(); + } else { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10261, "rootDiskOfferingUuid cannot be null when image mediaType is ISO")); + } + } else { + if (msg.getRootDiskOfferingUuid() != null) { + diskOfferingUuids.add(msg.getRootDiskOfferingUuid()); + } else if (msg.getRootDiskSize() != null) { + allVolumeSizeAsked += msg.getRootDiskSize(); + } else { + throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10262, "rootDiskOfferingUuid cannot be null when create vm without image")); + } + } + + HashMap 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 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); + } + } + + return allVolumeSizeAsked; + }).addCheckCondition((msg) -> !msg.getStrategy().equals(VmCreationStrategy.JustCreate.toString()))); + + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIRecoverVmInstanceMsg.class) + .addCounterQuota(VmQuotaConstant.VM_TOTAL_NUM)); + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIStartVmInstanceMsg.class) + .addCounterQuota(VmQuotaConstant.VM_RUNNING_NUM) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> new VmQuotaUtil().getRequiredCpu(msg.getVmInstanceUuid())) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> new VmQuotaUtil().getRequiredMemory(msg.getVmInstanceUuid()))); + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(StartVmInstanceMsg.class) + .addCounterQuota(VmQuotaConstant.VM_RUNNING_NUM) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> new VmQuotaUtil().getRequiredCpu(msg.getVmInstanceUuid())) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> new VmQuotaUtil().getRequiredMemory(msg.getVmInstanceUuid()))); + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APICreateDataVolumeMsg.class) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VOLUME_SIZE, (msg) -> { + if (msg.getDiskOfferingUuid() == null) { + return msg.getDiskSize(); + } + + String sql = "select diskSize from DiskOfferingVO where uuid = :uuid "; + TypedQuery dq = dbf.getEntityManager().createQuery(sql, Long.class); + dq.setParameter("uuid", msg.getDiskOfferingUuid()); + Long dsize = dq.getSingleResult(); + dsize = dsize == null ? 0 : dsize; + return dsize; + }) + .addCounterQuota(VmQuotaConstant.DATA_VOLUME_NUM)); + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIRecoverDataVolumeMsg.class) + .addCounterQuota(VmQuotaConstant.DATA_VOLUME_NUM)); + + quota.addQuotaMessageChecker(new QuotaMessageHandler<>(APIChangeResourceOwnerMsg.class) + .addCheckCondition((msg) -> Q.New(VmInstanceVO.class) + .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) + .notEq(VmInstanceVO_.type, "baremetal2") + .isExists()) + .addCounterQuota(VmQuotaConstant.VM_TOTAL_NUM) + .addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_CPU_NUM, (msg) -> { + VmInstanceState state = Q.New(VmInstanceVO.class) + .select(VmInstanceVO_.state) + .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) + .findValue(); + + // vm is running + if (list(VmInstanceState.Stopped, VmInstanceState.Destroying, + VmInstanceState.Destroyed, VmInstanceState.Created).contains(state)) { + return 0L; + } + + return new VmQuotaUtil().getRequiredCpu(msg.getResourceUuid()); + }).addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_MEMORY_SIZE, (msg) -> { + VmInstanceState state = Q.New(VmInstanceVO.class) + .select(VmInstanceVO_.state) + .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) + .findValue(); + + // vm is running + if (list(VmInstanceState.Stopped, VmInstanceState.Destroying, + VmInstanceState.Destroyed, VmInstanceState.Created).contains(state)) { + return 0L; + } + + return new VmQuotaUtil().getRequiredMemory(msg.getResourceUuid()); + }).addMessageRequiredQuotaHandler(VmQuotaConstant.VM_RUNNING_NUM, (msg) -> { + VmInstanceState state = Q.New(VmInstanceVO.class) + .select(VmInstanceVO_.state) + .eq(VmInstanceVO_.uuid, msg.getResourceUuid()) + .findValue(); + + // vm is running + if (list(VmInstanceState.Stopped, VmInstanceState.Destroying, + VmInstanceState.Destroyed, VmInstanceState.Created).contains(state)) { + return 0L; + } + + return 1L; + })); + return list(quota); + } +} diff --git a/conf/springConfigXml/VmInstanceManager.xml b/conf/springConfigXml/VmInstanceManager.xml index ef3d5a7cc9..a5388cbc51 100755 --- a/conf/springConfigXml/VmInstanceManager.xml +++ b/conf/springConfigXml/VmInstanceManager.xml @@ -29,7 +29,8 @@ - + + org.zstack.compute.vm.VmImageSelectBackupStorageFlow @@ -130,18 +131,43 @@ org.zstack.compute.vm.ResumeVmOnHypervisorFlow + + + + + + + - - + + + + + + + + + + + - - - - + + + + + + + + + + + + + @@ -217,7 +243,7 @@ - +