Skip to content

Commit f315efc

Browse files
committed
fix: add gpu stats to dashboard
this also adjusts the utilization colors so that lower utilizations get less saturated colors, and higher utilizations get more saturated colors
1 parent 4700ef4 commit f315efc

File tree

8 files changed

+117
-58
lines changed

8 files changed

+117
-58
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2023 The Kubernetes Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import type Kind from "./kinds.js"
18+
import type { ValidTheme as ValidUtilizationTheme } from "./themes/utilization.js"
19+
20+
export type SupportedUtilizationGrid = "cpu%" | "mem%" | "gpu%" | "gpumem%"
21+
22+
export type SupportedGrid = "status" | SupportedUtilizationGrid
23+
24+
export const defaultUtilizationThemes: Record<SupportedUtilizationGrid, ValidUtilizationTheme> = {
25+
"cpu%": "rain",
26+
"mem%": "purple",
27+
"gpu%": "red",
28+
"gpumem%": "magenta",
29+
}
30+
31+
export const providerFor: Record<SupportedUtilizationGrid, string> = {
32+
"cpu%": "CPU Utilization",
33+
"mem%": "Mem Utilization",
34+
"gpu%": "GPU Utilization",
35+
"gpumem%": "Mem Utilization",
36+
}
37+
38+
export function isSupportedGrid(kind: Kind): kind is SupportedGrid {
39+
return kind === "status" || Object.keys(defaultUtilizationThemes).includes(kind)
40+
}

plugins/plugin-codeflare-dashboard/src/controller/dashboard/index.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import tailf from "./tailf.js"
2020
import dashboardUI from "./db.js"
2121
import status from "./status.js"
2222
import utilization from "./utilization.js"
23+
import { SupportedGrid, isSupportedGrid } from "./grids.js"
2324
import { KindA, isValidKindA, validKinds } from "./kinds.js"
2425
import type { GridSpec } from "../../components/Dashboard/index.js"
2526

@@ -39,8 +40,8 @@ function enterAltBufferMode() {
3940
console.log("\x1b[?1049h")
4041
}
4142

