Skip to content

Commit

Permalink
Replace VNC support with GNOME remote desktop
Browse files Browse the repository at this point in the history
Replace the Tiger VNC provided VNC support with GNOME remote desktop
provided RDP support & remove Tiger VNC support.

Also log a warning if the old VNC options are used & these options will
now no longer have an effect.

Resolves: RHEL-38407
  • Loading branch information
M4rtinK committed Jun 7, 2024
1 parent 2194421 commit 0a042ff
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 397 deletions.
4 changes: 2 additions & 2 deletions anaconda.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def exitHandler(rebootData):
WatchProcesses.unwatch_all_processes()

if flags.use_rd:
vnc.shutdownServer()
gnome_remote_destop.shutdown_server()

if "nokill" in kernel_arguments:
util.vtActivate(1)
Expand Down Expand Up @@ -292,7 +292,7 @@ def warn_on_deprecated_options(opts, log):
opts.display_mode = constants.DisplayModes.TUI
opts.noninteractive = True

from pyanaconda import vnc
from pyanaconda import gnome_remote_destop
from pyanaconda import kickstart
# we are past the --version and --help shortcut so we can import display &
# startup_utils, which import Blivet, without slowing down anything critical
Expand Down
219 changes: 128 additions & 91 deletions pyanaconda/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
from pyanaconda.core.process_watchers import WatchProcesses
from pyanaconda import startup_utils
from pyanaconda.core import util, constants, hw
from pyanaconda import vnc
from pyanaconda.gnome_remote_destop import GRDServer
from pyanaconda.core.i18n import _
from pyanaconda.flags import flags
from pyanaconda.modules.common.constants.services import NETWORK
from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke
from pyanaconda.ui.tui.spokes.askrd import AskRDSpoke, RDPAuthSpoke
from pyanaconda.ui.tui import tui_quit_callback
# needed for checking if the pyanaconda.ui.gui modules are available
import pyanaconda.ui
Expand All @@ -60,7 +60,6 @@
"Enforce text mode when installing from remote media with the inst.text boot option."
# on RHEL also: "Use the customer portal download URL in ilo/drac devices for greater speed."


def start_user_systemd():
"""Start the user instance of systemd.
Expand Down Expand Up @@ -104,74 +103,104 @@ def start_spice_vd_agent():
log.info("Started spice-vdagent.")


# VNC
# RDP

def ask_vnc_question(anaconda, vnc_server, message):
def ask_rd_question(anaconda, grd_server, message):
""" Ask the user if TUI or GUI-over-VNC should be started.
:param anaconda: instance of the Anaconda class
:param vnc_server: instance of the VNC server object
:param grd_server: instance of the GRD server object
:param str message: a message to show to the user together
with the question
:return: if remote desktop should be used
:rtype: bool
"""
App.initialize()
loop = App.get_event_loop()
loop.set_quit_callback(tui_quit_callback)
spoke = AskVNCSpoke(anaconda.ksdata, message=message)
spoke = AskRDSpoke(anaconda.ksdata, message=message)
ScreenHandler.schedule_screen(spoke)
App.run()

if anaconda.ksdata.vnc.enabled:
if spoke.use_remote_desktop:
if not anaconda.gui_mode:
log.info("VNC requested via VNC question, switching Anaconda to GUI mode.")
log.info("RDP requested via RDP question, switching Anaconda to GUI mode.")
anaconda.display_mode = constants.DisplayModes.GUI
flags.usevnc = True
vnc_server.password = anaconda.ksdata.vnc.password
flags.use_rd = True
grd_server.rdp_username = spoke.rdp_username
grd_server.rdp_password = spoke.rdp_password

return spoke.use_remote_desktop

def ask_for_rd_credentials(anaconda, grd_server, username=None, password=None):
""" Ask the user to provide RDP credentials interactively.
:param anaconda: instance of the Anaconda class
:param grd_server: instance of the GRD server object
:param str username: user set username (if any)
:param str password: user set password (if any)
:rtype: bool
"""
App.initialize()
loop = App.get_event_loop()
loop.set_quit_callback(tui_quit_callback)
spoke = RDPAuthSpoke(anaconda.ksdata, username=username, password=password)
ScreenHandler.schedule_screen(spoke)
App.run()

log.info("RDP credentials set")
anaconda.display_mode = constants.DisplayModes.GUI
flags.use_rd = True
grd_server.rdp_username = spoke._username
grd_server.rdp_password = spoke._password

def check_vnc_can_be_started(anaconda):
"""Check if we can start VNC in the current environment.
def check_rd_can_be_started(anaconda):
"""Check if we can start an RDP session in the current environment.
:returns: if VNC can be started and list of possible reasons
why VNC can't be started
:returns: if RDP session can be started and list of possible reasons
why the session can't be started
:rtype: (boot, list)
"""

