package org.ovirt.engine.ui.uicommonweb.models.vms; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.businessentities.StoragePool; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VmBase; import org.ovirt.engine.core.common.businessentities.VmNumaNode; import org.ovirt.engine.core.common.businessentities.comparators.DiskByDiskAliasComparator; import org.ovirt.engine.core.common.businessentities.storage.Disk; import org.ovirt.engine.core.common.queries.ConfigurationValues; import org.ovirt.engine.core.common.queries.IdQueryParameters; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.utils.CommonCompatibilityVersionUtils; import org.ovirt.engine.core.common.validation.VmActionByVmOriginTypeValidator; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.StringHelper; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.ui.frontend.AsyncQuery; import org.ovirt.engine.ui.frontend.Frontend; import org.ovirt.engine.ui.uicommonweb.builders.BuilderExecutor; import org.ovirt.engine.ui.uicommonweb.builders.vm.CommentVmBaseToUnitBuilder; import org.ovirt.engine.ui.uicommonweb.builders.vm.CommonVmBaseToUnitBuilder; import org.ovirt.engine.ui.uicommonweb.builders.vm.NameAndDescriptionVmBaseToUnitBuilder; import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider; import org.ovirt.engine.ui.uicommonweb.models.SystemTreeItemModel; import org.ovirt.engine.ui.uicommonweb.models.vms.instancetypes.ExistingVmInstanceTypeManager; import org.ovirt.engine.ui.uicommonweb.models.vms.instancetypes.InstanceTypeManager; public class ExistingVmModelBehavior extends VmModelBehaviorBase<UnitVmModel> { private InstanceTypeManager instanceTypeManager; protected VM vm; private int hostCpu; private VDS runningOnHost; public ExistingVmModelBehavior(VM vm) { this.vm = vm; dedicatedHostsNames = Collections.emptyList(); } public VM getVm() { return vm; } public void setVm(VM vm) { this.vm = vm; } @Override public void initialize(SystemTreeItemModel systemTreeSelectedItem) { super.initialize(systemTreeSelectedItem); toggleAutoSetVmHostname(); getModel().getVmInitEnabled().setEntity(getVm().getVmInit() != null); getModel().getVmInitModel().init(getVm().getStaticData()); getModel().getVmType().setIsChangeable(true); getModel().getIsSoundcardEnabled().setIsChangeable(true); getModel().getInstanceTypes().setIsChangeable(!vm.isRunning()); getModel().getLabelList().setIsAvailable(true); getModel().getVmId().setIsAvailable(true); getModel().getVmId().setIsChangeable(false); loadDataCenter(); instanceTypeManager = new ExistingVmInstanceTypeManager(getModel(), vm); if (vm.getVmPoolId() != null) { instanceTypeManager.setAlwaysEnabledFieldUpdate(true); } Frontend.getInstance().runQuery(VdcQueryType.GetVmNumaNodesByVmId, new IdQueryParameters(vm.getId()), new AsyncQuery<VdcQueryReturnValue>(returnValue -> { List<VmNumaNode> nodes = returnValue.getReturnValue(); getModel().setVmNumaNodes(nodes); getModel().updateNodeCount(nodes.size()); })); // load dedicated host names into host names list if (getVm().getDedicatedVmForVdsList().size() > 0) { Frontend.getInstance().runQuery(VdcQueryType.GetAllHostNamesPinnedToVmById, new IdQueryParameters(vm.getId()), asyncQuery((VdcQueryReturnValue returnValue) -> setDedicatedHostsNames((List<String>) returnValue.getReturnValue()))); } } private void loadDataCenter() { // Preinitialize the VM compatibility version because it's needed during init Version newCustomCompatibilityVersion = ((ExistingVmModelBehavior) getModel().getBehavior()).getVm().getStaticData().getCustomCompatibilityVersion(); if (newCustomCompatibilityVersion != null) { getModel().getCustomCompatibilityVersion().setItems( Collections.singletonList(newCustomCompatibilityVersion), newCustomCompatibilityVersion); } AsyncDataProvider.getInstance().getDataCenterById(asyncQuery( dataCenter -> { if (dataCenter != null) { final List<StoragePool> dataCenters = new ArrayList<>(Arrays.asList(new StoragePool[]{dataCenter})); initClusters(dataCenters); } else { ExistingVmModelBehavior behavior = (ExistingVmModelBehavior) getModel().getBehavior(); VM currentVm = behavior.vm; Cluster cluster = new Cluster(); cluster.setId(currentVm.getClusterId()); cluster.setName(currentVm.getClusterName()); cluster.setCompatibilityVersion(currentVm.getClusterCompatibilityVersion()); cluster.setStoragePoolId(currentVm.getStoragePoolId()); DataCenterWithCluster dataCenterWithCluster = new DataCenterWithCluster(null, cluster); getModel().getDataCenterWithClustersList().setItems(Arrays.asList(dataCenterWithCluster)); getModel().getDataCenterWithClustersList().setSelectedItem(dataCenterWithCluster); behavior.initTemplate(); behavior.initCdImage(); } }), vm.getStoragePoolId()); } protected void initClusters(final List<StoragePool> dataCenters) { AsyncDataProvider.getInstance().getClusterListByService( asyncQuery(clusters -> { List<Cluster> filteredClusters = AsyncDataProvider.getInstance().filterByArchitecture(clusters, vm.getClusterArch()); getModel().setDataCentersAndClusters(getModel(), dataCenters, filteredClusters, vm.getClusterId()); updateCompatibilityVersion(); initTemplate(); initCdImage(); }), true, false); } @Override public void templateWithVersion_SelectedItemChanged() { // This method will be called even if a VM created from Blank template. // Update model state according to VM properties. buildModel(vm.getStaticData(), (source, destination) -> { getModel().getIsStateless().setIsAvailable(vm.getVmPoolId() == null); getModel().getIsRunAndPause().setIsAvailable(vm.getVmPoolId() == null); getModel().getCpuSharesAmount().setEntity(vm.getCpuShares()); updateCpuSharesSelection(); updateRngDevice(getVm().getId()); updateTimeZone(vm.getTimeZone()); updateGraphics(vm.getId()); getModel().getHostCpu().setEntity(vm.isUseHostCpuFlags()); // Storage domain and provisioning are not available for an existing VM. getModel().getStorageDomain().setIsChangeable(false); getModel().getProvisioning().setIsAvailable(false); getModel().getProvisioning().setEntity(Guid.Empty.equals(vm.getVmtGuid())); getModel().getCpuPinning().setEntity(vm.getCpuPinning()); getModel().getCustomPropertySheet().deserialize(vm.getCustomProperties()); if (isHotSetCpuSupported()) { // cancel related events while fetching data getModel().getTotalCPUCores().getEntityChangedEvent().removeListener(getModel()); getModel().getCoresPerSocket().getSelectedItemChangedEvent().removeListener(getModel()); getModel().getThreadsPerCore().getSelectedItemChangedEvent().removeListener(getModel()); getModel().getNumOfSockets().getSelectedItemChangedEvent().removeListener(getModel()); AsyncDataProvider.getInstance().getHostById(new AsyncQuery<>(returnValue -> { runningOnHost = returnValue; hostCpu = calculateHostCpus(); updateNumOfSockets(); }), vm.getRunOnVds()); } updateCpuProfile(vm.getClusterId(), vm.getCpuProfileId()); }); } @Override protected void buildModel(VmBase vm, BuilderExecutor.BuilderExecutionFinished<VmBase, UnitVmModel> callback) { new BuilderExecutor<>(callback, new NameAndDescriptionVmBaseToUnitBuilder(), new CommentVmBaseToUnitBuilder(), new CommonVmBaseToUnitBuilder()) .build(vm, getModel()); } @Override public void dataCenterWithClusterSelectedItemChanged() { super.dataCenterWithClusterSelectedItemChanged(); if (getModel().getSelectedCluster() != null) { updateCpuProfile(getModel().getSelectedCluster().getId(), vm.getCpuProfileId()); if (isInStateWithMemoryVolume(getVm()) && !isRestoreMemoryVolumeSupported()) { getModel().getEditingEnabled().setMessage(getModel().constants.suspendedVMsWhenClusterChange()); } } } private boolean isInStateWithMemoryVolume(VM vm) { return EnumSet.of(VMStatus.Suspended, VMStatus.SavingState, VMStatus.RestoringState).contains(vm.getStatus()); } private boolean isRestoreMemoryVolumeSupported() { Version oldVmEffectiveVersion = getVm().getCompatibilityVersion(); // before edit Version newVmCustomCompatibilityVersion = getModel().getCustomCompatibilityVersion() == null ? null : getModel().getCustomCompatibilityVersion().getSelectedItem(); Version newClusterVersion = getModel().getSelectedCluster() == null ? null : getModel().getSelectedCluster().getCompatibilityVersion(); Version newVmEffectiveVersion = CommonCompatibilityVersionUtils.getEffective(newVmCustomCompatibilityVersion, newClusterVersion, Version.getLast()); return oldVmEffectiveVersion.equals(newVmEffectiveVersion); } private int calculateHostCpus() { return getModel().getSelectedCluster().getCountThreadsAsCores() ? runningOnHost.getCpuThreads() : runningOnHost.getCpuCores(); } @Override public void postDataCenterWithClusterSelectedItemChanged() { updateDefaultHost(); updateCustomPropertySheet(); updateNumOfSockets(); updateQuotaByCluster(vm.getQuotaId(), vm.getQuotaName()); updateCpuPinningVisibility(); updateMemoryBalloon(); updateCpuSharesAvailability(); updateVirtioScsiAvailability(); updateOSValues(); updateInstanceImages(); updateLeaseStorageDomains(vm.getLeaseStorageDomainId()); instanceTypeManager.updateAll(); } private void updateInstanceImages() { AsyncDataProvider.getInstance().getVmDiskList(asyncQuery(disks -> { List<InstanceImageLineModel> imageLineModels = new ArrayList<>(); boolean isChangeable = vm == null || VmActionByVmOriginTypeValidator.isCommandAllowed(vm, VdcActionType.UpdateVmDisk); Collections.sort(disks, new DiskByDiskAliasComparator()); for (Disk disk : disks) { InstanceImageLineModel lineModel = new InstanceImageLineModel(getModel().getInstanceImages()); lineModel.initialize(disk, getVm()); lineModel.setEnabled(isChangeable); imageLineModels.add(lineModel); } getModel().getInstanceImages().setIsChangeable(isChangeable); getModel().getInstanceImages().setItems(imageLineModels); getModel().getInstanceImages().setVm(getVm()); }), getVm().getId()); } @Override protected void changeDefaultHost() { super.changeDefaultHost(); doChangeDefaultHost(vm.getDedicatedVmForVdsList()); } @Override public void defaultHost_SelectedItemChanged() { updateCdImage(); } @Override public void provisioning_SelectedItemChanged() { } @Override public void oSType_SelectedItemChanged() { super.oSType_SelectedItemChanged(); Integer osType = getModel().getOSType().getSelectedItem(); if (osType != null) { Guid id = basedOnCustomInstanceType() ? vm.getId() : getModel().getInstanceTypes().getSelectedItem().getId(); updateVirtioScsiEnabledWithoutDetach(id, osType); } } @Override public void updateMinAllocatedMemory() { if (getModel().getMemSize().getEntity() == null) { return; } Cluster cluster = getModel().getSelectedCluster(); if (cluster == null) { return; } double overCommitFactor = 100.0 / cluster.getMaxVdsMemoryOverCommit(); getModel().getMinAllocatedMemory() .setEntity((int) (getModel().getMemSize().getEntity() * overCommitFactor)); } protected void initTemplate() { setupTemplateWithVersion(vm.getVmtGuid(), vm.isUseLatestVersion(), false); } public void initCdImage() { getModel().getCdImage().setSelectedItem(vm.getIsoPath()); boolean hasCd = !StringHelper.isNullOrEmpty(vm.getIsoPath()); getModel().getCdImage().setIsChangeable(hasCd); getModel().getCdAttached().setEntity(hasCd); updateCdImage(); } private void toggleAutoSetVmHostname() { if (getVm().getVmInit() != null && vm.getName() != null && !vm.getName().equals(getVm().getVmInit().getHostname())) { getModel().getVmInitModel().disableAutoSetHostname(); } } private void addCpuListeners() { getModel().getTotalCPUCores().getEntityChangedEvent().addListener(getModel()); getModel().getNumOfSockets().getSelectedItemChangedEvent().addListener(getModel()); } private void removeCpuListeners() {// remove if exists getModel().getTotalCPUCores().getEntityChangedEvent().removeListener(getModel()); getModel().getNumOfSockets().getSelectedItemChangedEvent().removeListener(getModel()); } @Override public void numOfSocketChanged() { if (isHotSetCpuSupported()) { int numOfSockets = extractIntFromListModel(getModel().getNumOfSockets()); int coresPerSocket = vm.getCpuPerSocket(); int threadsPerCore = vm.getThreadsPerCpu(); removeCpuListeners(); getModel().getTotalCPUCores().setEntity(Integer.toString(numOfSockets * coresPerSocket * threadsPerCore)); addCpuListeners(); } else { super.numOfSocketChanged(); } } @Override public void totalCpuCoresChanged() { if (isHotSetCpuSupported()) { if (runningOnHost == null) { return; //async call didn't return with the host yet } int totalCpuCores = getTotalCpuCores(); if (totalCpuCores == 0) { return; } removeCpuListeners(); if ((totalCpuCores / vm.getThreadsPerCpu()) > getHostCpu()) { totalCpuCores = getHostCpu() * vm.getThreadsPerCpu(); getModel().getTotalCPUCores().setEntity(Integer.toString(totalCpuCores)); } // Do not change threads/cores List<Integer> coresPerSockets = Arrays.asList(new Integer[]{vm.getCpuPerSocket()}); List<Integer> threadsPerCore = Arrays.asList(new Integer[]{vm.getThreadsPerCpu()}); getModel().getThreadsPerCore().setItems(threadsPerCore); getModel().getCoresPerSocket().setItems(coresPerSockets); getModel().getThreadsPerCore().setSelectedItem(vm.getThreadsPerCpu()); getModel().getCoresPerSocket().setSelectedItem(vm.getCpuPerSocket()); // change num of sockets getModel().getNumOfSockets().setItems(createSocketsRange()); int sockets = totalCpuCores / (vm.getCpuPerSocket() * vm.getThreadsPerCpu()); getModel().getNumOfSockets().setSelectedItem(sockets); addCpuListeners(); } else { super.totalCpuCoresChanged(); } } /** * span a list of all possible sockets values */ private List<Integer> createSocketsRange() { List<Integer> res = new ArrayList<>(); int maxHostCpu = getHostCpu(); int cpusPerSockets = vm.getCpuPerSocket(); for (int i = 1; i <= maxHostCpu; i++) { // sockets stepping must not exceed the host maximum if (i * cpusPerSockets <= maxHostCpu) { res.add(i); } } return res; } public boolean isHotSetCpuSupported() { Cluster selectedCluster = getModel().getSelectedCluster(); Version compatibilityVersion = getModel().getCompatibilityVersion(); boolean hotplugCpuSupported = Boolean.parseBoolean( ((Map<String, String>) AsyncDataProvider.getInstance().getConfigValuePreConverted( ConfigurationValues.HotPlugCpuSupported, compatibilityVersion.getValue())) .get(selectedCluster.getArchitecture().name())); return getVm().getStatus() == VMStatus.Up && hotplugCpuSupported; } public int getHostCpu() { return hostCpu; } @Override public void enableSinglePCI(boolean enabled) { super.enableSinglePCI(enabled); if (getInstanceTypeManager() != null) { getInstanceTypeManager().maybeSetSingleQxlPci(vm.getStaticData()); } } @Override public ExistingVmInstanceTypeManager getInstanceTypeManager() { return (ExistingVmInstanceTypeManager) instanceTypeManager; } @Override protected VM getVmWithNuma() { VM dummyVm = super.getVmWithNuma(); dummyVm.setId(vm.getId()); List<VmNumaNode> vmNumaNodes = getModel().getVmNumaNodes(); if (vmNumaNodes != null && !vmNumaNodes.isEmpty() && vmNumaNodes.size() == dummyVm.getvNumaNodeList().size()) { dummyVm.setvNumaNodeList(vmNumaNodes); } return dummyVm; } @Override protected void updateNumaEnabled() { super.updateNumaEnabled(); updateNumaEnabledHelper(); if (Boolean.TRUE.equals(getModel().getNumaEnabled().getEntity()) && getModel().getVmNumaNodes() != null) { getModel().getNumaNodeCount().setEntity(getModel().getVmNumaNodes().size()); } } @Override public void updateHaAvailability() { super.updateHaAvailability(); if (getVm().isHostedEngine()) { getModel().getIsHighlyAvailable().setEntity(false); getModel().getIsHighlyAvailable().setIsChangeable(false); getModel().getIsHighlyAvailable().setChangeProhibitionReason(getModel().constants.noHaWhenHostedEngineUsed()); } } @Override public void updateMaxMemory() { if (vm.getStatus() != VMStatus.Up) { super.updateMaxMemory(); } } }