package org.ovirt.engine.ui.uicommonweb.models.vms;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.action.VmOperationParameterBase;
import org.ovirt.engine.core.common.businessentities.GraphicsInfo;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
import org.ovirt.engine.core.common.businessentities.SsoMethod;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.console.ConsoleOptions;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.queries.ConfigurationValues;
import org.ovirt.engine.core.common.queries.ConfigureConsoleOptionsParams;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.ui.frontend.Frontend;
import org.ovirt.engine.ui.frontend.utils.FrontendUrlUtils;
import org.ovirt.engine.ui.uicommonweb.BaseCommandTarget;
import org.ovirt.engine.ui.uicommonweb.ConsoleUtils;
import org.ovirt.engine.ui.uicommonweb.DynamicMessages;
import org.ovirt.engine.ui.uicommonweb.ILogger;
import org.ovirt.engine.ui.uicommonweb.ShowErrorAsyncQuery;
import org.ovirt.engine.ui.uicommonweb.TypeResolver;
import org.ovirt.engine.ui.uicommonweb.UICommand;
import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider;
import org.ovirt.engine.ui.uicommonweb.help.HelpTag;
import org.ovirt.engine.ui.uicommonweb.models.Model;
import org.ovirt.engine.ui.uicompat.ConstantsManager;
import org.ovirt.engine.ui.uicompat.EventDefinition;
public class SpiceConsoleModel extends ConsoleModel {
public enum ClientConsoleMode { Native, Auto, Html5 }
public static EventDefinition spiceDisconnectedEventDefinition;
public static EventDefinition spiceConnectedEventDefinition;
private static final DynamicMessages dynamicMessages = (DynamicMessages) TypeResolver.getInstance().resolve(DynamicMessages.class);
private ConsoleClient spiceImpl;
private ClientConsoleMode consoleMode;
private void setSpiceImpl(ConsoleClient value) {
spiceImpl = value;
}
public ClientConsoleMode getClientConsoleMode() {
return consoleMode;
}
static {
spiceDisconnectedEventDefinition = new EventDefinition("SpiceDisconnected", SpiceConsoleModel.class); //$NON-NLS-1$
spiceConnectedEventDefinition = new EventDefinition("SpiceConnected", SpiceConsoleModel.class); //$NON-NLS-1$
}
public SpiceConsoleModel(VM myVm, Model parentModel) {
super(myVm, parentModel);
setTitle(ConstantsManager.getInstance().getConstants().spiceTitle());
setConsoleClientMode(getDefaultConsoleMode());
}
protected ClientConsoleMode getDefaultConsoleMode() {
return ClientConsoleMode.valueOf((String) AsyncDataProvider.getInstance().getConfigValuePreConverted(ConfigurationValues.ClientModeSpiceDefault));
}
public ConsoleClient getSpiceImpl() {
return spiceImpl;
}
public boolean isWanOptionsAvailableForMyVm() {
boolean isWindowsVm = AsyncDataProvider.getInstance().isWindowsOsType(getEntity().getOs());
boolean spiceGuestAgentInstalled = getEntity().getSpiceDriverVersion() != null;
return isWindowsVm && spiceGuestAgentInstalled;
}
/**
* Sets implementation of ConsoleClient which will be used
* and performs sets initial configuration as well (different for WA/UP).
*
* Default mode is "Auto"
*/
public void setConsoleClientMode(ClientConsoleMode consoleMode) {
ConsoleUtils consoleUtils = (ConsoleUtils) TypeResolver.getInstance().resolve(ConsoleUtils.class);
this.consoleMode = consoleMode;
switch (consoleMode) {
case Native:
setSpiceImpl((ConsoleClient) TypeResolver.getInstance().resolve(ISpiceNative.class));
break;
case Html5:
if (consoleUtils.webBasedClientsSupported()) {
setSpiceImpl((ConsoleClient) TypeResolver.getInstance().resolve(ISpiceHtml5.class));
} else {
getLogger().debug("Cannot select SPICE-HTML5."); //$NON-NLS-1$
setDefaultConsoleMode();
}
break;
default:
setDefaultConsoleMode();
break;
}
getConfigurator().configure(getSpiceImpl());
if (getEntity() != null) {
boolean isSpiceProxyDefined = consoleUtils.isSpiceProxyDefined(getEntity());
getSpiceImpl().getOptions().setSpiceProxyEnabled(isSpiceProxyDefined);
}
}
private void setDefaultConsoleMode() {
setSpiceImpl((ConsoleClient) TypeResolver.getInstance().resolve(ISpiceNative.class));
}
@Override
protected void connect() {
if (getEntity() != null) {
getLogger().debug("Connecting to Spice console..."); //$NON-NLS-1$
// Don't connect if there VM is not running on any host.
if (getEntity().getRunOnVds() == null) {
return;
}
// If it is not windows or SPICE guest agent is not installed, make sure the WAN options are disabled.
if (!AsyncDataProvider.getInstance().isWindowsOsType(getEntity().getVmOsId()) || !getEntity().getHasSpiceDriver()) {
getSpiceImpl().getOptions().setWanOptionsEnabled(false);
}
UICommand invokeConsoleCommand = new UICommand("invokeConsoleCommand", new BaseCommandTarget() { //$NON-NLS-1$
@Override
public void executeCommand(UICommand uiCommand) {
invokeConsole();
}
});
executeCommandWithConsoleSafenessWarning(invokeConsoleCommand);
}
}
@Override
public boolean canBeSelected() {
boolean hasVmSpiceSupport = Boolean.TRUE.equals(
AsyncDataProvider.getInstance().hasSpiceSupport(
getEntity().getOs(),
getEntity().getCompatibilityVersion()));
return getEntity().getGraphicsInfos().containsKey(GraphicsType.SPICE) && hasVmSpiceSupport;
}
private void cancel() {
setWindow(null);
}
@Override
public void executeCommand(UICommand command) {
super.executeCommand(command);
if ("Cancel".equals(command.getName())) { //$NON-NLS-1$
cancel();
}
}
public void invokeClient() {
final GraphicsInfo spiceInfo = getEntity().getGraphicsInfos().get(GraphicsType.SPICE);
if (spiceInfo == null) {
throw new IllegalStateException("Trying to invoke SPICE console but VM GraphicsInfo is null.");//$NON-NLS-1$
}
final ConsoleOptions options = getSpiceImpl().getOptions();
options.setVmId(getEntity().getId());
// configure options
ConfigureConsoleOptionsParams parameters = new ConfigureConsoleOptionsParams(options, true);
parameters.setEngineBaseUrl(FrontendUrlUtils.getRootURL());
parameters.setConsoleClientResourcesUrl(dynamicMessages.consoleClientResourcesUrl());
Frontend.getInstance().runQuery(
VdcQueryType.ConfigureConsoleOptions,
parameters,
new ShowErrorAsyncQuery(returnValue -> {
final ConsoleOptions configuredOptions = returnValue.getReturnValue();
// overriding global server settings by frontend settings
configuredOptions.setRemapCtrlAltDelete(options.isRemapCtrlAltDelete());
configuredOptions.setTitle(getClientTitle());
configuredOptions.setVmName(getEntity().getName());
configuredOptions.setFullScreen(options.isFullScreen());
configuredOptions.setSmartcardEnabledOverridden(options.isSmartcardEnabledOverridden());
if (!configuredOptions.isSpiceProxyEnabled()) {
configuredOptions.setSpiceProxy(null); // override spice proxy from backend
}
try {
getSpiceImpl().setOptions(configuredOptions);
getSpiceImpl().invokeClient();
} catch (RuntimeException ex) {
getLogger().error("Exception on Spice connect", ex); //$NON-NLS-1$
}
}));
}
public void invokeConsole() { // todo refactor this later
// Only if the VM has agent and we connect through user-portal
// we attempt to perform SSO (otherwise an error will be thrown)
if (!getConfigurator().getIsAdmin() && getEntity().getStatus() == VMStatus.Up
&& SsoMethod.GUEST_AGENT.equals(getEntity().getSsoMethod())) {
getLogger().info("SpiceConsoleManager::Connect: Attempting to perform SSO on Desktop " //$NON-NLS-1$
+ getEntity().getName());
Frontend.getInstance().runAction(VdcActionType.VmLogon, new VmOperationParameterBase(getEntity().getId()),
result -> {
final VdcReturnValueBase logonCommandReturnValue = result.getReturnValue();
boolean isLogonSucceeded = logonCommandReturnValue != null && logonCommandReturnValue.getSucceeded();
if (isLogonSucceeded) {
invokeClient();
}
else {
if (logonCommandReturnValue != null && logonCommandReturnValue.getFault().getError() == EngineError.nonresp) {
UICommand okCommand =
new UICommand("SpiceWithoutAgentOK", new BaseCommandTarget() { //$NON-NLS-1$
@Override
public void executeCommand(UICommand uiCommand) {
logSsoOnDesktopFailedAgentNonResp(getLogger(),
logonCommandReturnValue != null ?
logonCommandReturnValue.getDescription()
: ""); //$NON-NLS-1$
invokeClient();
getParentModel().setWindow(null);
}
});
UICommand cancelCommand = new UICommand("SpiceWithoutAgentCancel", new BaseCommandTarget() { //$NON-NLS-1$
@Override
public void executeCommand(UICommand uiCommand) {
getParentModel().setWindow(null);
}
});
createConnectWithoutAgentConfirmationPopup(okCommand, cancelCommand);
}
else {
logSsoOnDesktopFailed(getLogger(),
logonCommandReturnValue != null ? logonCommandReturnValue.getDescription()
: ""); //$NON-NLS-1$
}
}
},
this);
} else {
invokeClient();
}
}
private void createConnectWithoutAgentConfirmationPopup(UICommand okCommand, UICommand cancelCommand){
SpiceToGuestWithNonRespAgentModel spiceWithoutAgentModel = new SpiceToGuestWithNonRespAgentModel();
spiceWithoutAgentModel.setTitle(ConstantsManager.getInstance()
.getConstants()
.guestAgentNotResponsiveTitle());
spiceWithoutAgentModel.setHelpTag(HelpTag.sso_did_not_succeeded);
spiceWithoutAgentModel.setHashName("sso_did_not_succeeded"); //$NON-NLS-1$
spiceWithoutAgentModel.setMessage(ConstantsManager.getInstance()
.getMessages()
.connectingToGuestWithNotResponsiveAgentMsg());
okCommand.setTitle(ConstantsManager.getInstance().getConstants().ok());
okCommand.setIsDefault(true);
spiceWithoutAgentModel.getCommands().add(okCommand);
cancelCommand.setTitle(ConstantsManager.getInstance().getConstants().cancel());
cancelCommand.setIsCancel(true);
spiceWithoutAgentModel.getCommands().add(cancelCommand);
getParentModel().setWindow(spiceWithoutAgentModel);
}
private void logSsoOnDesktopFailedAgentNonResp(ILogger logger, String vmName) {
logger.info("SpiceConsoleManager::Connect: Failed to perform SSO on Destkop " //$NON-NLS-1$
+ vmName + " because agent is non-responsive, continuing without SSO."); //$NON-NLS-1$
}
private void logSsoOnDesktopFailed(ILogger logger, String vmName) {
logger.info("SpiceConsoleManager::Connect: Failed to perform SSO on Destkop " //$NON-NLS-1$
+ vmName + ", cancel open spice console request."); //$NON-NLS-1$
}
}