Skip to content

Commit

Permalink
frontend/license: allow configuring the GPU resource name
Browse files Browse the repository at this point in the history
  • Loading branch information
haraldschilly committed Jan 30, 2025
1 parent a66e226 commit 498fbfe
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 26 deletions.
3 changes: 2 additions & 1 deletion src/packages/frontend/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"tidyverse",
"timetravel",
"undelete",
"undeleting"
"undeleting",
"tolerations"
],
"ignoreWords": [
"antd",
Expand Down
66 changes: 51 additions & 15 deletions src/packages/frontend/site-licenses/purchase/quota-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
*/

/*
Editing a quota
- shows user rows for cpu, ram, disk, member, and always_running: optional
- they can edit all the rows.
- optional: also shows rows for support and network that can't be edited
*/

// cSpell: ignore jsonpatch requiresMemberhosting

import {
Button,
Checkbox,
Expand Down Expand Up @@ -44,14 +45,15 @@ import {
untangleUptime,
} from "@cocalc/util/consts/site-license";
import { KUCALC_ON_PREMISES } from "@cocalc/util/db-schema/site-defaults";
import {
COSTS,
CostMap,
} from "@cocalc/util/licenses/purchase/consts";
import { COSTS, CostMap } from "@cocalc/util/licenses/purchase/consts";
import { User } from "@cocalc/util/licenses/purchase/types";
import { money } from "@cocalc/util/licenses/purchase/utils";
import { plural, round1, test_valid_jsonpatch } from "@cocalc/util/misc";
import { extract_gpu, process_gpu_quota } from "@cocalc/util/types/gpu";
import {
extract_gpu,
GPU_DEFAULT_RESOURCE,
process_gpu_quota,
} from "@cocalc/util/types/gpu";
import { SiteLicenseQuota } from "@cocalc/util/types/site-licenses";
import { DEDICATED_VM_ONPREM_MACHINE } from "@cocalc/util/upgrades/consts";
import { Upgrades } from "@cocalc/util/upgrades/quota";
Expand Down Expand Up @@ -529,7 +531,8 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
</Paragraph>
<Paragraph>
In particular, the pod will get the following resource limit:{" "}
<code>nvidia.com/gpu: 1</code>.
<code>nvidia.com/gpu: 1</code>, where the specific GPU resource name
can be configured.
</Paragraph>
<Paragraph>
On top of that, you can optionally specify a{" "}
Expand Down Expand Up @@ -565,10 +568,18 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
}

function render_gpu(): JSX.Element {
const { num = 0, toleration = "", nodeLabel = "" } = extract_gpu(quota);
const {
num = 0,
toleration = "",
nodeLabel = "",
resource = GPU_DEFAULT_RESOURCE,
} = extract_gpu(quota);

const debug = process_gpu_quota(quota);

// edit text and save button on the same row
const style: CSS = { display: "inline-flex", gap: "5px" } as const;

return (
<Row style={ROW_STYLE}>
<Col md={col.control}>
Expand All @@ -583,6 +594,7 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
num: e.target.checked ? Math.max(1, num) : 0,
toleration,
nodeLabel,
resource,
},
})
}
Expand All @@ -600,11 +612,33 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
size={"small"}
value={num}
onChange={(num: number) =>
onChange({ gpu: { num, toleration, nodeLabel } })
onChange({ gpu: { num, toleration, nodeLabel, resource } })
}
/>{" "}
(usually "1")
</Paragraph>
<Paragraph>
<Text strong>GPU Resource</Text>{" "}
<TextInput
size={"small"}
disabled={disabled}
type={"text"}
on_change={(resource) =>
onChange({
gpu: {
resource: resource.trim(),
toleration,
num,
nodeLabel,
},
})
}
style={style}
text={resource}
/>{" "}
(optional, default "{GPU_DEFAULT_RESOURCE}", alternatively e.g.
"nvidia.com/mig-1g.5gb")
</Paragraph>
<Paragraph>
<Text strong>Node selector:</Text>{" "}
<TextInput
Expand All @@ -613,10 +647,10 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
type={"text"}
on_change={(label) =>
onChange({
gpu: { nodeLabel: label.trim(), toleration, num },
gpu: { nodeLabel: label.trim(), toleration, num, resource },
})
}
style={{ display: "inline-block" }}
style={style}
text={nodeLabel}
/>{" "}
(optional, [1])
Expand All @@ -628,9 +662,11 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
disabled={disabled}
type={"text"}
on_change={(tol) =>
onChange({ gpu: { toleration: tol.trim(), nodeLabel, num } })
onChange({
gpu: { toleration: tol.trim(), nodeLabel, num, resource },
})
}
style={{ display: "inline-block" }}
style={style}
text={toleration}
/>{" "}
(optional, [1])
Expand All @@ -639,7 +675,7 @@ export const QuotaEditor: React.FC<Props> = (props: Props) => {
[1] format: <code>key=value</code> or for taints, also{" "}
<code>key</code> to tolerate the key regardless of value. Keep the
field empty if you do not use label selectors or taints. Specify
mulitple ones via a "," comma. Below is a "debug" view.
multiple ones via a "," comma. Below is a "debug" view.
</Paragraph>
<pre style={{ fontSize: "85%" }}>
{JSON.stringify(debug, null, 2)}
Expand Down
33 changes: 23 additions & 10 deletions src/packages/util/types/gpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { SiteLicenseQuota } from "./site-licenses";

export const GPU_DEFAULT_RESOURCE = "nvidia.com/gpu"

export function extract_gpu(quota: SiteLicenseQuota = {}) {
const { gpu } = quota;
if (gpu == null) return { num: 0 };
Expand All @@ -10,7 +12,11 @@ export function extract_gpu(quota: SiteLicenseQuota = {}) {
}

type GPUQuotaInfo = {
resources?: { limits: { "nvidia.com/gpu": number } };
resources?: {
limits: {
[resource: string]: number; // resource default: $GPU_DEFAULT_RESOURCE
};
};
nodeSelector?: { [key: string]: string };
tolerations?: (
| {
Expand All @@ -28,31 +34,38 @@ type GPUQuotaInfo = {
};

export function process_gpu_quota(quota: SiteLicenseQuota = {}): GPUQuotaInfo {
const { num = 0, toleration = "", nodeLabel = "" } = extract_gpu(quota);
const {
num = 0,
toleration = "",
nodeLabel = "",
resource = GPU_DEFAULT_RESOURCE,
} = extract_gpu(quota);

const debug: GPUQuotaInfo = {};
const info: GPUQuotaInfo = {};
if (num > 0) {
debug.resources = { limits: { "nvidia.com/gpu": num } };
info.resources = { limits: { [resource]: num } };

if (nodeLabel) {
debug.nodeSelector = {};
info.nodeSelector = {};
for (const label of nodeLabel.split(",")) {
const [key, val] = label.trim().split("=");
debug.nodeSelector[key] = val;
info.nodeSelector[key] = val;
}
}

if (toleration) {
debug.tolerations = [];
info.tolerations = [];
for (const tol of toleration.split(",")) {
const [key, val] = tol.trim().split("=");
if (val) {
debug.tolerations.push({
info.tolerations.push({
key,
operator: "Equal",
value: val,
effect: "NoSchedule",
});
} else {
debug.tolerations.push({
info.tolerations.push({
key,
operator: "Exists",
effect: "NoSchedule",
Expand All @@ -61,5 +74,5 @@ export function process_gpu_quota(quota: SiteLicenseQuota = {}): GPUQuotaInfo {
}
}
}
return debug;
return info;
}
1 change: 1 addition & 0 deletions src/packages/util/types/site-licenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type GPU = {
num?: number; // usualy 1, to set nvidia.com/gpu=1, 0 means "disabled"
toleration?: string; // e.g. gpu=cocalc for key=value
nodeLabel?: string; // e.g. gpu=cocalc for key=value
resource?: string; // default: $GPU_DEFAULT_RESOURCE
};

export interface SiteLicenseQuota {
Expand Down

0 comments on commit 498fbfe

Please sign in to comment.