Skip to content

Commit

Permalink
feat: add gpu widget
Browse files Browse the repository at this point in the history
closes #43
  • Loading branch information
MauriceNino committed Jun 17, 2022
1 parent 540046f commit 0328463
Show file tree
Hide file tree
Showing 14 changed files with 345 additions and 15 deletions.
41 changes: 41 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,44 @@
## [3.5.2](https://github.com/MauriceNino/dashdot/compare/v3.5.1...v3.5.2) (2022-06-16)


### Bug Fixes

* **view:** change base 1024 size measurements to unambigous labels ([148f8db](https://github.com/MauriceNino/dashdot/commit/148f8dbb5decf81212cec6e938226f14dc79e565)), closes [#119](https://github.com/MauriceNino/dashdot/issues/119)
* **view:** single drive in raid mode not shown in one page ([ac01eb3](https://github.com/MauriceNino/dashdot/commit/ac01eb3ff592ba7e82e7b820e3cd8eab896e1dcb))

## [3.5.1](https://github.com/MauriceNino/dashdot/compare/v3.5.0...v3.5.1) (2022-06-15)


### Bug Fixes

* **view:** mobile view cutting off content ([3dd19bb](https://github.com/MauriceNino/dashdot/commit/3dd19bb33d662f02067b10ab100df94272ab9233))

# [3.5.0](https://github.com/MauriceNino/dashdot/compare/v3.4.0...v3.5.0) (2022-06-15)


### Bug Fixes

* **view:** add max-height to widgets ([8e9ccfb](https://github.com/MauriceNino/dashdot/commit/8e9ccfb644a5592e5e7b951cc056a2bce7187599))


### Features

* **view:** add pagination for servers with too many drives ([bb6fbfb](https://github.com/MauriceNino/dashdot/commit/bb6fbfb8f9262d61b4a4fecdf1a48856f5d55e79))

# [3.4.0](https://github.com/MauriceNino/dashdot/compare/v3.3.3...v3.4.0) (2022-06-15)


### Bug Fixes

* **api:** error on multiple default network interfaces ([3cf8774](https://github.com/MauriceNino/dashdot/commit/3cf877458cc938e0ae1a949935afee06b52f6184)), closes [#118](https://github.com/MauriceNino/dashdot/issues/118)


### Features

* **api, view:** add raid information to storage widget ([ba84d34](https://github.com/MauriceNino/dashdot/commit/ba84d34d2c403c7b1f8cfcb30de78593da58bf57)), closes [#40](https://github.com/MauriceNino/dashdot/issues/40)
* **api:** add option to select the used network interface ([8b6a78d](https://github.com/MauriceNino/dashdot/commit/8b6a78d6a9f18dfb4dbfdf73d797b94bb6b33b68)), closes [#117](https://github.com/MauriceNino/dashdot/issues/117)
* **view:** add option to show in imperial units ([d4b1f69](https://github.com/MauriceNino/dashdot/commit/d4b1f69a36e02c8e5b762cabb7b63225e9be3f10))

## [3.3.3](https://github.com/MauriceNino/dashdot/compare/v3.3.2...v3.3.3) (2022-06-13)


Expand Down
31 changes: 30 additions & 1 deletion .github/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ in the [INSTALL.md](./INSTALL.md).
| Variable | Description | Type | Default Value |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ---------------------------- |
| `DASHDOT_PORT` | The port where the express backend is running (the backend serves the frontend, so it is the same port for both) | number | `3001` |
| `DASHDOT_WIDGET_LIST` | Change the order of the elements in the list, to change the position on the page, or remove an item from the list, to remove it from the page (The available options are: `os`, `cpu`, `storage`, `ram`, `network`) | string | `os,cpu,storage,ram,network` |
| `DASHDOT_WIDGET_LIST` | Change the order of the elements in the list, to change the position on the page, or remove an item from the list, to remove it from the page (The available options are: `os`, `cpu`, `storage`, `ram`, `network`, `gpu`) | string | `os,cpu,storage,ram,network` |
| `DASHDOT_ACCEPT_OOKLA_EULA` | Use the newer and more accurate `speedtest` tool from Ookla, instead of the old `speedtest-cli` for your speedtests. When passing this flag, you accept Ooklas [EULA](https://www.speedtest.net/about/eula), [TERMS](https://www.speedtest.net/about/terms) and [PRIVACY](https://www.speedtest.net/about/privacy) | boolean | `false` |
| `DASHDOT_USE_IMPERIAL` | Shows any units in the imperial system, instead of the default metric | boolean | `false` |

Expand Down Expand Up @@ -89,6 +89,32 @@ in the [INSTALL.md](./INSTALL.md).

<!-- markdownlint-enable -->

## GPU Widget

To use the GPU widget, make sure to include it in the `DASHDOT_WIDGET_LIST`. One
limitation with the GPU widget is that it will **only run out of the box with from
source installations**. Docker images do not include the necessary tools (mainly,
because I don't want to bloat the image for everyone). If you absolutely need the
GPU widget running inside a docker container, you will need to create your own image.

These links might help you with that:

- <https://www.howtogeek.com/devops/how-to-use-an-nvidia-gpu-with-docker-containers/>
- <https://docs.docker.com/compose/gpu-support/>
- <https://towardsdatascience.com/how-to-properly-use-the-gpu-within-a-docker-container-4c699c78c6d1>
- <https://stackoverflow.com/q/25185405/9150652>
- <https://stackoverflow.com/q/63751883/9150652>

<!-- markdownlint-disable -->

| Variable | Description | Type | Default Value |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------------------- |
| `DASHDOT_GPU_LABEL_LIST` | Change the order of the labels in the list, to change the position in the widget, or remove an item from the list, to remove it from the widget (The available options are: `brand`, `model`, `memory`) | string | `brand, model, memory` |
| `DASHDOT_GPU_WIDGET_GROW` | To adjust the relative size of the GPU widget | number | `6` |
| `DASHDOT_GPU_WIDGET_MIN_WIDTH` | To adjust the minimum width of the GPU widget (in px) | number | `700` |
| `DASHDOT_GPU_DATAPOINTS` | The amount of datapoints in the GPU graph | number | `20` |
| `DASHDOT_GPU_POLL_INTERVAL` | Read the GPU load every x milliseconds | number | `1000` |

## Overrides

Override specific fields, by providing your desired value with the following options.
Expand Down Expand Up @@ -116,5 +142,8 @@ Override specific fields, by providing your desired value with the following opt
| `DASHDOT_OVERRIDE_STORAGE_BRANDS` | Pass a comma-separated list of brands of your drives. You can skip correct drives, by passing empty values for it (e.g. `Samsung,,WD` would result in `Samsung` for Drive 1 and `WD` for Drive 3) | string |
| `DASHDOT_OVERRIDE_STORAGE_SIZES` | Pass a comma-separated list of sizes of your drives. You can skip correct drives, by passing empty values for it (e.g. `123,,321` would result in `123` for Drive 1 and `321` for Drive 3). Number needs to be passed in bytes (e.g. `34359738368` for 32 GB, because it is `32 * 1024 * 1024 * 1024`) | string |
| `DASHDOT_OVERRIDE_STORAGE_TYPES` | Pass a comma-separated list of types of your drives. You can skip correct drives, by passing empty values for it (e.g. `SSD,,HDD` would result in `SSD` for Drive 1 and `HDD` for Drive 3) | string |
| `DASHDOT_OVERRIDE_GPU_BRANDS` | Pass a comma-separated list of brands of your GPUs. You can skip correct GPUs, by passing empty values for it (e.g. `Intel,,Nvidia` would result in `Intel` for GPU 1 and `Nvidia` for GPU 3) | string |
| `DASHDOT_OVERRIDE_GPU_MODELS` | Pass a comma-separated list of models of your GPUs. You can skip correct GPUs, by passing empty values for it (e.g. `CometLake-H GT2,,GeForce GTX 1650 Ti` would result in `CometLake-H GT2` for GPU 1 and `GeForce GTX 1650 Ti` for GPU 3) | string |
| `DASHDOT_OVERRIDE_GPU_MEMORIES` | Pass a comma-separated list of memory-sizes of your GPUs. You can skip correct GPUs, by passing empty values for it (e.g. `4096,,256` would result in `4 GiB` for GPU 1 and `256 MiB` for GPU 3) | string |

<!-- markdownlint-enable -->
9 changes: 9 additions & 0 deletions apps/api/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ export const CONFIG: Config = {
network_shown_datapoints: numNull(penv('NETWORK_SHOWN_DATAPOINTS')) ?? 20,
network_poll_interval: numNull(penv('NETWORK_POLL_INTERVAL')) ?? 1000,

gpu_label_list: lst(penv('GPU_LABEL_LIST') ?? 'brand,model,memory') as any[],
gpu_widget_grow: numNull(penv('GPU_WIDGET_GROW')) ?? 6,
gpu_widget_min_width: numNull(penv('GPU_WIDGET_MIN_WIDTH')) ?? 700,
gpu_shown_datapoints: numNull(penv('GPU_SHOWN_DATAPOINTS')) ?? 20,
gpu_poll_interval: numNull(penv('GPU_POLL_INTERVAL')) ?? 1000,

override: {
os: penv('OVERRIDE_OS'),
arch: penv('OVERRIDE_ARCH'),
Expand All @@ -78,5 +84,8 @@ export const CONFIG: Config = {
storage_brands: lst(penv('OVERRIDE_STORAGE_BRANDS') ?? ''),
storage_sizes: numlst(penv('OVERRIDE_STORAGE_SIZES') ?? ''),
storage_types: lst(penv('OVERRIDE_STORAGE_TYPES') ?? ''),
gpu_brands: lst(penv('OVERRIDE_GPU_BRANDS') ?? ''),
gpu_models: lst(penv('OVERRIDE_GPU_MODELS') ?? ''),
gpu_memories: numlst(penv('OVERRIDE_GPU_MEMORIES') ?? ''),
},
};
25 changes: 24 additions & 1 deletion apps/api/src/dynamic-info.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { CpuLoad, NetworkLoad, RamLoad, StorageLoad } from '@dash/common';
import {
CpuLoad,
GpuLoad,
NetworkLoad,
RamLoad,
StorageLoad,
} from '@dash/common';
import { exec as cexec } from 'child_process';
import { interval, mergeMap, Observable, ReplaySubject } from 'rxjs';
import * as si from 'systeminformation';
Expand Down Expand Up @@ -132,6 +138,22 @@ export const getDynamicServerInfo = () => {
}
);

const gpuObs = createBufferedInterval(
'GPU',
CONFIG.gpu_shown_datapoints,
CONFIG.gpu_poll_interval,
async (): Promise<GpuLoad> => {
const info = await si.graphics();

return {
layout: info.controllers.map(controller => ({
load: controller.utilizationGpu ?? 0,
memory: controller.utilizationMemory ?? 0,
})),
};
}
);

const speedTestObs = interval(CONFIG.speed_test_interval * 60 * 1000).pipe(
mergeMap(async () => await runSpeedTest())
);
Expand All @@ -141,6 +163,7 @@ export const getDynamicServerInfo = () => {
ram: ramObs,
storage: storageObs,
network: networkObs,
gpu: gpuObs,
speedTest: speedTestObs,
};
};
5 changes: 5 additions & 0 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,16 @@ server.listen(CONFIG.port, async () => {
socket.emit('network-load', network);
});

const gpuSub = obs.gpu.subscribe(async gpu => {
socket.emit('gpu-load', gpu);
});

socket.on('disconnect', () => {
cpuSub.unsubscribe();
ramSub.unsubscribe();
storageSub.unsubscribe();
networkSub.unsubscribe();
gpuSub.unsubscribe();
});
});

Expand Down
30 changes: 30 additions & 0 deletions apps/api/src/static-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ const normalizeCpuModel = (cpuModel: string) => {
.trim();
};

const normalizeGpuBrand = (brand: string) => {
return brand ? brand.replace(/(corporation)/gi, '').trim() : undefined;
};

const normalizeGpuName = (name: string) => {
return name ? name.replace(/(nvidia|amd|intel)/gi, '').trim() : undefined;
};

const normalizeGpuModel = (model: string) => {
return model ? model.replace(/\[.*\]/gi, '').trim() : undefined;
};

const STATIC_INFO: HardwareInfo = {
os: {
arch: '',
Expand Down Expand Up @@ -46,6 +58,9 @@ const STATIC_INFO: HardwareInfo = {
type: '',
publicIp: '',
},
gpu: {
layout: [],
},
};

const loadOsInfo = async (): Promise<void> => {
Expand Down Expand Up @@ -182,6 +197,20 @@ const loadNetworkInfo = async (): Promise<void> => {
}
};

const loadGpuInfo = async (): Promise<void> => {
const info = await si.graphics();

STATIC_INFO.gpu = {
layout: info.controllers.map(controller => ({
brand: normalizeGpuBrand(controller.vendor),
model:
normalizeGpuName(controller.name) ??
normalizeGpuModel(controller.model),
memory: controller.memoryTotal ?? controller.vram ?? 0,
})),
};
};

const commandExists = async (command: string): Promise<boolean> => {
try {
const { stdout, stderr } = await exec(`which ${command}`);
Expand Down Expand Up @@ -242,6 +271,7 @@ export const loadStaticServerInfo = async (): Promise<void> => {
loadRamInfo(),
loadStorageInfo(),
loadNetworkInfo(),
loadGpuInfo(),
]);

console.log(
Expand Down
6 changes: 0 additions & 6 deletions apps/cli/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@
}
}
},
"serve": {
"executor": "@nrwl/node:node",
"options": {
"buildTarget": "cli:build"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
Expand Down
2 changes: 1 addition & 1 deletion apps/view/src/components/glass-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type SCProps = GlassPaneProps & { mobile: boolean };
const Container = styled.div<SCProps>`
min-width: ${({ minWidth, mobile }) => (mobile ? 0 : minWidth)}px;
min-height: 360px;
max-height: 500px;
max-height: ${({ mobile }) => (mobile ? 'unset' : '500px')};
flex-grow: ${props => props.grow ?? 1};
transition: opacity 0.3 ease-in-out;
Expand Down
10 changes: 8 additions & 2 deletions apps/view/src/components/hardware-info-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ type HardwareInfoProps = {
gap?: number;
children?: ReactNode;
infosPerPage: number;
onPageChange?: (page: number) => void;
} & InfoTableProps;

export const HardwareInfoContainer = motion(
Expand All @@ -128,6 +129,11 @@ export const HardwareInfoContainer = motion(
const childrenLength = Children.count(props.children);
const [page, setPage] = useState(0);

const changePage = (page: number) => {
setPage(page);
props.onPageChange?.(page);
};

return (
<Container mobile={isMobile}>
<InfoIcon {...props}>
Expand All @@ -145,15 +151,15 @@ export const HardwareInfoContainer = motion(
<IconContainer layout>
<FontAwesomeIcon
icon={faArrowLeft}
onClick={() => setPage(p => p - 1)}
onClick={() => changePage(page - 1)}
/>
</IconContainer>
)}
{(page + 1) * props.infosPerPage < props.infos.length && (
<IconContainer layout>
<FontAwesomeIcon
icon={faArrowRight}
onClick={() => setPage(p => p + 1)}
onClick={() => changePage(page + 1)}
/>
</IconContainer>
)}
Expand Down
28 changes: 27 additions & 1 deletion apps/view/src/components/main-widget-container.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { CpuLoad, NetworkLoad, RamLoad, StorageLoad } from '@dash/common';
import {
CpuLoad,
GpuLoad,
NetworkLoad,
RamLoad,
StorageLoad,
} from '@dash/common';
import { motion, Variants } from 'framer-motion';
import { FC, useEffect, useRef, useState } from 'react';
import io from 'socket.io-client';
Expand All @@ -9,6 +15,7 @@ import { environment } from '../environments/environment';
import { useIsMobile } from '../services/mobile';
import { CpuWidget } from '../widgets/cpu';
import { ErrorWidget } from '../widgets/error';
import { GpuWidget } from '../widgets/gpu';
import { NetworkWidget } from '../widgets/network';
import { RamWidget } from '../widgets/ram';
import { ServerWidget } from '../widgets/server';
Expand Down Expand Up @@ -69,11 +76,13 @@ export const MainWidgetContainer: FC = () => {
const ramData = serverInfo.data?.ram;
const networkData = serverInfo.data?.network;
const storageData = serverInfo.data?.storage;
const gpuData = serverInfo.data?.gpu;
const config = serverInfo.data?.config;

const [cpuLoad, setCpuLoad] = useState<CpuLoad[]>([]);
const [ramLoad, setRamLoad] = useState<RamLoad[]>([]);
const [networkLoad, setNetworkLoad] = useState<NetworkLoad[]>([]);
const [gpuLoad, setGpuLoad] = useState<GpuLoad[]>([]);
const [storageLoad, setStorageLoad] = useState<StorageLoad>();
const configRef = useRef(config);

Expand Down Expand Up @@ -116,6 +125,16 @@ export const MainWidgetContainer: FC = () => {
});
});

socket.on('gpu-load', data => {
setGpuLoad(oldData => {
if (oldData.length >= (configRef.current?.gpu_shown_datapoints ?? 0)) {
return [...oldData.slice(1), data];
} else {
return [...oldData, data];
}
});
});

socket.on('storage-load', data => {
setStorageLoad(data);
});
Expand Down Expand Up @@ -199,6 +218,13 @@ export const MainWidgetContainer: FC = () => {
data: networkData,
load: networkLoad,
},
gpu: {
grow: config.gpu_widget_grow,
minWidth: config.gpu_widget_min_width,
Widget: GpuWidget,
data: gpuData,
load: gpuLoad,
},
};

return (
Expand Down
3 changes: 3 additions & 0 deletions apps/view/src/theme/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Theme = {
ramPrimary: string;
storagePrimary: string;
networkPrimary: string;
gpuPrimary: string;
};
};

Expand All @@ -33,6 +34,7 @@ const lightTheme: Theme = {
ramPrimary: '#ff526f',
storagePrimary: '#49e37a',
networkPrimary: '#ffd745',
gpuPrimary: '#c97bff',
},
};

Expand All @@ -52,6 +54,7 @@ const darkTheme: Theme = {
ramPrimary: '#c43148',
storagePrimary: '#22b352',
networkPrimary: '#e9c235',
gpuPrimary: '#a135eb',
},
};

Expand Down

0 comments on commit 0328463

Please sign in to comment.