Skip to content

Commit

Permalink
feat: add optional storage split view
Browse files Browse the repository at this point in the history
closes #104
  • Loading branch information
MauriceNino committed Jun 20, 2022
1 parent b84b99c commit 962ecbe
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 118 deletions.
1 change: 1 addition & 0 deletions apps/api/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const CONFIG: Config = {
cpu_shown_datapoints: numNull(penv('CPU_SHOWN_DATAPOINTS')) ?? 20,
cpu_poll_interval: numNull(penv('CPU_POLL_INTERVAL')) ?? 1000,

enable_storage_split_view: penv('ENABLE_STORAGE_SPLIT_VIEW') === 'true',
storage_label_list: lst(
penv('STORAGE_LABEL_LIST') ?? 'brand,size,type,raid'
) as any[],
Expand Down
34 changes: 30 additions & 4 deletions apps/api/src/dynamic-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,39 @@ export const getDynamicServerInfo = () => {
1,
CONFIG.storage_poll_interval,
async (): Promise<StorageLoad> => {
const sizes = await si.fsSize();

const filtered = sizes.filter(
const [layout, blocks, sizes] = await Promise.all([
getStaticServerInfo(),
si.blockDevices(),
si.fsSize(),
]);

const storageLayout = layout.storage.layout;
const validMounts = sizes.filter(
({ mount }) => mount.startsWith('/mnt/host_') || mount === '/'
);

return filtered.reduce((acc, { used }) => acc + used, 0);
return {
layout: storageLayout
.map(({ device }) => {
const deviceParts = blocks.filter(
block => block.type === 'part' && block.name.startsWith(device)
);
const isHost = deviceParts.every(({ mount }) => mount === '');

return isHost
? validMounts.find(({ mount }) => mount === '/')?.used
: deviceParts.reduce(
(acc, curr) =>
acc +
(validMounts.find(({ mount }) => curr.mount === mount)
?.used ?? 0),
0
);
})
.map(used => ({
load: used,
})),
};
}
);

Expand Down
90 changes: 38 additions & 52 deletions apps/api/src/static-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,61 +120,47 @@ const loadStorageInfo = async (): Promise<void> => {

const raidMembers = blocks.filter(block => block.fsType.endsWith('_member'));
const blockDisks = blocks.filter(block => block.type === 'disk');
const blockParts = blocks.filter(block => block.type === 'part');

if (raidMembers.length > 0) {
const blockLayout = blockDisks
.map(disk => {
const diskRaidMem = raidMembers.filter(member =>
member.name.startsWith(disk.name)
);
const diskParts = blockParts.filter(part =>
part.name.startsWith(disk.name)
);
const nativeDisk = disks.find(d => d.name === disk.model);

if (nativeDisk != null) {
if (diskParts.some(part => part.mount != null && part.mount !== '')) {
return {
brand: nativeDisk.vendor,
size: nativeDisk.size,
type: nativeDisk.type,
};
} else if (diskRaidMem.length > 0) {
const label = diskRaidMem[0].label.includes(':')
? diskRaidMem[0].label.split(':')[0]
: diskRaidMem[0].label;
return {
brand: nativeDisk.vendor,
size: nativeDisk.size,
type: nativeDisk.type,
raidGroup: label,
};
}

const blockLayout = blockDisks
.map(disk => {
const device = disk.name;
const diskRaidMem = raidMembers.filter(member =>
member.name.startsWith(device)
);
const nativeDisk = disks.find(d => d.name === disk.model);

if (nativeDisk != null) {
if (diskRaidMem.length > 0) {
const label = diskRaidMem[0].label.includes(':')
? diskRaidMem[0].label.split(':')[0]
: diskRaidMem[0].label;
return {
device: device,
brand: nativeDisk.vendor,
size: nativeDisk.size,
type: nativeDisk.type,
raidGroup: label,
};
} else {
return {
device: device,
brand: nativeDisk.vendor,
size: nativeDisk.size,
type: nativeDisk.type,
};
}
}

return undefined;
})
.filter(d => d != null);
return undefined;
})
.filter(d => d != null);

STATIC_INFO.next({
...STATIC_INFO.getValue(),
storage: {
layout: blockLayout,
},
});
} else {
STATIC_INFO.next({
...STATIC_INFO.getValue(),
storage: {
layout: disks.map(({ size, type, vendor }) => ({
brand: vendor,
size,
type,
})),
},
});
}
STATIC_INFO.next({
...STATIC_INFO.getValue(),
storage: {
layout: blockLayout,
},
});
};

const loadNetworkInfo = async (): Promise<void> => {
Expand Down
10 changes: 9 additions & 1 deletion apps/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ In Podman: ${isPodman}
boolean: true,
describe: 'show raw network info',
})
.option('gpu', {
boolean: true,
describe: 'show raw gpu info',
})
.option('custom', {
string: true,
describe:
Expand All @@ -100,7 +104,8 @@ In Podman: ${isPodman}
}
if (args.storage) {
console.log('Disk Layout:', inspectObj(await si.diskLayout()));
console.log('FS Size', inspectObj(await si.fsSize()));
console.log('FS Size:', inspectObj(await si.fsSize()));
console.log('BLock Devices:', inspectObj(await si.blockDevices()));
}
if (args.network) {
console.log(
Expand All @@ -109,6 +114,9 @@ In Podman: ${isPodman}
);
console.log('Network Stats:', inspectObj(await si.networkStats()));
}
if (args.gpu) {
console.log('Graphics:', inspectObj(await si.graphics()));
}
if (args.custom) {
console.log(
`Custom [${args.custom}]`,
Expand Down
10 changes: 10 additions & 0 deletions apps/docs/docs/config/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ sidebar_position: 4

# Storage Widget

## `DASHDOT_ENABLE_STORAGE_SPLIT_VIEW`

If you want to enable an optional split view for the storage widget, set this to `true`.

The split view will only work reliably, if all drives are mounted in the container.
Otherwise, any unassigned space will be attributed to the host drive.

- type: `boolean`
- default: `false`

## `DASHDOT_STORAGE_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`, `size`, `type`).
Expand Down
66 changes: 65 additions & 1 deletion apps/view/src/components/chart-components.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { FC, useMemo, useState } from 'react';
import { Area, AreaChart, Pie, PieChart, Sector } from 'recharts';
import {
Area,
AreaChart,
BarChart,
Pie,
PieChart,
Sector,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import styled, { useTheme } from 'styled-components';
import { ThemedText } from './text';

Expand Down Expand Up @@ -172,3 +182,57 @@ export const DefaultPieChart: FC<DefaultPieChartProps> = ({
</>
);
};

const ToolTipContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
`;

type DefaultVertBarChartProps = {
height: number;
width: number;
children: React.ReactNode;
data: any[];
tooltipRenderer: (value: any) => React.ReactNode;
};

export const DefaultVertBarChart: FC<DefaultVertBarChartProps> = ({
data,
height,
width,
children,
tooltipRenderer,
}) => {
const barSize = Math.min(height / data.length - 10, 60);
const gap = (data.length - 1) * 30;
const allBars = barSize * data.length;
const margin = (height - gap - allBars) / 2;

return (
<BarChart
data={data}
height={height}
width={width}
layout={'vertical'}
margin={{
top: margin,
bottom: margin,
right: 20,
left: 20,
}}
barSize={barSize}
>
<XAxis type='number' hide />
<YAxis type='category' hide />

<Tooltip
cursor={false}
content={x => <ToolTipContainer>{tooltipRenderer(x)}</ToolTipContainer>}
/>
{children}
</BarChart>
);
};
2 changes: 2 additions & 0 deletions apps/view/src/services/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import store from 'store';
type Settings = {
darkMode: boolean;
multiCore: boolean;
splitStorage: boolean;
};

export const setStoreSetting = <T extends keyof Settings = keyof Settings>(
Expand All @@ -24,6 +25,7 @@ const ALL_DISPATCHES: {
} = {
darkMode: [],
multiCore: [],
splitStorage: [],
};

export const useSetting = <T extends keyof Settings = keyof Settings>(
Expand Down
4 changes: 4 additions & 0 deletions apps/view/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ChartVal = {
x: number;
y: number;
};
11 changes: 5 additions & 6 deletions apps/view/src/widgets/cpu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Config, CpuInfo, CpuLoad } from '@dash/common';
import { faMicrochip } from '@fortawesome/free-solid-svg-icons';
//@ts-ignore
import { Datum } from '@nivo/line';
import { Variants } from 'framer-motion';
import { FC } from 'react';
import { Tooltip, YAxis } from 'recharts';
Expand All @@ -14,6 +12,7 @@ import { WidgetSwitch } from '../components/widget-switch';
import { useSetting } from '../services/settings';
import { celsiusToFahrenheit } from '../utils/calculations';
import { toInfoTable } from '../utils/format';
import { ChartVal } from '../utils/types';

const containerVariants = {
animate: {
Expand Down Expand Up @@ -69,7 +68,7 @@ export const CpuWidget: FC<CpuWidgetProps> = ({ load, data, config }) => {

const [multiCore, setMulticore] = useSetting('multiCore', false);

let chartData: Datum[][] = [];
let chartData: ChartVal[][] = [];

if (multiCore) {
const coresWithValues = load.reduce(
Expand All @@ -92,13 +91,13 @@ export const CpuWidget: FC<CpuWidgetProps> = ({ load, data, config }) => {
return acc;
},
{} as {
[key: number]: Datum[];
[key: number]: ChartVal[];
}
);

chartData = Object.entries(coresWithValues).map(([_, value]) => value);
} else {
const chartValues: Datum[] = load.reduce((acc, curr, i) => {
const chartValues: ChartVal[] = load.reduce((acc, curr, i) => {
const avgLoad =
curr.reduce((acc, curr) => acc + curr.load, 0) / curr.length;

Expand All @@ -107,7 +106,7 @@ export const CpuWidget: FC<CpuWidgetProps> = ({ load, data, config }) => {
y: avgLoad,
});
return acc;
}, [] as Datum[]);
}, [] as ChartVal[]);

chartData = [chartValues];
}
Expand Down
7 changes: 3 additions & 4 deletions apps/view/src/widgets/gpu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Config, GpuInfo, GpuLoad } from '@dash/common';
import { faDesktop } from '@fortawesome/free-solid-svg-icons';
//@ts-ignore
import { Datum } from '@nivo/line';
import { FC, useMemo, useState } from 'react';
import { Tooltip, YAxis } from 'recharts';
import { useTheme } from 'styled-components';
Expand All @@ -11,6 +9,7 @@ import { HardwareInfoContainer } from '../components/hardware-info-container';
import { ThemedText } from '../components/text';
import { bytePrettyPrint } from '../utils/calculations';
import { toInfoTable } from '../utils/format';
import { ChartVal } from '../utils/types';

type GpuWidgetProps = {
load: GpuLoad[];
Expand Down Expand Up @@ -65,11 +64,11 @@ export const GpuWidget: FC<GpuWidgetProps> = ({ load, data, config }) => {
const chartDataLoad = load.map((load, i) => ({
x: i,
y: load.layout[page].load,
})) as Datum[];
})) as ChartVal[];
const chartDataMemory = load.map((load, i) => ({
x: i,
y: load.layout[page].memory,
})) as Datum[];
})) as ChartVal[];

return (
<HardwareInfoContainer
Expand Down
Loading

0 comments on commit 962ecbe

Please sign in to comment.