package org.ovirt.engine.ui.uicommonweb.models.vms.instancetypes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.ovirt.engine.core.common.businessentities.DisplayType;
import org.ovirt.engine.core.common.businessentities.GraphicsDevice;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
import org.ovirt.engine.core.common.businessentities.InstanceType;
import org.ovirt.engine.core.common.businessentities.MigrationSupport;
import org.ovirt.engine.core.common.businessentities.VmBase;
import org.ovirt.engine.core.common.businessentities.VmDevice;
import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType;
import org.ovirt.engine.core.common.businessentities.VmEntityType;
import org.ovirt.engine.core.common.businessentities.VmRngDevice;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.VmWatchdog;
import org.ovirt.engine.core.common.businessentities.VmWatchdogType;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.VmDeviceCommonUtils;
import org.ovirt.engine.core.common.utils.VmDeviceType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.ui.frontend.AsyncQuery;
import org.ovirt.engine.ui.frontend.Frontend;
import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider;
import org.ovirt.engine.ui.uicommonweb.models.EntityModel;
import org.ovirt.engine.ui.uicommonweb.models.ListModel;
import org.ovirt.engine.ui.uicommonweb.models.vms.CustomInstanceType;
import org.ovirt.engine.ui.uicommonweb.models.vms.PriorityUtil;
import org.ovirt.engine.ui.uicommonweb.models.vms.UnitVmModel;
import org.ovirt.engine.ui.uicommonweb.models.vms.VirtioScsiUtil;
import org.ovirt.engine.ui.uicompat.Event;
import org.ovirt.engine.ui.uicompat.EventArgs;
import org.ovirt.engine.ui.uicompat.IEventListener;
/**
* Base class which takes care about copying the proper fields from instance type/template/vm static to the VM.
*
* Please note that this class manages only the fields which are defined on the instance type, so the
* fields which are taken from the template and are not from instance type have to be handled on some
* different place (most often in the behavior classes).
*
* While this class takes care of the fields which are defined on instance type it does not mean they
* has to be taken from instance type. For example if the custom instance type is selected, the fields
* are taken from the selected template. If the edit VM is being displayed, the fields are taken from the VM static.
*
*/
public abstract class InstanceTypeManager {
// turns the maybeSet* to always let the entity be set - useful for e.g. pooled VMs were everything is disabled but has to be set according to the underlying VM
private boolean alwaysEnabledFieldUpdate = false;
private final UnitVmModel model;
private PriorityUtil priorityUtil;
private VirtioScsiUtil virtioScsiUtil;
private InstanceTypeAttachDetachManager instanceTypeAttachDetachManager;
/**
* Since the manager can be activated/deactivated more times (because of queries running in parallel) only
* after all the queries which has deactivated the manager again activates it can be the manager considered to be
* active again.
*/
private int deactivatedNumber = 0;
private List<ActivatedListener> activatedListeners;
public InstanceTypeManager(UnitVmModel model) {
this.model = model;
priorityUtil = new PriorityUtil(model);
virtioScsiUtil = new VirtioScsiUtil(model);
instanceTypeAttachDetachManager = new InstanceTypeAttachDetachManager(this, model);
activatedListeners = new ArrayList<>();
registerListeners(model);
}
/**
* First updates the list of instance types and selects the one which is supposed to be selected and then
* updates all the fields which are taken from the instance type (by calling the updateFields()).
*/
public void updateAll() {
final Guid selectedInstanceTypeId = getSelectedInstanceTypeId();
Frontend.getInstance().runQuery(VdcQueryType.GetAllInstanceTypes, new VdcQueryParametersBase(),
new AsyncQuery<VdcQueryReturnValue>(returnValue -> {
if (returnValue == null || !returnValue.getSucceeded()) {
return;
}
List<InstanceType> instanceTypes = new ArrayList<>();
// add this only if the user is allowed to
if (!getModel().isCreateInstanceOnly()) {
instanceTypes.add(CustomInstanceType.INSTANCE);
}
for (InstanceType instanceType : (Iterable<InstanceType>) returnValue.getReturnValue()) {
instanceTypes.add(instanceType);
}
getModel().getInstanceTypes().setItems(instanceTypes);
for (InstanceType instanceType : instanceTypes) {
if ((instanceType instanceof CustomInstanceType) && selectedInstanceTypeId == null) {
getModel().getInstanceTypes().setSelectedItem(CustomInstanceType.INSTANCE);
break;
}
if (instanceType.getId() == null || selectedInstanceTypeId == null) {
continue;
}
if (instanceType.getId().equals(selectedInstanceTypeId)) {
getModel().getInstanceTypes().setSelectedItem(instanceType);
break;
}
}
if (getModel().getInstanceTypes().getSelectedItem() instanceof CustomInstanceType) {
// detach if the instance type is "custom"
getModel().getAttachedToInstanceType().setEntity(false);
}
updateFields();
}));
}
/**
* All the fields which are copied from the instance type.
*
* This method ignores all the fields which are not changeable, because the fields which are not changeable
* are for some reason not enabled in the current context (e.g. the device is not supported on the specific cluster version).
*
* This method has to be re-called every time this situation changes (e.g. cluster level changes or os type changes) so the specific fields can
* be properly populated.
*/
public void updateFields() {
getModel().startProgress();
doUpdateManagedFieldsFrom(getSource());
}
public void updateFildsAfterOsChanged() {
deactivateAndStartProgress();
VmBase vmBase = getSource();
maybeSetSingleQxlPci(vmBase);
updateWatchdog(vmBase, false);
updateBalloon(vmBase, false);
maybeSetSelectedItem(model.getUsbPolicy(), vmBase.getUsbPolicy());
activate();
}
/**
* Starts progress, deactivates the manager and waits until it gets activated again.
* On activation it stops the progress
*/
public void deactivateAndStartProgress() {
model.startProgress();
deactivate(() -> model.stopProgress());
}
private void registerListeners(UnitVmModel model) {
ManagedFieldsManager managedFieldsManager = new ManagedFieldsManager();
model.getInstanceTypes().getSelectedItemChangedEvent().addListener(managedFieldsManager);
model.getTemplateWithVersion().getSelectedItemChangedEvent().addListener(managedFieldsManager);
}
public void activate() {
if (deactivatedNumber != 0) {
deactivatedNumber--;
}
if (isActive() && activatedListeners.size() != 0) {
// copy done to avoid infinite recursion
List<ActivatedListener> copy = new ArrayList<>(activatedListeners);
activatedListeners.clear();
for (ActivatedListener listener : copy) {
listener.activated();
}
}
}
public void deactivate(ActivatedListener activatedListener) {
activatedListeners.add(activatedListener);
deactivate();
}
public void deactivate() {
deactivatedNumber++;
}
public boolean isActive() {
return deactivatedNumber == 0;
}
protected UnitVmModel getModel() {
return model;
}
class ManagedFieldsManager implements IEventListener<EventArgs> {
@Override
public void eventRaised(Event<? extends EventArgs> ev, Object sender, EventArgs args) {
if (!isActive()) {
return;
}
boolean customInstanceTypeSelected = model.getInstanceTypes().getSelectedItem() instanceof CustomInstanceType;
if (sender == model.getTemplateWithVersion() && customInstanceTypeSelected) {
// returns the VM/Pool's static data or the template (depending on the specific new/existing manager)
updateInstanceTypeFieldsFrom(getSource());
} else if (sender == model.getInstanceTypes() && !customInstanceTypeSelected) {
// the instance type is in fact a template
updateInstanceTypeFieldsFrom((VmTemplate) model.getInstanceTypes().getSelectedItem());
} else if (sender == model.getInstanceTypes() && customInstanceTypeSelected) {
instanceTypeAttachDetachManager.manageInstanceType(CustomInstanceType.INSTANCE);
}
}
private void updateInstanceTypeFieldsFrom(VmBase template) {
if (template == null) {
return;
}
model.startProgress();
doUpdateManagedFieldsFrom(template);
}
}
protected void doUpdateManagedFieldsFrom(final VmBase vmBase) {
if (vmBase == null) {
model.stopProgress();
return;
}
deactivate();
maybeSetEntity(model.getMemSize(), vmBase.getMemSizeMb());
maybeSetEntity(model.getMaxMemorySize(), vmBase.getMaxMemorySizeMb());
maybeSetEntity(model.getIoThreadsEnabled(), vmBase.getNumOfIoThreads() != 0);
maybeSetEntity(model.getNumOfIoThreads(), vmBase.getNumOfIoThreads());
maybeSetEntity(model.getTotalCPUCores(), Integer.toString(vmBase.getNumOfCpus()));
model.setBootSequence(vmBase.getDefaultBootSequence());
List<MigrationSupport> supportedModes = (List<MigrationSupport>) getModel().getMigrationMode().getItems();
if (supportedModes.contains(vmBase.getMigrationSupport())) {
maybeSetSelectedItem(getModel().getMigrationMode(), vmBase.getMigrationSupport());
}
maybeSetEntity(model.getIsHighlyAvailable(), vmBase.isAutoStartup());
maybeSetSelectedItem(model.getNumOfSockets(), vmBase.getNumOfSockets());
maybeSetSelectedItem(model.getCoresPerSocket(), vmBase.getCpuPerSocket());
maybeSetSelectedItem(model.getThreadsPerCore(), vmBase.getThreadsPerCpu());
maybeSetSelectedItem(model.getEmulatedMachine(), vmBase.getCustomEmulatedMachine());
maybeSetSelectedItem(model.getCustomCpu(), vmBase.getCustomCpuName());
model.setSelectedMigrationDowntime(vmBase.getMigrationDowntime());
model.selectMigrationPolicy(vmBase.getMigrationPolicyId());
priorityUtil.initPriority(vmBase.getPriority(), new PriorityUtil.PriorityUpdatingCallbacks() {
@Override
public void beforeUpdates() {
deactivate();
}
@Override
public void afterUpdates() {
activate();
}
});
updateDefaultDisplayRelatedFields(vmBase);
if (vmBase.getMinAllocatedMem() != 0) {
model.getMinAllocatedMemory().setEntity(vmBase.getMinAllocatedMem());
}
activate();
AsyncDataProvider.getInstance().isSoundcardEnabled(new AsyncQuery<>(returnValue -> {
deactivate();
getModel().getIsSoundcardEnabled().setEntity(returnValue);
activate();
Frontend.getInstance().runQuery(VdcQueryType.GetConsoleDevices, new IdQueryParameters(vmBase.getId()),
new AsyncQuery<VdcQueryReturnValue>(r -> {
deactivate();
List<String> consoleDevices = r.getReturnValue();
getModel().getIsConsoleDeviceEnabled().setEntity(!consoleDevices.isEmpty());
activate();
postDoUpdateManagedFieldsFrom(vmBase);
}));
}), vmBase.getId());
}
private void postDoUpdateManagedFieldsFrom(VmBase vmBase) {
if (isNextRunConfigurationExists()) {
deactivate();
getModel().getIsSoundcardEnabled().setEntity(
VmDeviceCommonUtils.isVmDeviceExists(vmBase.getManagedDeviceMap(), VmDeviceGeneralType.SOUND));
getModel().getIsConsoleDeviceEnabled().setEntity(
VmDeviceCommonUtils.isVmDeviceExists(vmBase.getManagedDeviceMap(), VmDeviceType.CONSOLE));
Set<GraphicsType> graphicsTypeSet = new HashSet<>();
for (GraphicsType graphicsType : GraphicsType.values()) {
if (VmDeviceCommonUtils.isVmDeviceExists(vmBase.getManagedDeviceMap(),
graphicsType.getCorrespondingDeviceType())) {
graphicsTypeSet.add(graphicsType);
}
}
getModel().getGraphicsType().setSelectedItem(UnitVmModel.GraphicsTypes.fromGraphicsTypes(graphicsTypeSet));
activate();
}
updateWatchdog(vmBase, true);
}
private void updateWatchdog(final VmBase vmBase, final boolean continueWithNext) {
AsyncDataProvider.getInstance().getWatchdogByVmId(new AsyncQuery<VdcQueryReturnValue>(returnValue -> {
deactivate();
@SuppressWarnings("unchecked")
Collection<VmWatchdog> watchdogs = returnValue.getReturnValue();
if (watchdogs.size() == 0) {
model.getWatchdogAction().setSelectedItem(model.getWatchdogAction().getItems().iterator().next());
model.getWatchdogModel().setSelectedItem(model.getWatchdogModel().getItems().iterator().next());
}
for (VmWatchdog watchdog : watchdogs) {
if (watchdogAvailable(watchdog.getModel())) {
model.getWatchdogAction().setSelectedItem(watchdog.getAction() == null ? null
: watchdog.getAction());
model.getWatchdogModel().setSelectedItem(watchdog.getModel() == null ? null //$NON-NLS-1$
: watchdog.getModel());
}
}
activate();
if (continueWithNext) {
updateBalloon(vmBase, true);
}
}), vmBase.getId());
}
protected void updateBalloon(final VmBase vmBase, final boolean continueWithNext) {
if (model.getMemoryBalloonDeviceEnabled().getIsChangable() && model.getMemoryBalloonDeviceEnabled().getIsAvailable()) {
Frontend.getInstance().runQuery(VdcQueryType.IsBalloonEnabled, new IdQueryParameters(vmBase.getId()),
new AsyncQuery<VdcQueryReturnValue>(returnValue -> {
deactivate();
getModel().getMemoryBalloonDeviceEnabled().setEntity((Boolean) returnValue.getReturnValue());
activate();
if (continueWithNext) {
updateRngDevice(vmBase);
}
}
));
} else if (continueWithNext) {
updateRngDevice(vmBase);
}
}
protected void updateRngDevice(final VmBase vmBase) {
if (model.getIsRngEnabled().getIsChangable() && model.getIsRngEnabled().getIsAvailable()) {
if (!isNextRunConfigurationExists()) {
Frontend.getInstance().runQuery(VdcQueryType.GetRngDevice, new IdQueryParameters(vmBase.getId()),
new AsyncQuery<VdcQueryReturnValue>(returnValue -> {
deactivate();
List<VmDevice> rngDevices = returnValue.getReturnValue();
getModel().getIsRngEnabled().setEntity(!rngDevices.isEmpty());
if (!rngDevices.isEmpty()) {
VmRngDevice rngDevice = new VmRngDevice(rngDevices.get(0));
rngDevice.updateSourceByVersion(getModel().getCompatibilityVersion());
getModel().setRngDevice(rngDevice);
}
activate();
updateVirtioScsi(vmBase);
}
));
} else {
deactivate();
VmDevice rngDevice = VmDeviceCommonUtils.findVmDeviceByGeneralType(
vmBase.getManagedDeviceMap(), VmDeviceGeneralType.RNG);
getModel().getIsRngEnabled().setEntity(rngDevice != null);
if (rngDevice != null) {
getModel().setRngDevice(new VmRngDevice(rngDevice));
}
activate();
updateVirtioScsi(vmBase);
}
} else {
updateVirtioScsi(vmBase);
}
}
private void updateVirtioScsi(VmBase vmBase) {
if (isNextRunConfigurationExists()) {
getModel().getIsVirtioScsiEnabled().setEntity(
VmDeviceCommonUtils.isVmDeviceExists(vmBase.getManagedDeviceMap(), VmDeviceType.VIRTIOSCSI));
model.stopProgress();
return;
}
virtioScsiUtil.updateVirtioScsiEnabled(vmBase.getId(), getModel().getOSType().getSelectedItem(), new VirtioScsiUtil.VirtioScasiEnablingFinished() {
@Override
public void beforeUpdates() {
deactivate();
}
@Override
public void afterUpdates() {
activate();
model.stopProgress();
instanceTypeAttachDetachManager.manageInstanceType(model.getInstanceTypes().getSelectedItem());
}
});
}
private boolean watchdogAvailable(VmWatchdogType watchdogModel) {
for (VmWatchdogType availableWatchdogModel : model.getWatchdogModel().getItems()) {
if (watchdogModel == null && availableWatchdogModel == null) {
return true;
}
if (watchdogModel != null && availableWatchdogModel != null && watchdogModel.equals(availableWatchdogModel)) {
return true;
}
}
return false;
}
protected void updateDefaultDisplayRelatedFields(final VmBase vmBase) {
// Update display protocol selected item
final Collection<DisplayType> displayTypes = model.getDisplayType().getItems();
if (displayTypes == null || displayTypes.isEmpty()) {
return;
}
// graphics
Frontend.getInstance().runQuery(VdcQueryType.GetGraphicsDevices, new IdQueryParameters(vmBase.getId()),
new AsyncQuery<VdcQueryReturnValue>(returnValue -> {
deactivate();
List<GraphicsDevice> graphicsDevices = returnValue.getReturnValue();
model.getIsHeadlessModeEnabled().setEntity(vmBase.getDefaultDisplayType() == DisplayType.none);
// select display protocol
DisplayType displayProtocol = displayTypes.iterator().next(); // first by default
if (displayTypes.contains(vmBase.getDefaultDisplayType())) {
displayProtocol = vmBase.getDefaultDisplayType(); // if display types contain DT of a vm, pick this one
}
maybeSetSelectedItem(model.getDisplayType(), displayProtocol);
Set<GraphicsType> graphicsTypes = new HashSet<>();
for (GraphicsDevice graphicsDevice : graphicsDevices) {
graphicsTypes.add(graphicsDevice.getGraphicsType());
}
UnitVmModel.GraphicsTypes selected = UnitVmModel.GraphicsTypes.fromGraphicsTypes(graphicsTypes);
if (selected != null && getModel().getGraphicsType().getItems().contains(selected)) {
maybeSetSelectedItem(getModel().getGraphicsType(), selected);
}
maybeSetSelectedItem(model.getNumOfMonitors(), vmBase.getNumOfMonitors());
maybeSetSelectedItem(model.getUsbPolicy(), vmBase.getUsbPolicy());
maybeSetEntity(model.getIsSmartcardEnabled(), vmBase.isSmartcardEnabled());
maybeSetSingleQxlPci(vmBase);
activate();
}));
}
protected void maybeSetSingleQxlPci(VmBase vmBase) {
maybeSetEntity(model.getIsSingleQxlEnabled(), vmBase.getSingleQxlPci());
}
protected <T> void maybeSetSelectedItem(ListModel<T> entityModel, T value) {
if (alwaysEnabledFieldUpdate || (entityModel != null && entityModel.getIsChangable() && entityModel.getIsAvailable())) {
entityModel.setSelectedItem(value);
}
}
protected <T> void maybeSetEntity(EntityModel<T> listModel, T value) {
if (alwaysEnabledFieldUpdate || (listModel != null && listModel.getIsChangable() && listModel.getIsAvailable())) {
listModel.setEntity(value);
}
}
protected Guid getSelectedInstanceTypeId() {
return model.getInstanceTypes().getSelectedItem() != null ? model.getInstanceTypes().getSelectedItem().getId() : null;
}
public static interface ActivatedListener {
void activated();
}
public void setAlwaysEnabledFieldUpdate(boolean alwaysEnabledFieldUpdate) {
this.alwaysEnabledFieldUpdate = alwaysEnabledFieldUpdate;
}
/**
* The source from which the data has to be copyed to managed fields.
*
* It can be an instance type, a template or a VM's static data
*/
protected abstract VmBase getSource();
protected boolean isNextRunConfigurationExists() {
return false;
}
protected boolean isSourceCustomInstanceType() {
if (getSource() instanceof VmTemplate) {
VmTemplate source = (VmTemplate) getSource();
if (source.getTemplateType() == VmEntityType.INSTANCE_TYPE) {
// only the custom instance type has null id.
// not using instanceof check because findbugs can not handle it properly here
return source.getId() == null;
}
return false;
}
return false;
}
}