13327: Tests should be mostly passing
[arvados.git] / services / workbench2 / src / views / process-panel / process-resource-card.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from 'react';
6 import { CustomStyleRulesCallback } from 'common/custom-theme';
7 import { Card, CardHeader, IconButton, CardContent, Tooltip, Typography, Grid, Link } from '@mui/material';
8 import { WithStyles } from '@mui/styles';
9 import withStyles from '@mui/styles/withStyles';
10 import { ArvadosTheme } from 'common/custom-theme';
11 import {
12     CloseIcon,
13     MaximizeIcon,
14     ResourceIcon,
15     UnMaximizeIcon,
16     ShowChartIcon,
17 } from 'components/icon/icon';
18 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
19 import { connect } from 'react-redux';
20 import { Process } from 'store/processes/process';
21 import { NodeInstanceType } from 'store/process-panel/process-panel';
22 import { DetailsAttribute } from "components/details-attribute/details-attribute";
23 import { formatFileSize } from "common/formatters";
24 import { MountKind } from 'models/mount-types';
25
26 interface ProcessResourceCardDataProps {
27     process: Process;
28     nodeInfo: NodeInstanceType | null;
29     usageReport: string | null;
30 }
31
32 type CssRules = "card" | "header" | "title" | "avatar" | "iconHeader" | "content" | "sectionH3" | "reportButton";
33
34 const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
35     card: {
36         height: '100%'
37     },
38     header: {
39         paddingBottom: "0px"
40     },
41     title: {
42         paddingTop: theme.spacing(0.5),
43     },
44     avatar: {
45         paddingTop: theme.spacing(0.5),
46     },
47     iconHeader: {
48         fontSize: '1.875rem',
49         color: theme.customs.colors.greyL,
50     },
51     content: {
52         paddingTop: "0px",
53         maxHeight: `calc(100% - ${theme.spacing(7.5)})`,
54         overflow: "auto"
55     },
56     sectionH3: {
57         margin: "0.5em",
58         color: theme.customs.colors.greyD,
59         fontSize: "0.8125rem",
60         textTransform: "uppercase",
61     },
62     reportButton: {
63     }
64 });
65
66 type ProcessResourceCardProps = ProcessResourceCardDataProps & WithStyles<CssRules> & MPVPanelProps;
67
68 export const ProcessResourceCard = withStyles(styles)(connect()(
69     ({ classes, nodeInfo, usageReport, doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName, process, }: ProcessResourceCardProps) => {
70         let diskRequest = 0;
71         if (process.container?.mounts) {
72             for (const mnt in process.container.mounts) {
73                 const mp = process.container.mounts[mnt];
74                 if (mp.kind === MountKind.TEMPORARY_DIRECTORY) {
75                     diskRequest += mp.capacity;
76                 }
77             }
78         }
79
80         return (
81             <Card className={classes.card} data-cy="process-resources-card">
82                 <CardHeader
83                     className={classes.header}
84                     classes={{
85                         content: classes.title,
86                         avatar: classes.avatar,
87                     }}
88                     avatar={<ResourceIcon className={classes.iconHeader} />}
89                     title={
90                         <Typography noWrap variant='h6' color='inherit'>
91                             Resources
92                         </Typography>
93                     }
94                     action={
95                         <div>
96                             {usageReport && <Link href={usageReport} className={classes.reportButton} target="_blank"><ShowChartIcon /> Resource usage report</Link>}
97                             {doUnMaximizePanel && panelMaximized &&
98                                 <Tooltip title={`Unmaximize ${panelName || 'panel'}`} disableFocusListener>
99                                     <IconButton onClick={doUnMaximizePanel} size="large"><UnMaximizeIcon /></IconButton>
100                                 </Tooltip>}
101                             {doMaximizePanel && !panelMaximized &&
102                                 <Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
103                                     <IconButton onClick={doMaximizePanel} size="large"><MaximizeIcon /></IconButton>
104                                 </Tooltip>}
105                             {doHidePanel &&
106                                 <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
107                                     <IconButton disabled={panelMaximized} onClick={doHidePanel} size="large"><CloseIcon /></IconButton>
108                                 </Tooltip>}
109                         </div>
110                     } />
111                 <CardContent className={classes.content}>
112                     <Grid container>
113                         <Grid item xs={4}>
114                             <h3 className={classes.sectionH3}>Requested Resources</h3>
115                             <Grid container>
116                                 <Grid item xs={12}>
117                                     <DetailsAttribute label="Cores" value={process.container?.runtimeConstraints.vcpus} />
118                                 </Grid>
119                                 <Grid item xs={12}>
120                                     <DetailsAttribute label="RAM*" value={formatFileSize(process.container?.runtimeConstraints.ram)} />
121                                 </Grid>
122                                 <Grid item xs={12}>
123                                     <DetailsAttribute label="Disk" value={formatFileSize(diskRequest)} />
124                                 </Grid>
125
126                                 {process.container?.runtimeConstraints.cuda &&
127                                     process.container?.runtimeConstraints.cuda.device_count > 0 ?
128                                     <>
129                                         <Grid item xs={12}>
130                                             <DetailsAttribute label="CUDA devices" value={process.container?.runtimeConstraints.cuda.device_count} />
131                                         </Grid>
132                                         <Grid item xs={12}>
133                                             <DetailsAttribute label="CUDA driver version" value={process.container?.runtimeConstraints.cuda.driver_version} />
134                                         </Grid>
135                                         <Grid item xs={12}>
136                                             <DetailsAttribute label="CUDA hardware capability" value={process.container?.runtimeConstraints.cuda.hardware_capability} />
137                                         </Grid>
138                                     </> : null}
139
140                                 {process.container?.runtimeConstraints.keep_cache_ram &&
141                                     process.container?.runtimeConstraints.keep_cache_ram > 0 ?
142                                     <Grid item xs={12}>
143                                         <DetailsAttribute label="Keep cache (RAM)" value={formatFileSize(process.container?.runtimeConstraints.keep_cache_ram)} />
144                                     </Grid> : null}
145
146                                 {process.container?.runtimeConstraints.keep_cache_disk &&
147                                     process.container?.runtimeConstraints.keep_cache_disk > 0 ?
148                                     <Grid item xs={12}>
149                                         <DetailsAttribute label="Keep cache (disk)" value={formatFileSize(process.container?.runtimeConstraints.keep_cache_disk)} />
150                                     </Grid> : null}
151
152                                 {process.container?.runtimeConstraints.API ? <Grid item xs={12}>
153                                     <DetailsAttribute label="API access" value={process.container?.runtimeConstraints.API.toString()} />
154                                 </Grid> : null}
155
156                             </Grid>
157                         </Grid>
158
159
160                         <Grid item xs={8}>
161                             <h3 className={classes.sectionH3}>Assigned Instance Type</h3>
162                             {nodeInfo === null ? <Grid item xs={8}>
163                                 No instance type recorded
164                             </Grid>
165                                 :
166                                 <Grid container>
167                                     <Grid item xs={6}>
168                                         <DetailsAttribute label="Cores" value={nodeInfo.VCPUs} />
169                                     </Grid>
170
171                                     <Grid item xs={6}>
172                                         <DetailsAttribute label="Provider type" value={nodeInfo.ProviderType} />
173                                     </Grid>
174
175                                     <Grid item xs={6}>
176                                         <DetailsAttribute label="RAM" value={formatFileSize(nodeInfo.RAM)} />
177                                     </Grid>
178
179                                     <Grid item xs={6}>
180                                         <DetailsAttribute label="Price" value={"$" + nodeInfo.Price.toString()} />
181                                     </Grid>
182
183                                     <Grid item xs={6}>
184                                         <DetailsAttribute label="Disk" value={formatFileSize(nodeInfo.IncludedScratch + nodeInfo.AddedScratch)} />
185                                     </Grid>
186
187                                     <Grid item xs={6}>
188                                         <DetailsAttribute label="Preemptible" value={nodeInfo.Preemptible.toString()} />
189                                     </Grid>
190
191                                     {nodeInfo.CUDA && nodeInfo.CUDA.DeviceCount > 0 &&
192                                         <>
193                                             <Grid item xs={6}>
194                                                 <DetailsAttribute label="CUDA devices" value={nodeInfo.CUDA.DeviceCount} />
195                                             </Grid>
196
197                                             <Grid item xs={6}>
198                                             </Grid>
199
200                                             <Grid item xs={6}>
201                                                 <DetailsAttribute label="CUDA driver version" value={nodeInfo.CUDA.DriverVersion} />
202                                             </Grid>
203
204                                             <Grid item xs={6}>
205                                             </Grid>
206
207                                             <Grid item xs={6}>
208                                                 <DetailsAttribute label="CUDA hardware capability" value={nodeInfo.CUDA.HardwareCapability} />
209                                             </Grid>
210                                         </>
211                                     }
212                                 </Grid>}
213                         </Grid>
214                     </Grid>
215                     <Typography>* RAM available to the program is limited to Requested RAM, not Instance RAM</Typography>
216                 </CardContent>
217             </Card >
218         );
219     }
220 ));