error_messages = []
vnc_startup_possible = True
rd_startup_possible = True

# disable VNC over text question when not enough memory is available
# disable remote desktop over text question when not enough memory is available
min_gui_ram = hw.minimal_memory_needed(with_gui=True)
if blivet.util.total_memory() < min_gui_ram:
error_messages.append("Not asking for VNC because current memory (%d) < MIN_GUI_RAM (%d)" %
error_messages.append("Not asking for remote desktop session because current memory "
"(%d) < MIN_GUI_RAM (%d)" %
(blivet.util.total_memory(), min_gui_ram))
vnc_startup_possible = False
rd_startup_possible = False

# disable VNC question if text mode is requested and this is a ks install
# disable remote desktop question if text mode is requested and this is a ks install
if anaconda.tui_mode and flags.automatedInstall:
error_messages.append("Not asking for VNC because of an automated install")
vnc_startup_possible = False
error_messages.append(
"Not asking for remote desktop session because of an automated install"
)
rd_startup_possible = False

# disable VNC question if we were explicitly asked for text in kickstart
# disable remote desktop question if we were explicitly asked for text in kickstart
if anaconda.ksdata.displaymode.displayMode == DISPLAY_MODE_TEXT:
error_messages.append("Not asking for VNC because text mode was explicitly asked for in kickstart")
vnc_startup_possible = False
error_messages.append("Not asking for remote desktop session because text mode "
"was explicitly asked for in kickstart")
rd_startup_possible = False

# disable VNC question if we don't have network
# disable remote desktop question if we don't have network
network_proxy = NETWORK.get_proxy()
if not network_proxy.IsConnecting() and not network_proxy.Connected:
error_messages.append("Not asking for VNC because we don't have a network")
vnc_startup_possible = False
rd_startup_possible = False

# disable VNC question if we don't have Xvnc
if not os.access('/usr/bin/Xvnc', os.X_OK):
error_messages.append("Not asking for VNC because we don't have Xvnc")
vnc_startup_possible = False
# disable remote desktop question if we don't have GNOME remote desktop
if not os.access('/usr/bin/grdctl', os.X_OK):
error_messages.append("Not asking for remote desktop because we don't have grdctl")
rd_startup_possible = False

return vnc_startup_possible, error_messages
return rd_startup_possible, error_messages


def do_startup_wl_actions(timeout):
def do_startup_wl_actions(timeout, headless=False, headless_resolution=None):
"""Start the window manager.
When window manager actually connects to the X server is unknowable, but
Expand All @@ -181,6 +210,9 @@ def do_startup_wl_actions(timeout):
fingers crossed.
Add XDG_DATA_DIRS to the environment to pull in our overridden schema
files.
:param bool headless: start a headless session (used for RDP access)
:param str headless_resolution: headless virtual monitor resolution in WxH format
"""
datadir = os.environ.get('ANACONDA_DATADIR', '/usr/share/anaconda')
if 'XDG_DATA_DIRS' in os.environ:
Expand All @@ -199,14 +231,33 @@ def wl_preexec():
# to set GUI subprocess SIGINT handler
signal.signal(signal.SIGINT, signal.SIG_IGN)

# lets compile arguments for the run-in-new-session script
argv = ["/usr/libexec/anaconda/run-in-new-session",
"--user", "root",
"--service", "anaconda",
"--vt", "6",
"--session-type", "wayland",
"--session-class", "user",
"gnome-kiosk", "--sm-disable", "--wayland", "--no-x11",
"--wayland-display", constants.WAYLAND_SOCKET_NAME]
"--session-class", "user"]

