-
Notifications
You must be signed in to change notification settings - Fork 87
consoles: Redesign #2008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
consoles: Redesign #2008
Changes from all commits
aa0b5c6
05ea999
5f9703d
5bd1f7a
5ce7223
ef471d2
c6fe1c7
418cf49
0ad85bc
d693f19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ import { Popover } from "@patternfly/react-core/dist/esm/components/Popover"; | |
import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip"; | ||
import { PendingIcon } from "@patternfly/react-icons"; | ||
|
||
import type { VM, VMDisk, VMInterface } from '../../types'; | ||
import type { VM, VMXML, VMGraphics, VMDisk, VMInterface } from '../../types'; | ||
|
||
import { | ||
getIfaceSourceName, | ||
|
@@ -111,6 +111,51 @@ export function needsShutdownSpice(vm: VM) { | |
return vm.hasSpice !== vm.inactiveXML.hasSpice; | ||
} | ||
|
||
export function needsShutdownVnc(vm: VM) { | ||
function find_vnc(v: VMXML): VMGraphics | undefined { | ||
if (v.displays) { | ||
for (const d of v.displays) | ||
if (d.type == "vnc") | ||
return d; | ||
} | ||
} | ||
|
||
const active_vnc = find_vnc(vm); | ||
const inactive_vnc = find_vnc(vm.inactiveXML); | ||
|
||
if (inactive_vnc) { | ||
if (!active_vnc) | ||
return true; | ||
|
||
// The active_vnc.port value is the actual port allocated at | ||
// machine start, it is never -1. Thus, we can't just compare | ||
// inactive_vnc.port with active_vnc.port here when | ||
// inactive_vnc.port is -1. Also, when inactive_vnc.port _is_ | ||
// -1, we can't tell whether active_vnc.port has been | ||
// allocated based on some old fixed port in inactive_vnc.port | ||
// (in which case we might want to shutdown and restart), or | ||
// whether it was allocated dynamically (in which case we | ||
// don't want to). But luckily that doesn't really matter and | ||
// a shutdown would not have any useful effect anyway, so we | ||
// don't have to worry that we are missing a notification for | ||
// a pending shutdown. | ||
// | ||
if (inactive_vnc.port != "-1" && active_vnc.port != inactive_vnc.port) | ||
return true; | ||
|
||
if (active_vnc.password != inactive_vnc.password) | ||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This added line is not executed by any test. |
||
} | ||
|
||
return false; | ||
} | ||
|
||
export function needsShutdownSerialConsole(vm: VM) { | ||
const serials = vm.displays && vm.displays.filter(display => display.type == 'pty'); | ||
const inactive_serials = vm.inactiveXML.displays && vm.inactiveXML.displays.filter(display => display.type == 'pty'); | ||
return serials.length != inactive_serials.length; | ||
} | ||
|
||
export function getDevicesRequiringShutdown(vm: VM) { | ||
if (!vm.persistent) | ||
return []; | ||
|
@@ -152,6 +197,14 @@ export function getDevicesRequiringShutdown(vm: VM) { | |
if (needsShutdownSpice(vm)) | ||
devices.push(_("SPICE")); | ||
|
||
// VNC | ||
if (needsShutdownVnc(vm)) | ||
devices.push(_("VNC")); | ||
|
||
// Serial console | ||
if (needsShutdownSerialConsole(vm)) | ||
devices.push(_("Text console")); | ||
|
||
// TPM | ||
if (needsShutdownTpm(vm)) | ||
devices.push(_("TPM")); | ||
|
@@ -207,7 +260,7 @@ export const VmNeedsShutdown = ({ vm } : { vm: VM }) => { | |
position="bottom" | ||
hasAutoWidth | ||
bodyContent={body}> | ||
<Label className="resource-state-text" color="teal" id={`vm-${vm.name}-needs-shutdown`} | ||
<Label className="resource-state-text" status="custom" id={`vm-${vm.name}-needs-shutdown`} | ||
icon={<PendingIcon />} onClick={() => null}> | ||
{_("Changes pending")} | ||
</Label> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
|
||
MIT License | ||
|
||
Copyright (c) 2025 Red Hat, Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
|
||
*/ | ||
|
||
import React from 'react'; | ||
|
||
import { initLogging } from '@novnc/novnc/lib/util/logging'; | ||
import RFB_module from '@novnc/novnc/lib/rfb'; | ||
const RFB = RFB_module.default; | ||
|
||
export const VncConsole = ({ | ||
children, | ||
host, | ||
port = '80', | ||
path = '', | ||
encrypt = false, | ||
resizeSession = true, | ||
clipViewport = false, | ||
dragViewport = false, | ||
scaleViewport = false, | ||
viewOnly = false, | ||
shared = false, | ||
credentials, | ||
repeaterID = '', | ||
vncLogging = 'warn', | ||
consoleContainerId, | ||
onDisconnected = () => {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This added line is not executed by any test. |
||
onInitFailed, | ||
onSecurityFailure, | ||
}) => { | ||
const rfb = React.useRef(); | ||
|
||
const novncElem = React.useRef(null); | ||
|
||
const onConnected = () => { | ||
}; | ||
|
||
const _onDisconnected = React.useCallback( | ||
(e) => { | ||
onDisconnected(e); | ||
}, | ||
[onDisconnected] | ||
); | ||
|
||
const _onSecurityFailure = React.useCallback( | ||
(e) => { | ||
onSecurityFailure(e); | ||
Comment on lines
+68
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These 2 added lines are not executed by any test. |
||
}, | ||
[onSecurityFailure] | ||
); | ||
|
||
const addEventListeners = React.useCallback(() => { | ||
if (rfb.current) { | ||
rfb.current?.addEventListener('connect', onConnected); | ||
rfb.current?.addEventListener('disconnect', _onDisconnected); | ||
rfb.current?.addEventListener('securityfailure', _onSecurityFailure); | ||
} | ||
}, [rfb, _onDisconnected, _onSecurityFailure]); | ||
|
||
const removeEventListeners = React.useCallback(() => { | ||
if (rfb.current) { | ||
rfb.current.removeEventListener('connect', onConnected); | ||
rfb.current.removeEventListener('disconnect', _onDisconnected); | ||
rfb.current.removeEventListener('securityfailure', _onSecurityFailure); | ||
} | ||
}, [rfb, _onDisconnected, _onSecurityFailure]); | ||
|
||
const connect = React.useCallback(() => { | ||
const protocol = encrypt ? 'wss' : 'ws'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This added line is not executed by any test. |
||
const url = `${protocol}://${host}:${port}/${path}`; | ||
|
||
const options = { | ||
repeaterID, | ||
shared, | ||
credentials | ||
}; | ||
rfb.current = new RFB(novncElem.current, url, options); | ||
addEventListeners(); | ||
rfb.current.viewOnly = viewOnly; | ||
rfb.current.clipViewport = clipViewport; | ||
rfb.current.dragViewport = dragViewport; | ||
rfb.current.scaleViewport = scaleViewport; | ||
rfb.current.resizeSession = resizeSession; | ||
}, [ | ||
addEventListeners, | ||
host, | ||
path, | ||
port, | ||
resizeSession, | ||
clipViewport, | ||
dragViewport, | ||
scaleViewport, | ||
viewOnly, | ||
encrypt, | ||
rfb, | ||
repeaterID, | ||
shared, | ||
credentials | ||
]); | ||
|
||
React.useEffect(() => { | ||
initLogging(vncLogging); | ||
try { | ||
connect(); | ||
} catch (e) { | ||
onInitFailed && onInitFailed(e); | ||
rfb.current = undefined; | ||
Comment on lines
+127
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These 3 added lines are not executed by any test. |
||
} | ||
|
||
return () => { | ||
disconnect(); | ||
removeEventListeners(); | ||
rfb.current = undefined; | ||
}; | ||
}, [connect, onInitFailed, removeEventListeners, vncLogging]); | ||
|
||
const disconnect = () => { | ||
if (!rfb.current) { | ||
return; | ||
Comment on lines
+140
to
+141
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These 2 added lines are not executed by any test. |
||
} | ||
rfb.current.disconnect(); | ||
}; | ||
|
||
return ( | ||
<div className="vm-console-vnc"> | ||
<div id={consoleContainerId} ref={novncElem} /> | ||
</div> | ||
); | ||
}; | ||
|
||
VncConsole.displayName = 'VncConsole'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.