From ed4389e99b60c9e2316312829ed5f105dd1cf3b7 Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Mon, 19 Dec 2022 13:45:56 -0500 Subject: [PATCH] 19438: Finished adding fields Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- src/common/formatters.ts | 176 +++++++++--------- src/models/runtime-constraints.ts | 8 + .../process-panel/process-panel-actions.ts | 3 +- src/store/process-panel/process-panel.ts | 20 +- .../process-panel/process-resource-card.tsx | 153 ++++++++++++--- 5 files changed, 235 insertions(+), 125 deletions(-) diff --git a/src/common/formatters.ts b/src/common/formatters.ts index 3cacc6c83b..a38609a678 100644 --- a/src/common/formatters.ts +++ b/src/common/formatters.ts @@ -4,128 +4,128 @@ import { PropertyValue } from 'models/search-bar'; import { - Vocabulary, - getTagKeyLabel, - getTagValueLabel, + Vocabulary, + getTagKeyLabel, + getTagValueLabel, } from 'models/vocabulary'; export const formatDate = (isoDate?: string | null, utc: boolean = false) => { - if (isoDate) { - const date = new Date(isoDate); - let text: string; - if (utc) { - text = date.toUTCString(); - } else { - text = date.toLocaleString(); + if (isoDate) { + const date = new Date(isoDate); + let text: string; + if (utc) { + text = date.toUTCString(); + } else { + text = date.toLocaleString(); + } + return text === 'Invalid Date' ? '(none)' : text; } - return text === 'Invalid Date' ? '(none)' : text; - } - return '-'; + return '-'; }; export const formatFileSize = (size?: number | string) => { - if (typeof size === 'number') { - if (size === 0) { - return '0 B'; - } + if (typeof size === 'number') { + if (size === 0) { + return '0 B'; + } - for (const { base, unit } of FILE_SIZES) { - if (size >= base) { - return `${(size / base).toFixed()} ${unit}`; - } + for (const { base, unit } of FILE_SIZES) { + if (size >= base) { + return `${(size / base).toFixed(base === 1 ? 0 : 1)} ${unit}`; + } + } } - } - if ((typeof size === 'string' && size === '') || size === undefined) { - return '-'; - } - return '0 B'; + if ((typeof size === 'string' && size === '') || size === undefined) { + return '-'; + } + return '0 B'; }; export const formatTime = (time: number, seconds?: boolean) => { - const minutes = Math.floor((time / (1000 * 60)) % 60).toFixed(0); - const hours = Math.floor(time / (1000 * 60 * 60)).toFixed(0); + const minutes = Math.floor((time / (1000 * 60)) % 60).toFixed(0); + const hours = Math.floor(time / (1000 * 60 * 60)).toFixed(0); - if (seconds) { - const seconds = Math.floor((time / 1000) % 60).toFixed(0); - return hours + 'h ' + minutes + 'm ' + seconds + 's'; - } + if (seconds) { + const seconds = Math.floor((time / 1000) % 60).toFixed(0); + return hours + 'h ' + minutes + 'm ' + seconds + 's'; + } - return hours + 'h ' + minutes + 'm'; + return hours + 'h ' + minutes + 'm'; }; export const getTimeDiff = (endTime: string, startTime: string) => { - return new Date(endTime).getTime() - new Date(startTime).getTime(); + return new Date(endTime).getTime() - new Date(startTime).getTime(); }; export const formatProgress = (loaded: number, total: number) => { - const progress = loaded >= 0 && total > 0 ? (loaded * 100) / total : 0; - return `${progress.toFixed(2)}%`; + const progress = loaded >= 0 && total > 0 ? (loaded * 100) / total : 0; + return `${progress.toFixed(2)}%`; }; export function formatUploadSpeed( - prevLoaded: number, - loaded: number, - prevTime: number, - currentTime: number + prevLoaded: number, + loaded: number, + prevTime: number, + currentTime: number ) { - const speed = - loaded > prevLoaded && currentTime > prevTime - ? (loaded - prevLoaded) / (currentTime - prevTime) - : 0; + const speed = + loaded > prevLoaded && currentTime > prevTime + ? (loaded - prevLoaded) / (currentTime - prevTime) + : 0; - return `${(speed / 1000).toFixed(2)} MB/s`; + return `${(speed / 1000).toFixed(2)} MB/s`; } const FILE_SIZES = [ - { - base: 1099511627776, - unit: 'TB', - }, - { - base: 1073741824, - unit: 'GB', - }, - { - base: 1048576, - unit: 'MB', - }, - { - base: 1024, - unit: 'KB', - }, - { - base: 1, - unit: 'B', - }, + { + base: 1099511627776, + unit: 'TiB', + }, + { + base: 1073741824, + unit: 'GiB', + }, + { + base: 1048576, + unit: 'MiB', + }, + { + base: 1024, + unit: 'KiB', + }, + { + base: 1, + unit: 'B', + }, ]; export const formatPropertyValue = ( - pv: PropertyValue, - vocabulary?: Vocabulary + pv: PropertyValue, + vocabulary?: Vocabulary ) => { - if (vocabulary && pv.keyID && pv.valueID) { - return `${getTagKeyLabel(pv.keyID, vocabulary)}: ${getTagValueLabel( - pv.keyID, - pv.valueID!, - vocabulary - )}`; - } - if (pv.key) { - return pv.value ? `${pv.key}: ${pv.value}` : pv.key; - } - return ''; + if (vocabulary && pv.keyID && pv.valueID) { + return `${getTagKeyLabel(pv.keyID, vocabulary)}: ${getTagValueLabel( + pv.keyID, + pv.valueID!, + vocabulary + )}`; + } + if (pv.key) { + return pv.value ? `${pv.key}: ${pv.value}` : pv.key; + } + return ''; }; export const formatContainerCost = (cost: number): string => { - const decimalPlaces = 3; + const decimalPlaces = 3; - const factor = Math.pow(10, decimalPlaces); - const rounded = Math.round(cost * factor) / factor; - if (cost > 0 && rounded === 0) { - // Display min value of 0.001 - return `$${1 / factor}`; - } else { - // Otherwise use rounded value to proper decimal places - return `$${rounded}`; - } + const factor = Math.pow(10, decimalPlaces); + const rounded = Math.round(cost * factor) / factor; + if (cost > 0 && rounded === 0) { + // Display min value of 0.001 + return `$${1 / factor}`; + } else { + // Otherwise use rounded value to proper decimal places + return `$${rounded}`; + } }; diff --git a/src/models/runtime-constraints.ts b/src/models/runtime-constraints.ts index 89101c6ea3..63982529bd 100644 --- a/src/models/runtime-constraints.ts +++ b/src/models/runtime-constraints.ts @@ -2,9 +2,17 @@ // // SPDX-License-Identifier: AGPL-3.0 +export interface CUDAParameters { + device_count: number; + driver_version: string; + hardware_capability: string; +} + export interface RuntimeConstraints { ram: number; vcpus: number; keep_cache_ram?: number; + keep_cache_disk?: number; API: boolean; + cuda?: CUDAParameters; } diff --git a/src/store/process-panel/process-panel-actions.ts b/src/store/process-panel/process-panel-actions.ts index 71cb83a151..9b1e984706 100644 --- a/src/store/process-panel/process-panel-actions.ts +++ b/src/store/process-panel/process-panel-actions.ts @@ -114,7 +114,6 @@ export const loadNodeJson = (containerRequest: ContainerRequestResource) => return; }; try { - const propsOutputs = getRawOutputs(containerRequest); const filesPromise = services.collectionService.files(containerRequest.logUuid); const collectionPromise = services.collectionService.get(containerRequest.logUuid); const [files, collection] = await Promise.all([filesPromise, collectionPromise]); @@ -124,7 +123,7 @@ export const loadNodeJson = (containerRequest: ContainerRequestResource) => let nodeData = nodeFile ? await services.collectionService.getFileContents(nodeFile) : undefined; if (nodeData && (nodeData = JSON.parse(nodeData))) { dispatch(processPanelActions.SET_NODE_INFO({ - nodeInfo: CommonService.mapKeys(camelCase)(nodeData) as NodeInstanceType + nodeInfo: nodeData as NodeInstanceType })); } else { dispatch(processPanelActions.SET_NODE_INFO(noLog)); diff --git a/src/store/process-panel/process-panel.ts b/src/store/process-panel/process-panel.ts index 34abd614bd..1ec60ff54c 100644 --- a/src/store/process-panel/process-panel.ts +++ b/src/store/process-panel/process-panel.ts @@ -14,21 +14,21 @@ export type OutputDetails = { } export interface CUDAFeatures { - driverVersion: string; - hardwareCapability: string; - deviceCount: number; + DriverVersion: string; + HardwareCapability: string; + DeviceCount: number; } export interface NodeInstanceType { - name: string; - providerType: string; + Name: string; + ProviderType: string; VCPUs: number; RAM: number; - scratch: number; - includedScratch: number; - addedScratch: number; - price: number; - preemptible: boolean; + Scratch: number; + IncludedScratch: number; + AddedScratch: number; + Price: number; + Preemptible: boolean; CUDA: CUDAFeatures; }; diff --git a/src/views/process-panel/process-resource-card.tsx b/src/views/process-panel/process-resource-card.tsx index 79e600e7e0..88b8155b05 100644 --- a/src/views/process-panel/process-resource-card.tsx +++ b/src/views/process-panel/process-resource-card.tsx @@ -28,21 +28,36 @@ import { connect } from 'react-redux'; import { Process } from 'store/processes/process'; import { NodeInstanceType } from 'store/process-panel/process-panel'; import { DefaultView } from 'components/default-view/default-view'; +import { DetailsAttribute } from "components/details-attribute/details-attribute"; +import { formatFileSize } from "common/formatters"; +import { InputCollectionMount } from 'store/processes/processes-actions'; +import { MountKind, TemporaryDirectoryMount } from 'models/mount-types'; interface ProcessResourceCardDataProps { process: Process; nodeInfo: NodeInstanceType | null; } -type CssRules = "card" | "header" | "title" | "avatar" | "iconHeader" | "content"; +type CssRules = "card" | "header" | "title" | "avatar" | "iconHeader" | "content" | "sectionH3"; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ - card: {}, - header: {}, + card: { + height: '100%' + }, + header: { + paddingBottom: "0px" + }, title: {}, avatar: {}, iconHeader: {}, - content: {} + content: { + paddingTop: "0px", + maxHeight: `calc(100% - ${theme.spacing.unit * 4.5}px)`, + overflow: "auto" + }, + sectionH3: { + margin: "0.5em" + } }); type ProcessResourceCardProps = ProcessResourceCardDataProps & WithStyles & MPVPanelProps; @@ -50,7 +65,17 @@ type ProcessResourceCardProps = ProcessResourceCardDataProps & WithStyles { - const loading = nodeInfo === null; + const loading = false; + + let diskRequest = 0; + if (process.container?.mounts) { + for (const mnt in process.container.mounts) { + const mp = process.container.mounts[mnt]; + if (mp.kind === MountKind.TEMPORARY_DIRECTORY) { + diskRequest += mp.capacity; + } + } + } return } /> - <> - {/* raw is undefined until params are loaded */} - {loading && - - } - {/* Once loaded, either raw or params may still be empty - * Raw when all params are empty - * Params when raw is provided by containerRequest properties but workflow mount is absent for preview - */} - {!loading && - <> -
- stuff -
- } - {!loading && - - } - + + +

Requested resources

+ + + + + + + + + + + + + + + {process.container?.runtimeConstraints.keep_cache_ram && + process.container?.runtimeConstraints.keep_cache_ram > 0 ? + + + : null} + + {process.container?.runtimeConstraints.keep_cache_disk && + process.container?.runtimeConstraints.keep_cache_disk > 0 ? + + + : null} + + {process.container?.runtimeConstraints.cuda && + process.container?.runtimeConstraints.cuda.device_count > 0 ? + <> + + + + + + + + + + : null} + +
+ + + +

Assigned instance type

+ {nodeInfo === null ? + No instance type recorded + + : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {nodeInfo.CUDA.DeviceCount > 0 && + <> + + + + + + + + + + + } + } +
+
-
; + ; } )); -- 2.30.2