-
Notifications
You must be signed in to change notification settings - Fork 79
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
Add "view, edit, and add Video devices" to detailspage #1795
base: main
Are you sure you want to change the base?
Changes from all commits
be1a210
ef911a8
639c8a2
afc6b32
7577f3c
74b25dd
499adeb
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 |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
.pf-v5-c-console__vnc > div, | ||
.pf-v5-c-console__vnc > div > div { | ||
inline-size: 100%; | ||
block-size: 100%; | ||
block-size: 90%; | ||
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. What is the reason for this? To make space for the line with the VNC details and Edit button? I think we need to find a better solution in that case. |
||
} | ||
|
||
.pf-v5-c-console__actions { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,14 +19,20 @@ | |
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import cockpit from 'cockpit'; | ||
import { AccessConsoles } from "@patternfly/react-console"; | ||
import { vmId } from "../../../helpers.js"; | ||
import { Button, Text, TextContent, TextVariants } from '@patternfly/react-core'; | ||
import { DialogsContext } from 'dialogs.jsx'; | ||
import { AddVNC } from './vncAdd.jsx'; | ||
import { EditVNCModal } from './vncEdit.jsx'; | ||
|
||
import SerialConsole from './serialConsole.jsx'; | ||
import Vnc from './vnc.jsx'; | ||
import DesktopConsole from './desktopConsole.jsx'; | ||
import { | ||
domainAttachVnc, | ||
domainCanConsole, | ||
domainDesktopConsole, | ||
domainGet, | ||
domainSerialConsoleCommand | ||
} from '../../../libvirtApi/domain.js'; | ||
|
||
|
@@ -43,11 +49,18 @@ | |
}; | ||
|
||
class Consoles extends React.Component { | ||
static contextType = DialogsContext; | ||
|
||
Check warning Code scanning / CodeQL Unused or undefined state property Warning
Component state property 'addVncInProgress' is
written Error loading related location Loading Component state property 'addVncInProgress' is written Error loading related location Loading Component state property 'addVncInProgress' is written Error loading related location Loading |
||
constructor (props) { | ||
super(props); | ||
|
||
this.state = { | ||
serial: props.vm.displays && props.vm.displays.filter(display => display.type == 'pty'), | ||
selectedConsole: this.getDefaultConsole(props.vm), | ||
addVncInProgress: false, | ||
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. As COdeQL says: addVncProgress is never read, we can remove it. |
||
vncAddress: "", | ||
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. Also, these three can be removed when |
||
vncPort: "", | ||
vncPassword: "", | ||
}; | ||
|
||
this.getDefaultConsole = this.getDefaultConsole.bind(this); | ||
|
@@ -61,6 +74,10 @@ | |
if (newSerial.length !== oldSerial.length || oldSerial.some((pty, index) => pty.alias !== newSerial[index].alias)) | ||
return { serial: newSerial }; | ||
|
||
if (nextProps.selectedConsole !== prevState.selectedConsole) { | ||
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 looks problematic. The 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. But: |
||
return { selectedConsole: nextProps.selectedConsole }; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
@@ -91,51 +108,116 @@ | |
domainDesktopConsole({ name: vm.name, id: vm.id, connectionName: vm.connectionName, consoleDetail: vm.displays.find(display => display.type == type) }); | ||
} | ||
|
||
add() { | ||
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 function is not used, I think. Let's remove it. |
||
const Dialogs = this.context; | ||
const { vm } = this.props; | ||
|
||
this.setState({ addVncInProgress: true }); | ||
const vncParams = { | ||
connectionName: vm.connectionName, | ||
vmName: vm.name, | ||
vncAddress: this.state.vncAddress || "", | ||
vncPort: this.state.vncPort || "", | ||
vncPassword: this.state.vncPassword || "", | ||
}; | ||
|
||
domainAttachVnc(vncParams) | ||
.then(() => { | ||
domainGet({ connectionName: vm.connectionName, id: vm.id }); | ||
Dialogs.close(); | ||
}) | ||
.catch(exc => this.dialogErrorSet(_("Video device settings could not be saved"), exc.message)) | ||
.finally(() => this.setState({ addVncInProgress: false })); | ||
} | ||
|
||
render () { | ||
const Dialogs = this.context; | ||
const { vm, onAddErrorNotification, isExpanded } = this.props; | ||
const { serial } = this.state; | ||
const { serial, selectedConsole } = this.state; | ||
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.
|
||
const spice = vm.displays && vm.displays.find(display => display.type == 'spice'); | ||
const vnc = vm.displays && vm.displays.find(display => display.type == 'vnc'); | ||
const id = vmId(vm.name); | ||
|
||
const openVncAdd = () => { | ||
Dialogs.show(<AddVNC idPrefix={`${id}-add-vnc`} | ||
vm={vm} />); | ||
}; | ||
|
||
const openVncEdit = () => { | ||
Dialogs.show(<EditVNCModal idPrefix={`${id}-edit-vnc`} | ||
vmName={vm.name} | ||
vmId={id} | ||
connectionName={vm.connectionName} | ||
consoleDetail={vnc} />); | ||
}; | ||
|
||
if (!domainCanConsole || !domainCanConsole(vm.state)) { | ||
return (<VmNotRunning />); | ||
return ( | ||
<> | ||
<VmNotRunning /> | ||
{selectedConsole === 'VncConsole' && !vnc && ( | ||
<Button variant="secondary" size="sm" | ||
onClick={openVncAdd}> | ||
{_("Add VNC")} | ||
</Button> | ||
)} | ||
{selectedConsole === 'VncConsole' && vnc && ( | ||
<TextContent> | ||
<Text component={TextVariants.p}> | ||
{`VNC address=${vnc.address} port=${vnc.port === '-1' ? 'auto' : vnc.port} `} | ||
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. It is confusing that "-1" is translated to "auto" here, but the dialogs still expose the raw "-1" number. |
||
<Button variant="link" | ||
onClick={openVncEdit}> | ||
{_("Edit")} | ||
</Button> | ||
</Text> | ||
</TextContent> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
const onDesktopConsole = () => { // prefer spice over vnc | ||
this.onDesktopConsoleDownload(spice ? 'spice' : 'vnc'); | ||
}; | ||
|
||
return ( | ||
<AccessConsoles preselectedType={this.getDefaultConsole()} | ||
textSelectConsoleType={_("Select console type")} | ||
textSerialConsole={_("Serial console")} | ||
textVncConsole={_("VNC console")} | ||
textDesktopViewerConsole={_("Desktop viewer")}> | ||
{serial.map((pty, idx) => (<SerialConsole type={serial.length == 1 ? "SerialConsole" : cockpit.format(_("Serial console ($0)"), pty.alias || idx)} | ||
key={"pty-" + idx} | ||
connectionName={vm.connectionName} | ||
vmName={vm.name} | ||
spawnArgs={domainSerialConsoleCommand({ vm, alias: pty.alias })} />))} | ||
{vnc && | ||
<Vnc type="VncConsole" | ||
vmName={vm.name} | ||
vmId={vm.id} | ||
connectionName={vm.connectionName} | ||
consoleDetail={vnc} | ||
onAddErrorNotification={onAddErrorNotification} | ||
isExpanded={isExpanded} />} | ||
{(vnc || spice) && | ||
<DesktopConsole type="DesktopViewer" | ||
onDesktopConsole={onDesktopConsole} | ||
vnc={vnc} | ||
spice={spice} />} | ||
</AccessConsoles> | ||
<> | ||
{selectedConsole === 'SerialConsole' && serial.map((pty, idx) => ( | ||
<SerialConsole type={serial.length == 1 ? "SerialConsole" : cockpit.format(_("Serial console ($0)"), pty.alias || idx)} | ||
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. When there is more than one serial console, this will just show them all, no? I think there needs to be dropdown here to select one of them when there are multiple. |
||
key={"pty-" + idx} | ||
connectionName={vm.connectionName} | ||
vmName={vm.name} | ||
spawnArgs={domainSerialConsoleCommand({ vm, alias: pty.alias })} /> | ||
))} | ||
{selectedConsole === 'VncConsole' && !vnc && ( | ||
<Button variant="secondary" size="sm" | ||
onClick={openVncAdd}> | ||
{_("Add VNC")} | ||
</Button> | ||
)} | ||
{selectedConsole === 'VncConsole' && vnc && ( | ||
<Vnc type="VncConsole" | ||
vmName={vm.name} | ||
vmId={vm.id} | ||
connectionName={vm.connectionName} | ||
consoleDetail={vnc} | ||
onAddErrorNotification={onAddErrorNotification} | ||
isExpanded={isExpanded} /> | ||
)} | ||
{selectedConsole === 'DesktopViewer' && (vnc || spice) && ( | ||
<DesktopConsole type="DesktopViewer" | ||
onDesktopConsole={onDesktopConsole} | ||
vnc={vnc} | ||
spice={spice} /> | ||
)} | ||
</> | ||
); | ||
} | ||
} | ||
Consoles.propTypes = { | ||
vm: PropTypes.object.isRequired, | ||
onAddErrorNotification: PropTypes.func.isRequired, | ||
selectedConsole: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default Consoles; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,14 +17,18 @@ | |
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import cockpit from 'cockpit'; | ||
|
||
import { logDebug } from "../../../helpers.js"; | ||
import { VncConsole } from '@patternfly/react-console'; | ||
import { Dropdown, DropdownItem, DropdownList } from "@patternfly/react-core/dist/esm/components/Dropdown"; | ||
import { MenuToggle } from "@patternfly/react-core/dist/esm/components/MenuToggle"; | ||
import { Divider } from "@patternfly/react-core/dist/esm/components/Divider"; | ||
import { Button, Text, TextContent, TextVariants } from '@patternfly/react-core'; | ||
import { DialogsContext } from 'dialogs.jsx'; | ||
import { EditVNCModal } from './vncEdit.jsx'; | ||
|
||
import { logDebug } from '../../../helpers.js'; | ||
import { domainSendKey } from '../../../libvirtApi/domain.js'; | ||
|
||
const _ = cockpit.gettext; | ||
|
@@ -48,18 +52,26 @@ | |
KEY_DELETE: 111, | ||
}; | ||
|
||
class Vnc extends React.Component { | ||
Check warning Code scanning / CodeQL Unused or undefined state property Warning
Component state property 'vncAddress' is
written Error loading related location Loading Component state property 'vncPort' is written Error loading related location Loading Component state property 'vncPassword' is written Error loading related location Loading Component state property 'dialogError' is written Error loading related location Loading Component state property 'dialogErrorDetail' is written Error loading related location Loading |
||
static contextType = DialogsContext; | ||
|
||
constructor(props) { | ||
super(props); | ||
|
||
this.state = { | ||
path: undefined, | ||
isActionOpen: false, | ||
vncAddress: props.consoleDetail.address || '', | ||
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. Not used anymore. |
||
vncPort: props.consoleDetail.port || '', | ||
vncPassword: props.consoleDetail.password || '', | ||
}; | ||
|
||
this.connect = this.connect.bind(this); | ||
this.onDisconnected = this.onDisconnected.bind(this); | ||
this.onInitFailed = this.onInitFailed.bind(this); | ||
this.onExtraKeysDropdownToggle = this.onExtraKeysDropdownToggle.bind(this); | ||
this.onValueChanged = this.onValueChanged.bind(this); | ||
this.dialogErrorSet = this.dialogErrorSet.bind(this); | ||
} | ||
|
||
connect(props) { | ||
|
@@ -114,7 +126,17 @@ | |
this.setState({ isActionOpen: false }); | ||
} | ||
|
||
onValueChanged(key, value) { | ||
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. Unused. |
||
const stateDelta = { [key]: value }; | ||
this.setState(stateDelta); | ||
} | ||
|
||
dialogErrorSet(text, detail) { | ||
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. Unused. |
||
this.setState({ dialogError: text, dialogErrorDetail: detail }); | ||
} | ||
|
||
render() { | ||
const Dialogs = this.context; | ||
const { consoleDetail, connectionName, vmName, vmId, onAddErrorNotification, isExpanded } = this.props; | ||
const { path, isActionOpen } = this.state; | ||
if (!consoleDetail || !path) { | ||
|
@@ -161,8 +183,17 @@ | |
</Dropdown> | ||
]; | ||
|
||
const openVncEdit = () => { | ||
Dialogs.show(<EditVNCModal idPrefix={`${vmId}-edit-vnc`} | ||
vmName={vmName} | ||
vmId={vmId} | ||
connectionName={connectionName} | ||
consoleDetail={consoleDetail} />); | ||
}; | ||
|
||
return ( | ||
<VncConsole host={window.location.hostname} | ||
<> | ||
<VncConsole host={window.location.hostname} | ||
port={window.location.port || (encrypt ? '443' : '80')} | ||
path={path} | ||
encrypt={encrypt} | ||
|
@@ -178,11 +209,29 @@ | |
consoleContainerId={isExpanded ? "vnc-display-container-expanded" : "vnc-display-container-minimized"} | ||
resizeSession | ||
scaleViewport | ||
/> | ||
/> | ||
<TextContent> | ||
<Text component={TextVariants.p}> | ||
{`VNC address=${consoleDetail.address} port=${consoleDetail.port} `} | ||
<Button variant="link" | ||
onClick={openVncEdit}> | ||
{_("Edit")} | ||
</Button> | ||
</Text> | ||
</TextContent> | ||
</> | ||
); | ||
} | ||
} | ||
|
||
// TODO: define propTypes | ||
Vnc.propTypes = { | ||
vmName: PropTypes.string.isRequired, | ||
vmId: PropTypes.string.isRequired, | ||
connectionName: PropTypes.string.isRequired, | ||
consoleDetail: PropTypes.object.isRequired, | ||
onAddErrorNotification: PropTypes.func.isRequired, | ||
isExpanded: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default Vnc; |
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.
Not used anymore, please remove.