@@ -15,16 +15,18 @@ import {
15
15
Text ,
16
16
Tooltip ,
17
17
} from '@patternfly/react-core'
18
- import { GlobeAmericasIcon , PencilAltIcon , SearchIcon } from '@patternfly/react-icons'
18
+ import { ExternalLinkAltIcon , GlobeAmericasIcon , PencilAltIcon , SearchIcon } from '@patternfly/react-icons'
19
19
import _ from 'lodash'
20
20
import { Fragment , useEffect , useMemo , useState } from 'react'
21
21
import { generatePath , Link , useNavigate } from 'react-router-dom-v5-compat'
22
22
import { findResourceFieldLineNumber } from '../../../components/YamlEditor'
23
23
import { useTranslation } from '../../../lib/acm-i18next'
24
24
import { canUser } from '../../../lib/rbac-util'
25
25
import { NavigationPath } from '../../../NavigationPath'
26
- import { OwnerReference } from '../../../resources'
27
- import { AcmAlert , AcmLoadingPage , AcmTable , compareStrings } from '../../../ui-components'
26
+ import { ConfigMap , OwnerReference } from '../../../resources'
27
+ import { useRecoilValue , useSharedAtoms } from '../../../shared-recoil'
28
+ import { AcmAlert , AcmButton , AcmLoadingPage , AcmTable , compareStrings } from '../../../ui-components'
29
+ import { useAllClusters } from '../../Infrastructure/Clusters/ManagedClusters/components/useAllClusters'
28
30
import { useSearchDetailsContext } from './DetailsPage'
29
31
30
32
export function ResourceSearchLink ( props : {
@@ -216,6 +218,11 @@ export default function DetailsOverviewPage() {
216
218
const { cluster, resource, resourceLoading, resourceError, name } = useSearchDetailsContext ( )
217
219
const { t } = useTranslation ( )
218
220
const navigate = useNavigate ( )
221
+ const allClusters = useAllClusters ( true )
222
+ const { useIsObservabilityInstalled, configMapsState, clusterManagementAddonsState } = useSharedAtoms ( )
223
+ const configMaps = useRecoilValue ( configMapsState )
224
+ const clusterManagementAddons = useRecoilValue ( clusterManagementAddonsState )
225
+ const isObservabilityInstalled = useIsObservabilityInstalled ( )
219
226
const [ canEditResource , setCanEditResource ] = useState < boolean > ( false )
220
227
221
228
const { labelsLineNumber, annotationsLineNumber, tolerationsLineNumber } = useMemo ( ( ) => {
@@ -319,6 +326,35 @@ export default function DetailsOverviewPage() {
319
326
}
320
327
} , [ cluster , resource , navigate ] )
321
328
329
+ const vmCNVLink = useMemo ( ( ) => {
330
+ const clusterURL = allClusters . filter ( ( c ) => c . name === cluster ) ?. [ 0 ] ?. consoleURL
331
+ if ( resource ) {
332
+ return `${ clusterURL } /k8s/ns/${ resource . metadata ?. namespace } /kubevirt.io~v1~VirtualMachine/${ name } `
333
+ }
334
+ return ''
335
+ } , [ allClusters , cluster , resource , name ] )
336
+
337
+ const vmMetricLink = useMemo ( ( ) => {
338
+ const obsCont = clusterManagementAddons . filter ( ( cma ) => cma . metadata . name === 'observability-controller' )
339
+ let grafanaLink = obsCont ?. [ 0 ] ?. metadata ?. annotations ?. [ 'console.open-cluster-management.io/launch-link' ]
340
+ if ( grafanaLink ) {
341
+ grafanaLink = new URL ( grafanaLink ) . origin
342
+ }
343
+ if ( isObservabilityInstalled && resource ) {
344
+ const vmDashboard = configMaps . filter (
345
+ ( cm : ConfigMap ) => cm . metadata . name === 'grafana-dashboard-acm-openshift-virtualization-single-vm-view'
346
+ )
347
+ if ( vmDashboard . length > 0 ) {
348
+ const parsedDashboardData = JSON . parse (
349
+ vmDashboard [ 0 ] . data ?. [ 'acm-openshift-virtualization-single-vm-view.json' ]
350
+ )
351
+ const dashboardId = parsedDashboardData ?. uid
352
+ return `${ grafanaLink } /d/${ dashboardId } /executive-dashboards-single-virtual-machine-view?orgId=1&var-name=${ name } &var-namespace=${ resource ?. metadata ?. namespace } &var-cluster=${ cluster } `
353
+ }
354
+ }
355
+ return ''
356
+ } , [ cluster , clusterManagementAddons , configMaps , name , resource , isObservabilityInstalled ] )
357
+
322
358
if ( resourceError ) {
323
359
return (
324
360
< PageSection >
@@ -496,6 +532,61 @@ export default function DetailsOverviewPage() {
496
532
/>
497
533
</ DescriptionListDescription >
498
534
</ DescriptionListGroup >
535
+
536
+ { resource . kind . toLowerCase ( ) === 'virtualmachine' && (
537
+ < >
538
+ < DescriptionListGroup >
539
+ < DescriptionListTerm > { t ( 'Details' ) } </ DescriptionListTerm >
540
+ < DescriptionListDescription >
541
+ < AcmButton
542
+ variant = "link"
543
+ component = "a"
544
+ target = "_blank"
545
+ isInline = { true }
546
+ href = { vmCNVLink }
547
+ icon = { < ExternalLinkAltIcon /> }
548
+ iconPosition = "right"
549
+ >
550
+ { t ( 'Launch' ) }
551
+ </ AcmButton >
552
+ </ DescriptionListDescription >
553
+ </ DescriptionListGroup >
554
+ < DescriptionListGroup >
555
+ < DescriptionListTerm > { t ( 'Web console' ) } </ DescriptionListTerm >
556
+ < DescriptionListDescription >
557
+ < AcmButton
558
+ variant = "link"
559
+ component = "a"
560
+ target = "_blank"
561
+ isInline = { true }
562
+ href = { `${ vmCNVLink } /console` }
563
+ icon = { < ExternalLinkAltIcon /> }
564
+ iconPosition = "right"
565
+ >
566
+ { t ( 'Launch' ) }
567
+ </ AcmButton >
568
+ </ DescriptionListDescription >
569
+ </ DescriptionListGroup >
570
+ { isObservabilityInstalled && (
571
+ < DescriptionListGroup >
572
+ < DescriptionListTerm > { t ( 'Metrics' ) } </ DescriptionListTerm >
573
+ < DescriptionListDescription >
574
+ < AcmButton
575
+ variant = "link"
576
+ component = "a"
577
+ target = "_blank"
578
+ isInline = { true }
579
+ href = { vmMetricLink }
580
+ icon = { < ExternalLinkAltIcon /> }
581
+ iconPosition = "right"
582
+ >
583
+ { t ( 'Launch' ) }
584
+ </ AcmButton >
585
+ </ DescriptionListDescription >
586
+ </ DescriptionListGroup >
587
+ ) }
588
+ </ >
589
+ ) }
499
590
</ DescriptionList >
500
591
</ Stack >
501
592
</ PageSection >
0 commit comments