diff --git a/pkg/shell/hosts.jsx b/pkg/shell/hosts.jsx index b9407b590ab9..417887d6c3b1 100644 --- a/pkg/shell/hosts.jsx +++ b/pkg/shell/hosts.jsx @@ -75,6 +75,7 @@ export class CockpitHosts extends React.Component { current_key: props.machine.key, show_modal: false, edit_machine: null, + switch_machine: null, }; this.toggleMenu = this.toggleMenu.bind(this); @@ -82,13 +83,24 @@ export class CockpitHosts extends React.Component { this.onAddNewHost = this.onAddNewHost.bind(this); this.onEditHosts = this.onEditHosts.bind(this); this.onHostEdit = this.onHostEdit.bind(this); + this.onHostSwitch = this.onHostSwitch.bind(this); this.onRemove = this.onRemove.bind(this); } componentDidMount() { + console.log("DID MOUNT"); cockpit.user().then(user => { this.setState({ current_user: user.name || "" }); }).catch(exc => console.log(exc)); + + window.trigger_connection_flow = machine => { + if (!this.state.show_modal) + this.onHostSwitch(machine, machine.connection_string); + }; + } + + componentWillUnmount() { + window.trigger_connection_flow = null; } static getDerivedStateFromProps(nextProps, prevState) { @@ -124,6 +136,14 @@ export class CockpitHosts extends React.Component { this.setState({ show_modal: true, edit_machine: machine }); } + onHostSwitch(machine, addr) { + console.log("SWITCH", addr, machine.state); + if (machine.state == "connected" || machine.address == "localhost") + this.props.jump(addr); + else if (machine.state != "connecting") + this.setState({ show_modal: true, switch_machine: machine }); + } + onEditHosts() { this.setState(s => { return { editing: !s.editing } }); } @@ -180,7 +200,7 @@ export class CockpitHosts extends React.Component { header={(m.user ? m.user : this.state.current_user) + " @"} status={m.state === "failed" ? { type: "error", title: _("Connection error") } : null} className={m.state} - jump={this.props.jump} + jump={addr => this.onHostSwitch(m, addr)} actions={<> @@ -242,22 +262,27 @@ export class CockpitHosts extends React.Component { {this.state.show_modal && this.setState({ show_modal: false, edit_machine: null })} - address={this.state.edit_machine ? this.state.edit_machine.address : null} - caller_callback={this.state.edit_machine - ? (new_connection_string) => { - const parts = this.props.machines.split_connection_string(new_connection_string); - if (this.state.edit_machine == this.props.machine && parts.address != this.state.edit_machine.address) { - const addr = this.props.hostAddr({ host: parts.address }, true); + onClose={() => this.setState({ show_modal: false, + edit_machine: null, switch_machine: null })} + address={this.state.edit_machine?.address || this.state.switch_machine?.address} + template={this.state.switch_machine ? "connect" : null} + caller_callback={(new_connection_string) => { + console.log("CALLBACK", new_connection_string); + const parts = this.props.machines.split_connection_string(new_connection_string); + const addr = this.props.hostAddr({ host: parts.address }, true); + if (this.state.edit_machine) { + if (this.state.edit_machine == this.props.machine && + parts.address != this.state.edit_machine.address) { this.props.jump(addr); } - return Promise.resolve(); - } - : (new_connection_string) => { - const parts = this.props.machines.split_connection_string(new_connection_string); + } else { this.props.loader.connect(parts.address); + if (this.state.switch_machine) + this.props.jump(addr); return Promise.resolve(); - }} /> + } + }} + /> } ); diff --git a/pkg/shell/hosts_dialog.jsx b/pkg/shell/hosts_dialog.jsx index 6ac3af372bd5..6cc5d95a0358 100644 --- a/pkg/shell/hosts_dialog.jsx +++ b/pkg/shell/hosts_dialog.jsx @@ -46,6 +46,7 @@ import { ModalError } from "cockpit-components-inline-notification.jsx"; const _ = cockpit.gettext; export const codes = { + "danger": "connect", "no-cockpit": "not-supported", "not-supported": "not-supported", "protocol-error": "not-supported", @@ -108,6 +109,61 @@ export const CrossMachineWarning = () => { title={_("Malicious pages on a remote machine may affect other connected hosts")} />; }; +class Connect extends React.Component { + constructor(props) { + super(props); + + this.state = { + inProgress: false, + }; + } + + onConnect() { + console.log("CONNECT", this.props.full_address); + + this.setState({ inProgress: true }); + this.props.run(this.props.try2Connect(this.props.full_address), ex => { + if (ex.problem === "no-host") { + let host_id_port = address; + let port = "22"; + const port_index = host_id_port.lastIndexOf(":"); + if (port_index === -1) + host_id_port = address + ":22"; + else + port = host_id_port.substr(port_index + 1); + + ex.message = cockpit.format(_("Unable to contact the given host $0. Make sure it has ssh running on port $1, or specify another port in the address."), host_id_port, port); + ex.problem = "not-found"; + } + this.setState({ inProgress: false }); + this.props.setError(ex); + }); + } + + render() { + return ( + + + + } + > + + + + + ); + } +} + class AddMachine extends React.Component { constructor(props) { super(props); @@ -229,22 +285,24 @@ class AddMachine extends React.Component { }); }); - this.props.run(this.props.try2Connect(address), ex => { - if (ex.problem === "no-host") { - let host_id_port = address; - let port = "22"; - const port_index = host_id_port.lastIndexOf(":"); - if (port_index === -1) - host_id_port = address + ":22"; - else - port = host_id_port.substr(port_index + 1); - - ex.message = cockpit.format(_("Unable to contact the given host $0. Make sure it has ssh running on port $1, or specify another port in the address."), host_id_port, port); - ex.problem = "not-found"; - } - this.setState({ inProgress: false }); - this.props.setError(ex); - }); + this.props.setError({ problem: "danger", command: "close" }); + + // this.props.run(this.props.try2Connect(address), ex => { + // if (ex.problem === "no-host") { + // let host_id_port = address; + // let port = "22"; + // const port_index = host_id_port.lastIndexOf(":"); + // if (port_index === -1) + // host_id_port = address + ":22"; + // else + // port = host_id_port.substr(port_index + 1); + + // ex.message = cockpit.format(_("Unable to contact the given host $0. Make sure it has ssh running on port $1, or specify another port in the address."), host_id_port, port); + // ex.problem = "not-found"; + // } + // this.setState({ inProgress: false }); + // this.props.setError(ex); + // }); } render() { @@ -295,7 +353,6 @@ class AddMachine extends React.Component { { this.props.dialogError && } {body} - ); @@ -393,7 +450,6 @@ class MachinePort extends React.Component { { this.props.dialogError && } {body} - ); @@ -527,7 +583,6 @@ class HostKey extends React.Component { { this.props.dialogError && } {body} - ); @@ -905,13 +960,37 @@ class ChangeAuth extends React.Component { { this.props.dialogError && } {body} - ); } } +export function try2Connect(machines_ins, address, options) { + return new Promise((resolve, reject) => { + const conn_options = { ...options, payload: "echo", host: address }; + + conn_options["init-superuser"] = get_init_superuser_for_options(conn_options); + + const machine = machines_ins.lookup(address); + if (machine && machine.host_key && !machine.on_disk) { + conn_options['temp-session'] = false; // Compatibility option + conn_options.session = 'shared'; + conn_options['host-key'] = machine.host_key; + } + + const client = cockpit.channel(conn_options); + client.send("x"); + client.addEventListener("message", () => { + resolve(); + client.close(); + }); + client.addEventListener("close", (event, options) => { + reject(options); + }); + }); + } + export class HostModal extends React.Component { constructor(props) { super(props); @@ -950,28 +1029,7 @@ export class HostModal extends React.Component { } try2Connect(address, options) { - return new Promise((resolve, reject) => { - const conn_options = { ...options, payload: "echo", host: address }; - - conn_options["init-superuser"] = get_init_superuser_for_options(conn_options); - - const machine = this.props.machines_ins.lookup(address); - if (machine && machine.host_key && !machine.on_disk) { - conn_options['temp-session'] = false; // Compatibility option - conn_options.session = 'shared'; - conn_options['host-key'] = machine.host_key; - } - - const client = cockpit.channel(conn_options); - client.send("x"); - client.addEventListener("message", () => { - resolve(); - client.close(); - }); - client.addEventListener("close", (event, options) => { - reject(options); - }); - }); + return try2Connect(this.props.machines_ins, address, options); } complete() { @@ -1060,7 +1118,9 @@ export class HostModal extends React.Component { complete: this.complete, }; - if (template === "add-machine") + if (template === "connect") + return ; + else if (template === "add-machine") return ; else if (template === "unknown-hostkey" || template === "unknown-host" || template === "invalid-hostkey") return ; diff --git a/pkg/shell/indexes.jsx b/pkg/shell/indexes.jsx index 9b5077f77f83..0e4ae75b562f 100644 --- a/pkg/shell/indexes.jsx +++ b/pkg/shell/indexes.jsx @@ -30,7 +30,6 @@ import { EmptyStatePanel } from "cockpit-components-empty-state.jsx"; import { CockpitNav, CockpitNavItem, SidebarToggle } from "./nav.jsx"; import { TopNav } from ".//topnav.jsx"; import { CockpitHosts, CockpitCurrentHost } from "./hosts.jsx"; -import { codes, HostModal, CrossMachineWarning } from "./hosts_dialog.jsx"; import { EarlyFailure, EarlyFailureReady } from './failures.jsx'; import { WithDialogs } from "dialogs.jsx"; @@ -85,9 +84,6 @@ function MachinesIndex(index_options, machines, loader) { update_topbar(); }); - /* Is troubleshooting dialog open */ - let troubleshooting_opened = false; - sidebar_toggle_root.render(); // Focus with skiplinks @@ -175,11 +171,16 @@ function MachinesIndex(index_options, machines, loader) { paragraph={cockpit.message(watchdog_problem)} />); } + function trigger_connection_flow(machine) { + if (window.trigger_connection_flow) + window.trigger_connection_flow(machine); + } + /* Handles navigation */ function navigate(state, reconnect) { /* If this is a watchdog problem or we are troubleshooting * let the dialog handle it */ - if (watchdog_problem || troubleshooting_opened) + if (watchdog_problem) return; if (!state) @@ -210,6 +211,8 @@ function MachinesIndex(index_options, machines, loader) { machine.problem = "not-found"; } else if (reconnect || state.host == "localhost") { loader.connect(state.host); + } else { + trigger_connection_flow(machine); } const compiled = compile(machine); @@ -449,31 +452,7 @@ function MachinesIndex(index_options, machines, loader) { return component; } - let troubleshoot_dialog_root = null; - function update_frame(machine, state, compiled) { - function render_troubleshoot() { - troubleshooting_opened = true; - const template = codes[machine.problem] || "change-port"; - if (!troubleshoot_dialog_root) - troubleshoot_dialog_root = root('troubleshoot-dialog'); - troubleshoot_dialog_root.render(React.createElement(HostModal, { - template, - address: machine.address, - machines_ins: machines, - onClose: () => { - troubleshoot_dialog_root.unmount(); - troubleshoot_dialog_root = null; - troubleshooting_opened = false; - navigate(null, true); - } - })); - } - - function connect_machine() { - loader.connect(machine.address); - } - let current_frame = index.current_frame(); if (machine.state != "connected") { @@ -482,65 +461,22 @@ function MachinesIndex(index_options, machines, loader) { current_frame = null; index.current_frame(current_frame); - const connecting = (machine.state == "connecting"); - let title, message; - if (machine.restarting) { - title = _("The machine is rebooting"); - message = ""; - } else if (connecting) { - title = _("Connecting to the machine"); - message = ""; - } else { - title = _("Not connected to host"); - if (machine.problem == "not-found") { - message = _("Cannot connect to an unknown host"); - } else { - const error = machine.problem || machine.state; - if (error) - message = cockpit.message(error); - else - message = ""; - } - } - - let troubleshooting = false; - - if (!machine.restarting && (machine.problem === "no-host" || !!codes[machine.problem])) { - troubleshooting = true; - } - - const restarting = !!machine.restarting; - const reconnect = !connecting && machine.problem != "not-found" && !troubleshooting; - document.querySelector("#early-failure-ready").removeAttribute("hidden"); - if (machine.address == "localhost") { - early_failure_ready_root.render( - ); - } else { - early_failure_ready_root.render( - - - } - action={} /> - - ); - } - + early_failure_ready_root.render( + + + trigger_connection_flow(machine)}> + {_("Connect")} + } /> + + ); update_title(null, machine); /* Fall through when connecting, and allow frame to load at same time */ - if (!connecting) + if (machine.state != "connecting") return; }