21700: Install Bundler system-wide in Rails postinst
[arvados.git] / services / workbench2 / src / views / instance-types-panel / instance-types-panel.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 { StyleRulesCallback, WithStyles, withStyles, Card, CardContent, Typography, Grid } from '@material-ui/core';
7 import { ArvadosTheme } from 'common/custom-theme';
8 import { ResourceIcon } from 'components/icon/icon';
9 import { RootState } from 'store/store';
10 import { connect } from 'react-redux';
11 import { ClusterConfigJSON } from 'common/config';
12 import { NotFoundView } from 'views/not-found-panel/not-found-panel';
13 import { formatCWLResourceSize, formatCost, formatFileSize } from 'common/formatters';
14 import { DetailsAttribute } from 'components/details-attribute/details-attribute';
15 import { DefaultCodeSnippet } from 'components/default-code-snippet/default-code-snippet';
16
17 type CssRules = 'root' | 'infoBox' | 'instanceType';
18
19 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
20     root: {
21        width: "calc(100% + 20px)",
22        margin: "0 -10px",
23        overflow: 'auto'
24     },
25     infoBox: {
26         padding: "0 10px 10px",
27     },
28     instanceType: {
29         padding: "10px",
30     },
31 });
32
33 type InstanceTypesPanelConnectedProps = {config: ClusterConfigJSON};
34
35 type InstanceTypesPanelRootProps = InstanceTypesPanelConnectedProps & WithStyles<CssRules>;
36
37 const mapStateToProps = ({auth}: RootState): InstanceTypesPanelConnectedProps => ({
38     config: auth.config.clusterConfig,
39 });
40
41 export const InstanceTypesPanel = withStyles(styles)(connect(mapStateToProps)(
42     ({ config, classes }: InstanceTypesPanelRootProps) => {
43
44         const instances = config.InstanceTypes || {};
45
46         return <Grid className={classes.root} container direction="row">
47             <Grid className={classes.infoBox} item xs={12}>
48                 <Card>
49                     <CardContent>
50                         <Typography variant="body2">
51                             These are the cloud compute instance types
52                             configured for this cluster. The core count and
53                             maximum RAM request correspond to the greatest
54                             values you can put in the CWL Workflow
55                             ResourceRequest{" "}
56                             <DefaultCodeSnippet
57                                 inline
58                                 lines={["minCores"]}
59                             />{" "}
60                             and{" "}
61                             <DefaultCodeSnippet inline lines={["minRAM"]} />{" "}
62                             and still be scheduled on that instance type.
63                         </Typography>
64                     </CardContent>
65                 </Card>
66             </Grid>
67             {Object.keys(instances).length > 0 ?
68                 Object.keys(instances)
69                     .sort((a, b) => {
70                         const typeA = instances[a];
71                         const typeB = instances[b];
72
73                         if (typeA.Price !== typeB.Price) {
74                             return typeA.Price - typeB.Price;
75                         } else {
76                             return typeA.ProviderType.localeCompare(typeB.ProviderType);
77                         }
78                     }).map((instanceKey) => {
79                         const instanceType = instances[instanceKey];
80                         const maxDiskRequest = instanceType.IncludedScratch;
81                         const keepBufferOverhead = calculateKeepBufferOverhead(instanceType.VCPUs);
82                         const maxRamRequest = discountRamByPercent(instanceType.RAM - config.Containers.ReserveExtraRAM - keepBufferOverhead);
83
84                         return <Grid data-cy={instanceKey} className={classes.instanceType} item sm={6} xs={12} key={instanceKey}>
85                             <Card>
86                                 <CardContent>
87                                     <Typography variant="h6">
88                                         {instanceKey}
89                                     </Typography>
90                                     <Grid item xs={12}>
91                                         <DetailsAttribute label="Provider type" value={instanceType.ProviderType} />
92                                     </Grid>
93                                     <Grid item xs={12}>
94                                         <DetailsAttribute label="Price" value={formatCost(instanceType.Price)} />
95                                     </Grid>
96                                     <Grid item xs={12}>
97                                         <DetailsAttribute label="Cores" value={instanceType.VCPUs} />
98                                     </Grid>
99                                     <Grid item xs={12}>
100                                         <DetailsAttribute label="Max RAM request" value={`${formatCWLResourceSize(maxRamRequest)} (${formatFileSize(maxRamRequest)})`} />
101                                     </Grid>
102                                     <Grid item xs={12}>
103                                         <DetailsAttribute label="Max disk request" value={`${formatCWLResourceSize(maxDiskRequest)} (${formatFileSize(maxDiskRequest)})`} />
104                                     </Grid>
105                                     <Grid item xs={12}>
106                                         <DetailsAttribute label="Preemptible" value={instanceType.Preemptible.toString()} />
107                                     </Grid>
108                                     {instanceType.CUDA && instanceType.CUDA.DeviceCount > 0 ?
109                                         <>
110                                             <Grid item xs={12}>
111                                                 <DetailsAttribute label="CUDA GPUs" value={instanceType.CUDA.DeviceCount} />
112                                             </Grid>
113                                             <Grid item xs={12}>
114                                                 <DetailsAttribute label="Hardware capability" value={instanceType.CUDA.HardwareCapability} />
115                                             </Grid>
116                                             <Grid item xs={12}>
117                                                 <DetailsAttribute label="Driver version" value={instanceType.CUDA.DriverVersion} />
118                                             </Grid>
119                                         </> : <></>
120                                     }
121                                 </CardContent>
122                             </Card>
123                         </Grid>;
124                     }) :
125                 <NotFoundView
126                     icon={ResourceIcon}
127                     messages={["No instances found"]}
128                 />
129             }
130         </Grid>;
131     }
132 ));
133
134 export const calculateKeepBufferOverhead = (coreCount: number): number => {
135     // TODO replace with exported server config
136     const buffersPerVCPU = 1;
137
138     // Returns 220 MiB + 64MiB+10% per buffer
139     return (220 << 20) + (buffersPerVCPU * coreCount * (1 << 26) * (11/10))
140 };
141
142 export const discountRamByPercent = (requestedRamBytes: number): number => {
143     // TODO replace this with exported server config or remove when no longer
144     // used by server in ram calculation
145     const discountPercent = 5;
146
147     return requestedRamBytes * 100 / (100-discountPercent);
148 };