42-
export function usage(cmd: string) {
43-
return `Usage: codeflare ${cmd} ${validKinds().join("|")} [<jobId>|-N]`
43+
export function usage(cmd: string, extraKinds: string[] = []) {
44+
return `Usage: codeflare ${cmd} ${extraKinds.concat(validKinds()).join("|")} [<jobId>|-N]`
4445
}
4546

4647
async function lastNJob(profile: string, N: number) {
@@ -90,25 +91,29 @@ export default async function dashboard(args: Arguments<Options>, cmd: "db" | "d
9091
const { jobId, profile } = await jobIdFrom(args, cmd)
9192

9293
if (!isValidKindA(kind)) {
93-
throw new Error(usage(cmd))
94+
throw new Error(usage(cmd, ["all"]))
9495
} else if (!jobId) {
95-
throw new Error(usage(cmd))
96+
throw new Error(usage(cmd, ["all"]))
9697
}
9798

98-
const gridFor = async (kind: "status" | "cpu" | "memory"): Promise<GridSpec> => {
99+
const gridFor = async (kind: SupportedGrid): Promise<GridSpec> => {
99100
const tails = await tailf(kind, profile, jobId)
100101
return kind === "status"
101102
? status(tails, { demo, theme, themeDefault: "patternfly" })
102-
: kind === "cpu"
103-
? utilization(kind, tails, { demo, theme, themeDefault: "rain" })
104-
: utilization(kind, tails, { demo, theme, themeDefault: "purple" })
103+
: utilization(kind, tails, { demo, theme })
105104
}
106105

107106
const gridForA = async (kind: KindA): Promise<null | GridSpec | GridSpec[]> => {
108107
if (kind === "all") {
109-
const grids = await Promise.all([gridFor("status"), gridFor("cpu"), gridFor("memory")])
108+
const grids = await Promise.all([
109+
gridFor("status"),
110+
gridFor("cpu%"),
111+
gridFor("mem%"),
112+
gridFor("gpu%"),
113+
gridFor("gpumem%"),
114+
])
110115
return grids.filter(Boolean)
111-
} else if (kind === "status" || kind === "cpu" || kind === "memory") {
116+
} else if (isSupportedGrid(kind)) {
112117
return gridFor(kind)
113118
} else {
114119
return null

plugins/plugin-codeflare-dashboard/src/controller/dashboard/kinds.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17-
type Kind = "status" | "gpu" | "cpu" | "memory" | "logs"
17+
import type { SupportedGrid } from "./grids.js"
18+
19+
type Kind = SupportedGrid | "logs"
1820
export type KindA = Kind | "all"
1921
export default Kind
2022

@@ -26,9 +28,10 @@ export const resourcePaths: Record<Kind, string[]> = {
2628
"events/runtime-env-setup.txt",
2729
"logs/job.txt",
2830
],
29-
gpu: ["resources/gpu.txt"],
30-
cpu: ["resources/pod-vmstat.txt"],
31-
memory: ["resources/pod-memory.txt"],
31+
"gpu%": ["resources/gpu.txt"],
32+
"gpumem%": ["resources/gpu.txt"],
33+
"cpu%": ["resources/pod-vmstat.txt"],
34+
"mem%": ["resources/pod-memory.txt"],
3235
logs: ["logs/job.txt"],
3336
}
3437

plugins/plugin-codeflare-dashboard/src/controller/dashboard/themes/utilization.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ import type { TextProps } from "ink"
2121
export type Theme = [TextProps, TextProps, TextProps, TextProps, TextProps]
2222

2323
export const red: Theme = [
24-
{ color: "#b30000" },
25-
{ color: "#e34a33" },
26-
{ color: "#fc8d59" },
27-
{ color: "#fdbb84" },
2824
{ color: "#fdd49e" },
25+
{ color: "#fdbb84" },
26+
{ color: "#fc8d59" },
27+
{ color: "#e34a33" },
28+
{ color: "#b30000" },
2929
]
3030

3131
export const purple: Theme = [
32-
{ color: "#810f7c" },
33-
{ color: "#8856a7" },
34-
{ color: "#8c96c6" },
35-
{ color: "#9ebcda" },
3632
{ color: "#bfd3e6" },
33+
{ color: "#9ebcda" },
34+
{ color: "#8c96c6" },
35+
{ color: "#8856a7" },
36+
{ color: "#810f7c" },
3737
]
3838

3939
/*
@@ -47,35 +47,35 @@ export const magenta: Theme = [
4747
*/
4848

4949
export const magenta: Theme = [
50-
{ color: "#7a0177" },
51-
{ color: "#c51b8a" },
52-
{ color: "#f768a1" },
53-
{ color: "#fa9fb5" },
5450
{ color: "#fcc5c0" },
51+
{ color: "#fa9fb5" },
52+
{ color: "#f768a1" },
53+
{ color: "#c51b8a" },
54+
{ color: "#7a0177" },
5555
]
5656

5757
export const green: Theme = [
58-
{ color: "#006837" },
59-
{ color: "#31a354" },
60-
{ color: "#78c679" },
61-
{ color: "#addd8e" },
6258
{ color: "#d9f0a3" },
59+
{ color: "#addd8e" },
60+
{ color: "#78c679" },
61+
{ color: "#31a354" },
62+
{ color: "#006837" },
6363
]
6464

6565
export const blue: Theme = [
66-
{ color: "#045a8d" },
67-
{ color: "#2b8cbe" },
68-
{ color: "#74a9cf" },
69-
{ color: "#a6bddb" },
7066
{ color: "#d0d1e6" },
67+
{ color: "#a6bddb" },
68+
{ color: "#74a9cf" },
69+
{ color: "#2b8cbe" },
70+
{ color: "#045a8d" },
7171
]
7272

7373
export const rain: Theme = [
74-
{ color: "#016c59" },
75-
{ color: "#1c9099" },
76-
{ color: "#67a9cf" },
77-
{ color: "#a6bddb" },
7874
{ color: "#d0d1e6" },
75+
{ color: "#a6bddb" },
76+
{ color: "#67a9cf" },
77+
{ color: "#1c9099" },
78+
{ color: "#016c59" },
7979
]
8080

8181
export const themes = {

plugins/plugin-codeflare-dashboard/src/controller/dashboard/utilization.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ import type { Tail } from "./tailf.js"
2121
import type Options from "./options.js"
2222
import { isValidTheme, themes } from "./themes/utilization.js"
2323
import { OnData, Worker, GridSpec } from "../../components/Dashboard/index.js"
24+
import { SupportedUtilizationGrid, defaultUtilizationThemes, providerFor } from "./grids.js"
2425

25-
type WorkerState = "0-20%" | "20-40%" | "40-60%" | "60-80%" | "80-100%"
26+
type WorkerState = "<20%" | "<40%" | "<60%" | "<80%" | "<100%"
2627

27-
const states: WorkerState[] = ["0-20%", "20-40%", "40-60%", "60-80%", "80-100%"]
28+
const states: WorkerState[] = ["<20%", "<40%", "<60%", "<80%", "<100%"]
2829

2930
/**
3031
* Maintain a model of live data from a given set of file streams
@@ -38,18 +39,27 @@ class Live {
3839
private stateFor(util: string): WorkerState {
3940
const percent = parseInt(util.replace(/%$/, ""), 10)
4041
const bucketWidth = ~~(100 / states.length)
41-
return states[~~(percent / bucketWidth)]
42+
return states[Math.min(~~(percent / bucketWidth), states.length - 1)]
4243
}
4344

44-
public constructor(private readonly tails: Promise<Tail>[], cb: OnData, styleOf: Record<WorkerState, TextProps>) {
45+
public constructor(
46+
expectedProvider: string,
47+
private readonly tails: Promise<Tail>[],
48+
cb: OnData,
49+
styleOf: Record<WorkerState, TextProps>
50+
) {
4551
tails.map((tailf) => {
4652
tailf.then(({ stream }) => {
4753
stream.on("data", (data) => {
4854
if (data) {
4955
const line = stripAnsi(data)
5056
const cols = line.split(/\s+/)
5157

52-
const provider = !cols[0] ? undefined : cols[0].replace(/^\[/, "")
58+
const provider = (!cols[0] ? undefined : cols[0].replace(/^\[/, "")) + (cols[1] ? " " + cols[1] : "")
59+
if (provider !== expectedProvider) {
60+
return
61+
}
62+
5363
const key = !cols[2] ? undefined : cols[2].replace(/\]$/, "")
5464
const metric = !provider || !key ? undefined : this.stateFor(key)
5565
const name = cols[3] ? cols[3].trim() : undefined
@@ -61,7 +71,7 @@ class Live {
6171
} else if (!metric) {
6272
// ignoring this line
6373
return
64-
} else if (provider === "Workers" && (!/^pod\//.test(name) || /cleaner/.test(name))) {
74+
} else if (!/^pod\//.test(name) || /cleaner/.test(name)) {
6575
// only track pod events, and ignore our custodial pods
6676
return
6777
} else {
@@ -178,11 +188,11 @@ function capitalize(str: string) {
178188
}
179189

180190
export default function utilizationDashboard(
181-
kind: "cpu" | "memory",
191+
kind: SupportedUtilizationGrid,
182192
tails: Promise<Tail>[],
183-
opts: Pick<Options, "demo" | "theme" | "themeDefault">
193+
opts: Pick<Options, "demo" | "theme"> & Partial<Pick<Options, "themeDefault">>
184194
): GridSpec {
185-
const { theme: themeS = opts.themeDefault } = opts
195+
const { theme: themeS = opts.themeDefault || defaultUtilizationThemes[kind] } = opts
186196
if (!isValidTheme(themeS)) {
187197
throw new Error("Invalid theme: " + themeS)
188198
}
@@ -198,7 +208,8 @@ export default function utilizationDashboard(
198208
if (opts.demo) {
199209
return new Demo(cb, styleOf)
200210
} else {
201-
return new Live(tails, cb, styleOf)
211+
const expectedProvider = providerFor[kind]
212+
return new Live(expectedProvider, tails, cb, styleOf)
202213
}
203214
}
204215

plugins/plugin-codeflare-dashboard/src/controller/dump.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export default async function dump(args: Arguments<Options>) {
5151
if (kind === "path") {
5252
// print the path to the data captured for the given jobId in the given profile
5353
const { dirname } = await import("path")
54-
return Array.from(new Set(await pathsFor("cpu", profile, jobId).then((_) => _.map((_) => dirname(_)))))[0]
54+
return Array.from(new Set(await pathsFor("cpu%", profile, jobId).then((_) => _.map((_) => dirname(dirname(_))))))[0]
5555
} else if (!args.parsedOptions.f) {
5656
const { createReadStream } = await import("fs")
5757
await Promise.all(

plugins/plugin-codeflare/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@types/split2": "^3.2.1"
3131
},
3232
"dependencies": {
33-
"@guidebooks/store": "^7.4.3",
33+
"@guidebooks/store": "^7.4.4",
3434
"@logdna/tail-file": "^3.0.1",
3535
"@patternfly/react-charts": "^6.94.18",
3636
"@patternfly/react-core": "^4.276.6",

0 commit comments

Comments
 (0)