if headless:
# headless (remote connection) - stay on VT1 where connection info is
argv.extend(["--vt", "1"])
else:
# local display - switch to VT6 & show GUI there
argv.extend(["--vt", "6"])

# add the generic GNOME Kiosk invocation
argv.extend(["gnome-kiosk", "--sm-disable",
"--wayland", "--no-x11",
"--wayland-display", constants.WAYLAND_SOCKET_NAME])

# remote access needs gnome-kiosk to start in headless mode &
# configure a virtual monitor
if headless:
# check virtual monitor resolution has been set
if headless_resolution is None:
# use default value
headless_resolution = "1280x1024"
argv.extend(["--headless", "--virtual-monitor", headless_resolution])

childproc = util.startProgram(argv, env_add={'XDG_DATA_DIRS': xdg_data_dirs},
preexec_fn=wl_preexec)
Expand Down Expand Up @@ -274,77 +325,62 @@ def setup_display(anaconda, options):
log.warning("invalid inst.xtimeout option value: %s", options.xtimeout)
xtimeout = constants.X_TIMEOUT

vnc_server = vnc.VncServer() # The vnc Server object.
vnc_server.anaconda = anaconda
vnc_server.timeout = xtimeout
grd_server = GRDServer(anaconda) # The RDP server object
rdp_credentials_sufficient = False

if options.vnc:
flags.usevnc = True
if options.rdp_enabled:
flags.use_rd = True
if not anaconda.gui_mode:
log.info("VNC requested via boot/CLI option, switching Anaconda to GUI mode.")
log.info("RDP requested via boot/CLI option, switching Anaconda to GUI mode.")
anaconda.display_mode = constants.DisplayModes.GUI
vnc_server.password = options.vncpassword

# Only consider vncconnect when vnc is a param
if options.vncconnect:
cargs = options.vncconnect.split(":")
vnc_server.vncconnecthost = cargs[0]
if len(cargs) > 1 and len(cargs[1]) > 0:
if len(cargs[1]) > 0:
vnc_server.vncconnectport = cargs[1]
grd_server.rdp_username = options.rdp_username
grd_server.rdp_password = options.rdp_password
# note if we have both set
rdp_credentials_sufficient = options.rdp_username and options.rdp_password

if options.xdriver:
write_xdriver(options.xdriver, root="/")

if anaconda.ksdata.vnc.enabled:
flags.usevnc = True
if not anaconda.gui_mode:
log.info("VNC requested via kickstart, switching Anaconda to GUI mode.")
anaconda.display_mode = constants.DisplayModes.GUI

if vnc_server.password == "":
vnc_server.password = anaconda.ksdata.vnc.password

if vnc_server.vncconnecthost == "":
vnc_server.vncconnecthost = anaconda.ksdata.vnc.host

if vnc_server.vncconnectport == "":
vnc_server.vncconnectport = anaconda.ksdata.vnc.port

# check if GUI without WebUI
if anaconda.gui_mode and not anaconda.is_webui_supported:
mods = (tup[1] for tup in pkgutil.iter_modules(pyanaconda.ui.__path__, "pyanaconda.ui."))
if "pyanaconda.ui.gui" not in mods:
stdout_log.warning("Graphical user interface not available, falling back to text mode")
anaconda.display_mode = constants.DisplayModes.TUI
flags.usevnc = False
flags.vncquestion = False

