diff --git a/src/components/vm/consoles/consoles.jsx b/src/components/vm/consoles/consoles.jsx
index 46e649729..ee579d7dd 100644
--- a/src/components/vm/consoles/consoles.jsx
+++ b/src/components/vm/consoles/consoles.jsx
@@ -58,10 +58,10 @@ class Consoles extends React.Component {
const { vm } = this.props;
if (vm.displays) {
- if (vm.displays.vnc) {
+ if (vm.displays.find(display => display.type == "vnc")) {
return 'VncConsole';
}
- if (vm.displays.spice) {
+ if (vm.displays.find(display => display.type == "spice")) {
return 'DesktopViewer';
}
}
@@ -78,20 +78,21 @@ class Consoles extends React.Component {
onDesktopConsoleDownload (type) {
const { vm } = this.props;
// fire download of the .vv file
- domainDesktopConsole({ name: vm.name, id: vm.id, connectionName: vm.connectionName, consoleDetail: vm.displays[type] });
+ domainDesktopConsole({ name: vm.name, id: vm.id, connectionName: vm.connectionName, consoleDetail: vm.displays.find(display => display.type == type) });
}
render () {
const { vm, onAddErrorNotification } = this.props;
+ const spice = vm.displays && vm.displays.find(display => display.type == 'spice');
+ const serial = vm.displays && vm.displays.filter(display => display.type == 'pty');
+ const vnc = vm.displays && vm.displays.find(display => display.type == 'vnc');
if (!domainCanConsole || !domainCanConsole(vm.state)) {
return ();
}
- const serialConsoleCommand = domainSerialConsoleCommand({ vm });
-
const onDesktopConsole = () => { // prefer spice over vnc
- this.onDesktopConsoleDownload(vm.displays.spice ? 'spice' : 'vnc');
+ this.onDesktopConsoleDownload(spice ? 'spice' : 'vnc');
};
return (
@@ -100,22 +101,23 @@ class Consoles extends React.Component {
textSerialConsole={_("Serial console")}
textVncConsole={_("VNC console")}
textDesktopViewerConsole={_("Desktop viewer")}>
- {!!serialConsoleCommand &&
- }
- {vm.displays && vm.displays.vnc &&
+ {serial.map((pty, idx) => )}
+ {vnc &&
}
- {vm.displays && (vm.displays.vnc || vm.displays.spice) &&
+ {(vnc || spice) &&
}
+ vnc={vnc}
+ spice={spice} />}
);
}
diff --git a/src/components/vm/consoles/desktopConsole.jsx b/src/components/vm/consoles/desktopConsole.jsx
index 61393c36a..454551953 100644
--- a/src/components/vm/consoles/desktopConsole.jsx
+++ b/src/components/vm/consoles/desktopConsole.jsx
@@ -36,10 +36,10 @@ function fmt_to_fragments(fmt) {
return React.createElement.apply(null, [React.Fragment, { }].concat(fmt.split(/(\$[0-9]+)/g).map(replace)));
}
-const DesktopConsoleDownload = ({ displays, onDesktopConsole }) => {
+const DesktopConsoleDownload = ({ vnc, spice, onDesktopConsole }) => {
return (
- vmState == 'running';
export const domainCanRename = (vmState) => vmState == 'shut off';
export const domainCanResume = (vmState) => vmState == 'paused';
export const domainIsRunning = (vmState) => domainCanReset(vmState);
-export const domainSerialConsoleCommand = ({ vm }) => vm.displays.pty ? ['virsh', ...VMS_CONFIG.Virsh.connections[vm.connectionName].params, 'console', vm.name] : false;
+export const domainSerialConsoleCommand = ({ vm, alias }) => {
+ if (vm.displays.find(display => display.type == 'pty'))
+ return ['virsh', ...VMS_CONFIG.Virsh.connections[vm.connectionName].params, 'console', vm.name, alias || ''];
+ else
+ return false;
+};
let pythonPath;
diff --git a/src/libvirtUtils.js b/src/libvirtUtils.js
index ad5af9ddd..4a29c0243 100644
--- a/src/libvirtUtils.js
+++ b/src/libvirtUtils.js
@@ -52,12 +52,13 @@ function prepareParamsFromObjOfObjs(objectData, valueTransformer) {
}
export function prepareDisplaysParam(displays) {
- return prepareParamsFromObjOfObjs(displays, display => {
+ return prepareParamsFromArrOfObjs(displays.filter(display => ["vnc", "spice"].includes(display.type)), display => {
return {
type: display.type,
listen: display.address,
port: display.port,
tlsport: display.tlsPort,
+ alias: display.alias,
};
});
}
diff --git a/test/check-machines-consoles b/test/check-machines-consoles
index 85b5da54b..5e34a7b40 100755
--- a/test/check-machines-consoles
+++ b/test/check-machines-consoles
@@ -103,6 +103,7 @@ class TestMachinesConsoles(VirtualMachinesCase):
@no_retry_when_changed
def testSerialConsole(self):
b = self.browser
+ m = self.machine
name = "vmWithSerialConsole"
self.createVm(name, graphics='vnc', ptyconsole=True)
@@ -130,10 +131,21 @@ class TestMachinesConsoles(VirtualMachinesCase):
b.click("button:contains(Expand)")
b.assert_pixels("#vm-vmWithSerialConsole-consoles-page", "vm-details-console-serial")
+ # Add a second serial console
+ m.execute("virsh destroy vmWithSerialConsole && virt-xml --add-device vmWithSerialConsole --console pty,target_type=virtio && virsh start vmWithSerialConsole")
+ b.click("#pf-c-console__type-selector")
+ b.wait_visible("#pf-c-console__type-selector + .pf-c-select__menu")
+ b.click("li:contains('Serial console (console0)') button")
+ b.wait(lambda: m.execute("ps aux | grep 'virsh -c qemu:///system console vmWithSerialConsole console0'"))
+ b.click("#pf-c-console__type-selector")
+ b.click("li:contains('Serial console (console1)') button")
+ b.wait(lambda: m.execute("ps aux | grep 'virsh -c qemu:///system console vmWithSerialConsole console1'"))
+
# disconnecting the serial console closes the pty channel
self.allow_journal_messages("connection unexpectedly closed by peer",
".*Connection reset by peer")
self.allow_browser_errors("Disconnection timed out.")
+ self.allow_journal_messages(".* couldn't shutdown fd: Transport endpoint is not connected")
def testSwitchConsoleFromSerialToGraphical(self):
b = self.browser