package org.ovirt.engine.ui.uicommonweb.models.vms;
import java.util.ArrayList;
import java.util.Collection;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.VmBase;
import org.ovirt.engine.core.common.businessentities.network.Network;
import org.ovirt.engine.core.common.businessentities.network.VmInterfaceType;
import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface;
import org.ovirt.engine.core.common.businessentities.network.VnicProfileView;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.ui.frontend.Frontend;
import org.ovirt.engine.ui.uicommonweb.UICommand;
import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider;
import org.ovirt.engine.ui.uicommonweb.models.EntityModel;
import org.ovirt.engine.ui.uicommonweb.models.IModel;
import org.ovirt.engine.ui.uicommonweb.models.ListModel;
import org.ovirt.engine.ui.uicommonweb.models.Model;
import org.ovirt.engine.ui.uicommonweb.validation.IValidation;
import org.ovirt.engine.ui.uicommonweb.validation.NoSpecialCharactersWithDotValidation;
import org.ovirt.engine.ui.uicommonweb.validation.NotEmptyValidation;
import org.ovirt.engine.ui.uicommonweb.validation.UnicastMacAddressValidation;
import org.ovirt.engine.ui.uicommonweb.validation.VnicProfileValidation;
import org.ovirt.engine.ui.uicompat.ConstantsManager;
import org.ovirt.engine.ui.uicompat.Event;
import org.ovirt.engine.ui.uicompat.EventArgs;
import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs;
public abstract class VmInterfaceModel extends Model {
protected static final String ON_SAVE_COMMAND = "OnSave"; //$NON-NLS-1$
private Collection<VmInterfaceType> supportedVnicTypes;
private EntityModel<String> privateName;
private ListModel<VnicProfileView> privateProfile;
private EntityModel<Boolean> linked;
private EntityModel<Boolean> linked_IsSelected;
private EntityModel<Boolean> unlinked_IsSelected;
private ListModel<VmInterfaceType> privateNicType;
private EntityModel<String> privateMAC;
private EntityModel<Boolean> enableMac;
private EntityModel<Boolean> plugged;
private EntityModel<Boolean> plugged_IsSelected;
private EntityModel<Boolean> unplugged_IsSelected;
protected final boolean hotPlugSupported;
private final VmBase vm;
private final ArrayList<VmNetworkInterface> vmNicList;
private final VMStatus vmStatus;
private UICommand okCommand;
private final IModel sourceModel;
private final Version clusterCompatibilityVersion;
private ProfileBehavior profileBehavior;
private Guid dcId;
protected VmInterfaceModel(VmBase vm,
VMStatus vmStatus,
Guid dcId,
Version clusterCompatibilityVersion,
ArrayList<VmNetworkInterface> vmNicList,
IModel sourceModel,
ProfileBehavior profileBehavior) {
this.dcId = dcId;
this.profileBehavior = profileBehavior;
this.vm = vm;
this.vmNicList = vmNicList;
this.vmStatus = vmStatus;
this.sourceModel = sourceModel;
this.clusterCompatibilityVersion = clusterCompatibilityVersion;
hotPlugSupported =
AsyncDataProvider.getInstance().getNicHotplugSupport(vm.getOsId(),
clusterCompatibilityVersion);
setName(new EntityModel<String>());
setNicType(new ListModel<VmInterfaceType>() {
@Override
public void setSelectedItem(VmInterfaceType value) {
super.setSelectedItem(value);
updateLinkChangability();
}
});
setProfile(new ListModel<VnicProfileView>() {
@Override
public void setSelectedItem(VnicProfileView value) {
super.setSelectedItem(value);
getNicType().setSelectedItem(getDefaultNicTypeByProfile());
updateLinkChangability();
}
});
setMAC(new EntityModel<String>());
setEnableMac(new EntityModel<Boolean>() {
@Override
public void setEntity(Boolean enableManualMac) {
super.setEntity(enableManualMac);
getMAC().setIsChangeable(enableManualMac);
}
});
getEnableMac().setEntity(false);
getMAC().getPropertyChangedEvent().addListener(this);
setLinked(new EntityModel<Boolean>());
getLinked().getPropertyChangedEvent().addListener(this);
setLinked_IsSelected(new EntityModel<Boolean>());
getLinked_IsSelected().getEntityChangedEvent().addListener(this);
setUnlinked_IsSelected(new EntityModel<Boolean>());
getUnlinked_IsSelected().getEntityChangedEvent().addListener(this);
setPlugged(new EntityModel<Boolean>());
getPlugged().getPropertyChangedEvent().addListener(this);
setPlugged_IsSelected(new EntityModel<Boolean>());
getPlugged_IsSelected().getEntityChangedEvent().addListener(this);
setUnplugged_IsSelected(new EntityModel<Boolean>());
getUnplugged_IsSelected().getEntityChangedEvent().addListener(this);
}
protected abstract void init();
public IModel getSourceModel() {
return sourceModel;
}
public VmBase getVm() {
return vm;
}
public ArrayList<VmNetworkInterface> getVmNicList() {
return vmNicList;
}
public VMStatus getVmStatus() {
return vmStatus;
}
/**
* The user may also plug and unplug interfaces when the VM is down (regardless of hotplug support)
* or create an unplugged NIC in a running VM when there isn't support for hotplug.
* @return an boolean.
*/
public boolean allowPlug() {
return hotPlugSupported || getVmStatus().equals(VMStatus.Down);
}
public Version getClusterCompatibilityVersion() {
return clusterCompatibilityVersion;
}
public EntityModel<String> getName() {
return privateName;
}
private void setName(EntityModel<String> value) {
privateName = value;
}
public ListModel<VnicProfileView> getProfile() {
return privateProfile;
}
private void setProfile(ListModel<VnicProfileView> value) {
privateProfile = value;
}
public EntityModel<Boolean> getLinked() {
return linked;
}
private void setLinked(EntityModel<Boolean> value) {
linked = value;
}
public EntityModel<Boolean> getLinked_IsSelected() {
return linked_IsSelected;
}
public void setLinked_IsSelected(EntityModel<Boolean> value) {
linked_IsSelected = value;
}
public EntityModel<Boolean> getUnlinked_IsSelected() {
return unlinked_IsSelected;
}
public void setUnlinked_IsSelected(EntityModel<Boolean> value) {
unlinked_IsSelected = value;
}
public ListModel<VmInterfaceType> getNicType() {
return privateNicType;
}
private void setNicType(ListModel<VmInterfaceType> value) {
privateNicType = value;
}
public EntityModel<String> getMAC() {
return privateMAC;
}
private void setMAC(EntityModel<String> value) {
privateMAC = value;
}
public EntityModel<Boolean> getEnableMac() {
return enableMac;
}
private void setEnableMac(EntityModel<Boolean> value) {
enableMac = value;
}
public EntityModel<Boolean> getPlugged() {
return plugged;
}
private void setPlugged(EntityModel<Boolean> value) {
plugged = value;
}
public EntityModel<Boolean> getPlugged_IsSelected() {
return plugged_IsSelected;
}
public void setPlugged_IsSelected(EntityModel<Boolean> value) {
plugged_IsSelected = value;
}
public EntityModel<Boolean> getUnplugged_IsSelected() {
return unplugged_IsSelected;
}
public void setUnplugged_IsSelected(EntityModel<Boolean> value) {
unplugged_IsSelected = value;
}
@Override
public void eventRaised(Event<? extends EventArgs> ev, Object sender, EventArgs args) {
super.eventRaised(ev, sender, args);
if (sender == getMAC()) {
mAC_PropertyChanged((PropertyChangedEventArgs) args);
}
else if (sender == getPlugged()) {
PropertyChangedEventArgs propArgs = (PropertyChangedEventArgs) args;
if (propArgs.propertyName.equals("Entity")) { //$NON-NLS-1$
boolean plugged = getPlugged().getEntity();
getPlugged_IsSelected().setEntity(plugged);
getUnplugged_IsSelected().setEntity(!plugged);
} else if (propArgs.propertyName.equals("IsChangable")) { //$NON-NLS-1$
boolean isPlugChangeable = getPlugged().getIsChangable();
getPlugged_IsSelected().setChangeProhibitionReason(getLinked().getChangeProhibitionReason());
getPlugged_IsSelected().setIsChangeable(isPlugChangeable);
getUnplugged_IsSelected().setChangeProhibitionReason(getLinked().getChangeProhibitionReason());
getUnplugged_IsSelected().setIsChangeable(isPlugChangeable);
} else if (propArgs.propertyName.equals("IsAvailable")) { //$NON-NLS-1$
boolean isPlugAvailable = getPlugged().getIsAvailable();
getPlugged_IsSelected().setIsAvailable(isPlugAvailable);
getUnplugged_IsSelected().setIsAvailable(isPlugAvailable);
}
}
else if (sender == getPlugged_IsSelected()) {
if (getPlugged_IsSelected().getEntity()) {
getPlugged().setEntity(true);
}
}
else if (sender == getUnplugged_IsSelected()) {
if (getUnplugged_IsSelected().getEntity()) {
getPlugged().setEntity(false);
}
}
else if (sender == getLinked()) {
PropertyChangedEventArgs propArgs = (PropertyChangedEventArgs) args;
if (propArgs.propertyName.equals("Entity")) { //$NON-NLS-1$
boolean linked = getLinked().getEntity();
getLinked_IsSelected().setEntity(linked);
getUnlinked_IsSelected().setEntity(!linked);
} else if (propArgs.propertyName.equals("IsChangable")) { //$NON-NLS-1$
boolean isLinkedChangeable = getLinked().getIsChangable();
getLinked_IsSelected().setChangeProhibitionReason(getLinked().getChangeProhibitionReason());
getLinked_IsSelected().setIsChangeable(isLinkedChangeable);
getUnlinked_IsSelected().setChangeProhibitionReason(getLinked().getChangeProhibitionReason());
getUnlinked_IsSelected().setIsChangeable(isLinkedChangeable);
} else if (propArgs.propertyName.equals("IsAvailable")) { //$NON-NLS-1$
boolean isLinkedAvailable = getLinked().getIsAvailable();
getLinked_IsSelected().setIsAvailable(isLinkedAvailable);
getUnlinked_IsSelected().setIsAvailable(isLinkedAvailable);
}
}
else if (sender == getLinked_IsSelected()) {
if (getLinked_IsSelected().getEntity()) {
getLinked().setEntity(true);
}
}
else if (sender == getUnlinked_IsSelected()) {
if (getUnlinked_IsSelected().getEntity()) {
getLinked().setEntity(false);
}
}
}
private void mAC_PropertyChanged(PropertyChangedEventArgs e) {
if (e.propertyName.equals("IsChangeAllowed") && !getMAC().getIsChangable()) { //$NON-NLS-1$
getMAC().setIsValid(true);
}
}
public boolean validate() {
getName().validateEntity(new IValidation[] { new NotEmptyValidation(), new NoSpecialCharactersWithDotValidation() });
getNicType().validateSelectedItem(new IValidation[] { new NotEmptyValidation() });
getProfile().validateSelectedItem(new IValidation[] { new VnicProfileValidation(getNicType().getSelectedItem()) });
getMAC().setIsValid(true);
if (getMAC().getIsChangable()) {
getMAC().validateEntity(new IValidation[] { new NotEmptyValidation(), new UnicastMacAddressValidation() });
}
return getName().getIsValid() && getNicType().getIsValid()
&& getMAC().getIsValid() && getProfile().getIsValid();
}
protected abstract VmNetworkInterface createBaseNic();
protected void onSave() {
VmNetworkInterface nic = createBaseNic();
if (getProgress() != null) {
return;
}
if (!validate()) {
return;
}
// Save changes.
nic.setName(getName().getEntity());
VnicProfileView profile = getProfile().getSelectedItem();
nic.setVnicProfileId(profile.getId());
nic.setNetworkName(profile.getNetworkName());
nic.setLinked(getLinked().getEntity());
if (getNicType().getSelectedItem() == null) {
nic.setType(null);
}
else {
nic.setType(getNicType().getSelectedItem().getValue());
}
onSaveMAC(nic);
nic.setPlugged(getPlugged().getEntity());
startProgress();
Frontend.getInstance().runAction(getVdcActionType(),
createVdcActionParameters(nic),
result -> {
VdcReturnValueBase returnValue = result.getReturnValue();
stopProgress();
if (returnValue != null && returnValue.getSucceeded()) {
cancel();
postOnSave();
}
},
this);
}
protected void postOnSave() {
// Do nothing
}
protected void cancel() {
sourceModel.setWindow(null);
}
protected abstract String getDefaultMacAddress();
protected abstract VdcActionType getVdcActionType();
protected void initProfiles() {
profileBehavior.initProfiles(getVm().getClusterId(), dcId, new AsyncQuery<>(result -> {
getProfile().setItems(result);
profileBehavior.initSelectedProfile(getProfile(), getNic());
updateProfileChangability();
// fetch completed
okCommand.setIsExecutionAllowed(true);
initSelectedType();
}));
}
protected void initCommands() {
okCommand = UICommand.createDefaultOkUiCommand(ON_SAVE_COMMAND, this);
// wait for data to fetch
okCommand.setIsExecutionAllowed(false);
getCommands().add(okCommand);
UICommand cancelCommand = UICommand.createCancelUiCommand(CANCEL_COMMAND, this); //$NON-NLS-1$
getCommands().add(cancelCommand);
}
protected abstract VmNetworkInterface getNic();
protected abstract void initSelectedType();
@Override
public void executeCommand(UICommand command) {
super.executeCommand(command);
if (ON_SAVE_COMMAND.equals(command.getName())) {
onSave();
}
else if (CANCEL_COMMAND.equals(command.getName())) {
cancel();
}
}
protected abstract void initMAC();
protected abstract void initLinked();
protected void onSaveMAC(VmNetworkInterface nicToSave) {
nicToSave.setMacAddress(getMAC().getIsChangable() ? getMAC().getEntity() == null ? null
: getMAC().getEntity().toLowerCase() : getDefaultMacAddress());
}
protected abstract VdcActionParametersBase createVdcActionParameters(VmNetworkInterface nicToSave);
protected void updateLinkChangability() {
boolean isNullProfileSelected = getProfile().getSelectedItem() == null;
if (isNullProfileSelected) {
getLinked().setIsChangeable(false);
return;
}
if (VmInterfaceType.pciPassthrough.equals(getNicType().getSelectedItem())
|| getProfile().getSelectedItem().isPassthrough()) {
getLinked().setEntity(true);
getLinked().setChangeProhibitionReason(ConstantsManager.getInstance()
.getConstants()
.linkStateUpdateNotSupportedForPassthroughVnic());
getLinked().setIsChangeable(false);
return;
}
getLinked().setIsChangeable(true);
}
protected void updateProfileChangability() {
getProfile().setIsChangeable(true);
}
protected boolean selectedNetworkExternal() {
VnicProfileView profile = getProfile().getSelectedItem();
Network network = null;
if (profile != null && profile.getId() != null) {
network = getProfileBehavior().findNetworkById(profile.getId());
}
return network != null && network.isExternal();
}
public ProfileBehavior getProfileBehavior() {
return profileBehavior;
}
protected VmInterfaceType getDefaultNicTypeByProfile() {
VnicProfileView profile = getProfile().getSelectedItem();
boolean passthroughSupported = getSupportedVnicTypes().contains(VmInterfaceType.pciPassthrough);
return profile != null && profile.isPassthrough() && passthroughSupported ? VmInterfaceType.pciPassthrough
: AsyncDataProvider.getInstance().getDefaultNicType(getSupportedVnicTypes());
}
protected Collection<VmInterfaceType> getSupportedVnicTypes() {
return supportedVnicTypes == null ? new ArrayList<VmInterfaceType>() : supportedVnicTypes;
}
protected void setSupportedVnicTypes(Collection<VmInterfaceType> supportedVnicTypes) {
this.supportedVnicTypes = supportedVnicTypes;
}
}