@@ -23,6 +23,7 @@ import { AccessConsoles } from "@patternfly/react-console";
23
23
import { Button } from "@patternfly/react-core/dist/esm/components/Button" ;
24
24
import { Card , CardBody , CardFooter , CardHeader , CardTitle } from '@patternfly/react-core/dist/esm/components/Card' ;
25
25
import { ExpandIcon , HelpIcon } from '@patternfly/react-icons' ;
26
+ import { ToggleGroup , ToggleGroupItem } from '@patternfly/react-core/dist/esm/components/ToggleGroup' ;
26
27
27
28
import SerialConsole from './serialConsole.jsx' ;
28
29
import Vnc , { VncState } from './vnc.jsx' ;
@@ -39,132 +40,118 @@ import './consoles.css';
39
40
40
41
const _ = cockpit . gettext ;
41
42
42
- class Consoles extends React . Component {
43
- constructor ( props ) {
44
- super ( props ) ;
43
+ export function console_default ( vm ) {
44
+ const serials = vm . displays && vm . displays . filter ( display => display . type == 'pty' ) ;
45
+ const vnc = vm . displays && vm . displays . find ( display => display . type == 'vnc' ) ;
45
46
46
- this . state = {
47
- serial : props . vm . displays && props . vm . displays . filter ( display => display . type == 'pty' ) ,
48
- } ;
49
-
50
- this . getDefaultConsole = this . getDefaultConsole . bind ( this ) ;
51
- this . onDesktopConsoleDownload = this . onDesktopConsoleDownload . bind ( this ) ;
52
- }
53
-
54
- static getDerivedStateFromProps ( nextProps , prevState ) {
55
- const oldSerial = prevState . serial ;
56
- const newSerial = nextProps . vm . displays && nextProps . vm . displays . filter ( display => display . type == 'pty' ) ;
57
-
58
- if ( newSerial . length !== oldSerial . length || oldSerial . some ( ( pty , index ) => pty . alias !== newSerial [ index ] . alias ) )
59
- return { serial : newSerial } ;
47
+ if ( vnc || serials . length == 0 )
48
+ return "vnc" ;
49
+ else
50
+ return "serial0" ;
51
+ }
60
52
61
- return null ;
53
+ export function console_name ( vm , type ) {
54
+ if ( ! type )
55
+ type = console_default ( vm ) ;
56
+
57
+ if ( type . startsWith ( "serial" ) ) {
58
+ const serials = vm . displays && vm . displays . filter ( display => display . type == 'pty' ) ;
59
+ if ( serials . length == 1 )
60
+ return _ ( "Serial console" ) ;
61
+ const idx = Number ( type . substr ( 6 ) ) ;
62
+ return cockpit . format ( _ ( "Serial console ($0)" ) , serials [ idx ] ?. alias || idx ) ;
63
+ } else if ( type == "vnc" ) {
64
+ return _ ( "Graphical console" ) ;
65
+ } else {
66
+ return _ ( "Console" ) ;
62
67
}
68
+ }
63
69
64
- getDefaultConsole ( ) {
65
- const { vm } = this . props ;
66
-
67
- if ( vm . displays ) {
68
- if ( vm . displays . find ( display => display . type == "vnc" ) ) {
69
- return 'VncConsole' ;
70
- }
71
- if ( vm . displays . find ( display => display . type == "spice" ) ) {
72
- return 'DesktopViewer' ;
73
- }
70
+ function connection_address ( ) {
71
+ let address ;
72
+ if ( cockpit . transport . host == "localhost" ) {
73
+ const app = cockpit . transport . application ( ) ;
74
+ if ( app . startsWith ( "cockpit+=" ) ) {
75
+ address = app . substr ( 9 ) ;
76
+ } else {
77
+ address = window . location . hostname ;
74
78
}
75
-
76
- const serialConsoleCommand = domainSerialConsoleCommand ( { vm } ) ;
77
- if ( serialConsoleCommand ) {
78
- return 'SerialConsole' ;
79
+ } else {
80
+ address = cockpit . transport . host ;
81
+ const pos = address . indexOf ( "@" ) ;
82
+ if ( pos >= 0 ) {
83
+ address = address . substr ( pos + 1 ) ;
79
84
}
80
-
81
- // no console defined, but the VncConsole is always there and
82
- // will instruct people how to enable it for real.
83
- return 'VncConsole' ;
84
85
}
86
+ return address ;
87
+ }
85
88
86
- onDesktopConsoleDownload ( type ) {
87
- const { vm } = this . props ;
88
- // fire download of the .vv file
89
- const consoleDetail = vm . displays . find ( display => display . type == type ) ;
90
-
91
- let address ;
92
- if ( cockpit . transport . host == "localhost" ) {
93
- const app = cockpit . transport . application ( ) ;
94
- if ( app . startsWith ( "cockpit+=" ) ) {
95
- address = app . substr ( 9 ) ;
96
- } else {
97
- address = window . location . hostname ;
98
- }
99
- } else {
100
- address = cockpit . transport . host ;
101
- const pos = address . indexOf ( "@" ) ;
102
- if ( pos >= 0 ) {
103
- address = address . substr ( pos + 1 ) ;
104
- }
105
- }
89
+ function console_launch ( vm , consoleDetail ) {
90
+ // fire download of the .vv file
91
+ domainDesktopConsole ( { name : vm . name , consoleDetail : { ...consoleDetail , address : connection_address ( ) } } ) ;
92
+ }
93
+
94
+ export const Console = ( { vm, config, type, onAddErrorNotification, isExpanded } ) => {
95
+ let con = null ;
106
96
107
- domainDesktopConsole ( { name : vm . name , consoleDetail : { ...consoleDetail , address } } ) ;
97
+ if ( ! type )
98
+ type = console_default ( vm ) ;
99
+
100
+ if ( vm . state != "running" ) {
101
+ const vnc = vm . inactiveXML . displays && vm . inactiveXML . displays . find ( display => display . type == 'vnc' ) ;
102
+ const spice = vm . inactiveXML . displays && vm . inactiveXML . displays . find ( display => display . type == 'spice' ) ;
103
+ return < VncState vm = { vm } vnc = { vnc } spice = { spice } /> ;
108
104
}
109
105
110
- render ( ) {
111
- const { vm, onAddErrorNotification, isExpanded } = this . props ;
112
- const { serial } = this . state ;
113
- const spice = vm . displays && vm . displays . find ( display => display . type == 'spice' ) ;
106
+ if ( type . startsWith ( "serial" ) ) {
107
+ const serials = vm . displays && vm . displays . filter ( display => display . type == 'pty' ) ;
108
+ const idx = Number ( type . substr ( 6 ) ) ;
109
+ if ( serials . length > idx )
110
+ con = < SerialConsole
111
+ type = { type }
112
+ connectionName = { vm . connectionName }
113
+ vmName = { vm . name }
114
+ spawnArgs = { domainSerialConsoleCommand ( { vm, alias : serials [ idx ] . alias } ) } /> ;
115
+ } else if ( type == "vnc" ) {
114
116
const vnc = vm . displays && vm . displays . find ( display => display . type == 'vnc' ) ;
115
117
const inactive_vnc = vm . inactiveXML . displays && vm . inactiveXML . displays . find ( display => display . type == 'vnc' ) ;
118
+ const spice = vm . displays && vm . displays . find ( display => display . type == 'spice' ) ;
116
119
117
- if ( ! domainCanConsole || ! domainCanConsole ( vm . state ) ) {
118
- return (
119
- < div id = "vm-not-running-message" >
120
- < VncState vm = { vm } vnc = { inactive_vnc } />
121
- </ div >
122
- ) ;
123
- }
124
-
125
- const onDesktopConsole = ( ) => { // prefer spice over vnc
126
- this . onDesktopConsoleDownload ( spice ? 'spice' : 'vnc' ) ;
127
- } ;
120
+ con = < Vnc
121
+ type = "VncConsole"
122
+ vm = { vm }
123
+ consoleDetail = { vnc }
124
+ spiceDetail = { spice }
125
+ inactiveConsoleDetail = { inactive_vnc }
126
+ onAddErrorNotification = { onAddErrorNotification }
127
+ onLaunch = { ( ) => console_launch ( vm , vnc || spice ) }
128
+ connectionAddress = { connection_address ( ) }
129
+ isExpanded = { isExpanded } /> ;
130
+ }
128
131
132
+ if ( con ) {
129
133
return (
130
- < AccessConsoles preselectedType = { this . getDefaultConsole ( ) }
131
- textSelectConsoleType = { _ ( "Select console type" ) }
132
- textSerialConsole = { _ ( "Serial console" ) }
133
- textVncConsole = { _ ( "Graphical console" ) }
134
- textDesktopViewerConsole = { _ ( "Desktop viewer" ) } >
135
- { serial . map ( ( pty , idx ) => ( < SerialConsole type = { serial . length == 1 ? "SerialConsole" : cockpit . format ( _ ( "Serial console ($0)" ) , pty . alias || idx ) }
136
- key = { "pty-" + idx }
137
- connectionName = { vm . connectionName }
138
- vmName = { vm . name }
139
- spawnArgs = { domainSerialConsoleCommand ( { vm, alias : pty . alias } ) } /> ) ) }
140
- < Vnc type = "VncConsole"
141
- vm = { vm }
142
- consoleDetail = { vnc }
143
- inactiveConsoleDetail = { inactive_vnc }
144
- onAddErrorNotification = { onAddErrorNotification }
145
- isExpanded = { isExpanded } />
146
- { ( vnc || spice ) &&
147
- < DesktopConsole type = "DesktopViewer"
148
- onDesktopConsole = { onDesktopConsole }
149
- vnc = { vnc }
150
- spice = { spice } /> }
151
- </ AccessConsoles >
134
+ < div className = "pf-v5-c-console" >
135
+ { con }
136
+ </ div >
152
137
) ;
153
138
}
154
- }
155
-
156
- Consoles . propTypes = {
157
- vm : PropTypes . object . isRequired ,
158
- onAddErrorNotification : PropTypes . func . isRequired ,
159
139
} ;
160
140
161
- export default Consoles ;
141
+ export const ConsoleCard = ( { vm, config, type, setType, onAddErrorNotification } ) => {
142
+ const serials = vm . displays && vm . displays . filter ( display => display . type == 'pty' ) ;
162
143
163
- export const ConsoleCard = ( { vm, config, onAddErrorNotification } ) => {
164
- let actions = null ;
165
- if ( vm . state != "shut off" ) {
166
- actions = (
144
+ if ( ! type )
145
+ type = console_default ( vm ) ;
146
+
147
+ const actions = [ ] ;
148
+ const tabs = [ ] ;
149
+ let body ;
150
+
151
+ if ( vm . state == "running" ) {
152
+ actions . push (
167
153
< Button
154
+ key = "expand"
168
155
variant = "link"
169
156
onClick = { ( ) => {
170
157
const urlOptions = { name : vm . name , connection : vm . connectionName } ;
@@ -174,6 +161,33 @@ export const ConsoleCard = ({ vm, config, onAddErrorNotification }) => {
174
161
iconPosition = "right" > { _ ( "Expand" ) }
175
162
</ Button >
176
163
) ;
164
+
165
+ if ( serials . length > 0 )
166
+ tabs . push ( < ToggleGroupItem
167
+ key = "vnc"
168
+ text = { _ ( "Graphical" ) }
169
+ isSelected = { type == "vnc" }
170
+ onChange = { ( ) => setType ( "vnc" ) } /> ) ;
171
+
172
+ serials . forEach ( ( pty , idx ) => {
173
+ const t = "serial" + idx ;
174
+ tabs . push ( < ToggleGroupItem
175
+ key = { t }
176
+ text = { serials . length == 1 ? _ ( "Serial" ) : cockpit . format ( _ ( "Serial ($0)" ) , pty . alias || idx ) }
177
+ isSelected = { type == t }
178
+ onChange = { ( ) => setType ( t ) } /> ) ;
179
+ } )
180
+
181
+ body = < Console
182
+ vm = { vm }
183
+ config = { config }
184
+ onAddErrorNotification = { onAddErrorNotification }
185
+ type = { type }
186
+ isExpanded = { false } />
187
+ } else {
188
+ const vnc = vm . inactiveXML . displays && vm . inactiveXML . displays . find ( display => display . type == 'vnc' ) ;
189
+ const spice = vm . inactiveXML . displays && vm . inactiveXML . displays . find ( display => display . type == 'spice' ) ;
190
+ body = < VncState vm = { vm } vnc = { vnc } spice = { spice } /> ;
177
191
}
178
192
179
193
return (
@@ -184,9 +198,10 @@ export const ConsoleCard = ({ vm, config, onAddErrorNotification }) => {
184
198
isClickable >
185
199
< CardHeader actions = { { actions } } >
186
200
< CardTitle component = "h2" > { _ ( "Console" ) } </ CardTitle >
201
+ < ToggleGroup > { tabs } </ ToggleGroup >
187
202
</ CardHeader >
188
203
< CardBody >
189
- < Consoles vm = { vm } config = { config } onAddErrorNotification = { onAddErrorNotification } />
204
+ { body }
190
205
</ CardBody >
191
206
< CardFooter />
192
207
</ Card >
0 commit comments