# check if VNC can be started
vnc_can_be_started, vnc_error_messages = check_vnc_can_be_started(anaconda)
if not vnc_can_be_started:
# VNC can't be started - disable the VNC question and log
# all the errors that prevented VNC from being started
flags.vncquestion = False
for error_message in vnc_error_messages:
flags.use_rd = False
flags.rd_question = False

# check if remote desktop mode can be started
rd_can_be_started, rd_error_messages = check_rd_can_be_started(anaconda)

if rd_can_be_started:
# if remote desktop can be started & only inst.rdp
# or inst.rdp and insufficient credentials are provided
# via boot options, ask interactively.
if options.rdp_enabled and not rdp_credentials_sufficient:
ask_for_rd_credentials(anaconda, grd_server, options.rdp_username, options.rdp_password)
else:
# RDP can't be started - disable the RDP question and log
# all the errors that prevented RDP from being started
flags.rd_question = False
for error_message in rd_error_messages:
stdout_log.warning(error_message)

if anaconda.tui_mode and flags.vncquestion:
# we prefer vnc over text mode, so ask about that
if anaconda.tui_mode and flags.rd_question:
# we prefer remote desktop over text mode, so ask about that
message = _("Text mode provides a limited set of installation "
"options. It does not offer custom partitioning for "
"full control over the disk layout. Would you like "
"to use VNC mode instead?")
ask_vnc_question(anaconda, vnc_server, message)
if not anaconda.ksdata.vnc.enabled:
"to use remote graphical access via the RDP protocol instead?")
if not ask_rd_question(anaconda, grd_server, message):
# user has explicitly specified text mode
flags.vncquestion = False
flags.rd_question = False

anaconda.log_display_mode()
startup_utils.check_memory(anaconda, options)

# check_memory may have changed the display mode
want_gui = anaconda.gui_mode and not (flags.preexisting_wayland or flags.usevnc)
want_gui = anaconda.gui_mode and not (flags.preexisting_wayland or flags.use_rd)
if want_gui:
try:
do_startup_wl_actions(xtimeout)
Expand All @@ -370,24 +406,25 @@ def setup_display(anaconda, options):
time.sleep(2)

if not anaconda.gui_startup_failed:
if options.runres and anaconda.gui_mode and not flags.usevnc:
if options.runres and anaconda.gui_mode and not flags.use_rd:
def on_mutter_ready(observer):
set_resolution(options.runres)
observer.disconnect()

mutter_display = MutterDisplay()
mutter_display.on_service_ready(on_mutter_ready)

if anaconda.tui_mode and anaconda.gui_startup_failed and flags.vncquestion and not anaconda.ksdata.vnc.enabled:
if anaconda.tui_mode and anaconda.gui_startup_failed and flags.rd_question:

message = _("X was unable to start on your machine. Would you like to start VNC to connect to "
"this computer from another computer and perform a graphical installation or continue "
"with a text mode installation?")
ask_vnc_question(anaconda, vnc_server, message)
ask_rd_question(anaconda, grd_server, message)

# if they want us to use VNC do that now
if anaconda.gui_mode and flags.usevnc:
vnc_server.startServer()
do_startup_wl_actions(xtimeout)
# if they want us to use RDP do that now
if anaconda.gui_mode and flags.use_rd:
do_startup_wl_actions(xtimeout, headless=True, headless_resolution=options.runres)
grd_server.start_grd_rdp()

# with X running we can initialize the UI interface
anaconda.initialize_interface()
Expand Down
2 changes: 1 addition & 1 deletion pyanaconda/startup_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def prompt_for_ssh(options):
if options.ksfile:
return False

if options.vnc:
if options.rdp:
return False

# Do some work here to get the ip addr / hostname to pass
Expand Down
Loading

0 comments on commit 0a042ff

Please sign in to comment.