Put resources where somebody would look for them
`resources` was reachable from the SDK and the API and nowhere else, and `node_queued` was published to nothing at all — so a node waiting for a machine looked exactly like a node that had hung, which is the failure the event was added for. The node panel gets a Resources section: a size by name, the numbers for a node that wants its own, and how long it is expected to take. Picking a flavor drops the numbers, because saying both is two answers and the engine refuses it. A queued node draws a neutral dot rather than one of the three status colours — it is not running, it did not go well, and it did not go wrong; it is idle with a reason, which the tooltip gives. And a Workers screen, which is the first UI for any of this: every machine the engine can reach with what is free of each, what is attached, what a cluster has been asked for, and the sizes, editable. `fluksio status` grew a line of the same. The demo's training node already preferred a GPU worker, which was exactly the declaration that used to be dropped, so it now says how much of that machine it takes as well. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6HeySA27EkGANZN95QySW
This commit is contained in:
@@ -722,6 +722,154 @@ export const EventRowSchema = {
|
||||
title: 'EventRow'
|
||||
} as const;
|
||||
|
||||
export const FlavorCreateSchema = {
|
||||
properties: {
|
||||
cpus: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
title: 'Cpus',
|
||||
default: 1
|
||||
},
|
||||
gpus: {
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
title: 'Gpus',
|
||||
default: 0
|
||||
},
|
||||
ram: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
title: 'Ram',
|
||||
default: 2048
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
maxLength: 255,
|
||||
title: 'Description',
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
maxLength: 64,
|
||||
title: 'Name'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
title: 'FlavorCreate'
|
||||
} as const;
|
||||
|
||||
export const FlavorPublicSchema = {
|
||||
properties: {
|
||||
cpus: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
title: 'Cpus',
|
||||
default: 1
|
||||
},
|
||||
gpus: {
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
title: 'Gpus',
|
||||
default: 0
|
||||
},
|
||||
ram: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
title: 'Ram',
|
||||
default: 2048
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
maxLength: 255,
|
||||
title: 'Description',
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
title: 'Name'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
title: 'FlavorPublic'
|
||||
} as const;
|
||||
|
||||
export const FlavorUpdateSchema = {
|
||||
properties: {
|
||||
cpus: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Cpus'
|
||||
},
|
||||
gpus: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Gpus'
|
||||
},
|
||||
ram: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Ram'
|
||||
},
|
||||
description: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
maxLength: 255
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Description'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
title: 'FlavorUpdate',
|
||||
description: 'Everything but the name: renaming would orphan the nodes that ask.'
|
||||
} as const;
|
||||
|
||||
export const FlavorsPublicSchema = {
|
||||
properties: {
|
||||
data: {
|
||||
items: {
|
||||
'$ref': '#/components/schemas/FlavorPublic'
|
||||
},
|
||||
type: 'array',
|
||||
title: 'Data'
|
||||
},
|
||||
count: {
|
||||
type: 'integer',
|
||||
title: 'Count'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['data', 'count'],
|
||||
title: 'FlavorsPublic'
|
||||
} as const;
|
||||
|
||||
export const FlowDef_InputSchema = {
|
||||
properties: {
|
||||
name: {
|
||||
@@ -2364,6 +2512,22 @@ export const RemoteUserBodySchema = {
|
||||
title: 'RemoteUserBody'
|
||||
} as const;
|
||||
|
||||
export const ResourceLevelSchema = {
|
||||
properties: {
|
||||
total: {
|
||||
type: 'integer',
|
||||
title: 'Total'
|
||||
},
|
||||
free: {
|
||||
type: 'integer',
|
||||
title: 'Free'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['total', 'free'],
|
||||
title: 'ResourceLevel'
|
||||
} as const;
|
||||
|
||||
export const ResourcesSchema = {
|
||||
properties: {
|
||||
cpus: {
|
||||
@@ -2380,6 +2544,44 @@ export const ResourcesSchema = {
|
||||
description: 'Whole devices held for the whole execution, named to the node through CUDA_VISIBLE_DEVICES. Nothing else is given them while it runs, which is what keeps two preallocating processes apart.',
|
||||
default: 0
|
||||
},
|
||||
ram: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Ram',
|
||||
description: "Megabytes held for the whole execution; accepts '512M' or '2G'. Counted against machines that said how much they have, and ignored by those that did not — which is a machine with nothing to say about memory, not one with none."
|
||||
},
|
||||
flavor: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Flavor',
|
||||
description: 'A stored size by name, standing in for cpus, gpus and ram. Read again every time the node is built, so editing the flavor edits what the next run gets.'
|
||||
},
|
||||
duration_s: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Duration S',
|
||||
description: "How long one execution is expected to take; accepts '30m' or '2h'. A statement about the node for whoever is planning around it, not a limit — the limit is `timeout`."
|
||||
},
|
||||
env: {
|
||||
additionalProperties: {
|
||||
type: 'string'
|
||||
@@ -2407,6 +2609,42 @@ arrives. Both are a node saying how much of the machine it takes, which is
|
||||
what this is.`
|
||||
} as const;
|
||||
|
||||
export const ResourcesSnapshotSchema = {
|
||||
properties: {
|
||||
cpus: {
|
||||
'$ref': '#/components/schemas/ResourceLevel'
|
||||
},
|
||||
gpus: {
|
||||
'$ref': '#/components/schemas/ResourceLevel'
|
||||
},
|
||||
waiting: {
|
||||
items: {
|
||||
'$ref': '#/components/schemas/WaitingNode'
|
||||
},
|
||||
type: 'array',
|
||||
title: 'Waiting'
|
||||
},
|
||||
targets: {
|
||||
items: {
|
||||
'$ref': '#/components/schemas/TargetResources'
|
||||
},
|
||||
type: 'array',
|
||||
title: 'Targets'
|
||||
},
|
||||
provisioners: {
|
||||
items: {
|
||||
additionalProperties: true,
|
||||
type: 'object'
|
||||
},
|
||||
type: 'array',
|
||||
title: 'Provisioners'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['cpus', 'gpus'],
|
||||
title: 'ResourcesSnapshot'
|
||||
} as const;
|
||||
|
||||
export const RuleSchema = {
|
||||
properties: {
|
||||
events: {
|
||||
@@ -2877,6 +3115,47 @@ export const SweepEntrySchema = {
|
||||
title: 'SweepEntry'
|
||||
} as const;
|
||||
|
||||
export const TargetResourcesSchema = {
|
||||
properties: {
|
||||
target: {
|
||||
type: 'string',
|
||||
title: 'Target'
|
||||
},
|
||||
cpus: {
|
||||
'$ref': '#/components/schemas/ResourceLevel'
|
||||
},
|
||||
gpus: {
|
||||
'$ref': '#/components/schemas/ResourceLevel'
|
||||
},
|
||||
ram_mb: {
|
||||
anyOf: [
|
||||
{
|
||||
'$ref': '#/components/schemas/ResourceLevel'
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
]
|
||||
},
|
||||
labels: {
|
||||
items: {
|
||||
type: 'string'
|
||||
},
|
||||
type: 'array',
|
||||
title: 'Labels'
|
||||
},
|
||||
in_flight: {
|
||||
type: 'integer',
|
||||
title: 'In Flight',
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['target', 'cpus', 'gpus'],
|
||||
title: 'TargetResources',
|
||||
description: 'One machine: this engine, or a worker attached to it.'
|
||||
} as const;
|
||||
|
||||
export const TokenSchema = {
|
||||
properties: {
|
||||
access_token: {
|
||||
@@ -3326,6 +3605,26 @@ export const ValidationResultSchema = {
|
||||
title: 'ValidationResult'
|
||||
} as const;
|
||||
|
||||
export const WaitingNodeSchema = {
|
||||
properties: {
|
||||
node: {
|
||||
type: 'string',
|
||||
title: 'Node'
|
||||
},
|
||||
reason: {
|
||||
type: 'string',
|
||||
title: 'Reason'
|
||||
},
|
||||
seconds: {
|
||||
type: 'number',
|
||||
title: 'Seconds'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
required: ['node', 'reason', 'seconds'],
|
||||
title: 'WaitingNode'
|
||||
} as const;
|
||||
|
||||
export const WidgetDefSchema = {
|
||||
properties: {
|
||||
id: {
|
||||
@@ -3427,6 +3726,27 @@ export const WorkerInfoSchema = {
|
||||
type: 'string',
|
||||
title: 'Venv Digest',
|
||||
default: ''
|
||||
},
|
||||
cpus: {
|
||||
type: 'integer',
|
||||
title: 'Cpus',
|
||||
default: 1
|
||||
},
|
||||
gpus: {
|
||||
type: 'integer',
|
||||
title: 'Gpus',
|
||||
default: 0
|
||||
},
|
||||
ram_mb: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'integer'
|
||||
},
|
||||
{
|
||||
type: 'null'
|
||||
}
|
||||
],
|
||||
title: 'Ram Mb'
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -224,6 +224,37 @@ export type EventRow = {
|
||||
actor: string;
|
||||
};
|
||||
|
||||
export type FlavorCreate = {
|
||||
cpus?: number;
|
||||
gpus?: number;
|
||||
ram?: number;
|
||||
description?: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type FlavorPublic = {
|
||||
cpus?: number;
|
||||
gpus?: number;
|
||||
ram?: number;
|
||||
description?: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type FlavorsPublic = {
|
||||
data: Array<FlavorPublic>;
|
||||
count: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Everything but the name: renaming would orphan the nodes that ask.
|
||||
*/
|
||||
export type FlavorUpdate = {
|
||||
cpus?: (number | null);
|
||||
gpus?: (number | null);
|
||||
ram?: (number | null);
|
||||
description?: (string | null);
|
||||
};
|
||||
|
||||
/**
|
||||
* One atomic flow.
|
||||
*/
|
||||
@@ -866,6 +897,11 @@ export type RemoteUserBody = {
|
||||
code: string;
|
||||
};
|
||||
|
||||
export type ResourceLevel = {
|
||||
total: number;
|
||||
free: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* What one execution of a node needs to have to itself.
|
||||
*
|
||||
@@ -890,6 +926,18 @@ export type Resources = {
|
||||
* Whole devices held for the whole execution, named to the node through CUDA_VISIBLE_DEVICES. Nothing else is given them while it runs, which is what keeps two preallocating processes apart.
|
||||
*/
|
||||
gpus?: number;
|
||||
/**
|
||||
* Megabytes held for the whole execution; accepts '512M' or '2G'. Counted against machines that said how much they have, and ignored by those that did not — which is a machine with nothing to say about memory, not one with none.
|
||||
*/
|
||||
ram?: (number | null);
|
||||
/**
|
||||
* A stored size by name, standing in for cpus, gpus and ram. Read again every time the node is built, so editing the flavor edits what the next run gets.
|
||||
*/
|
||||
flavor?: (string | null);
|
||||
/**
|
||||
* How long one execution is expected to take; accepts '30m' or '2h'. A statement about the node for whoever is planning around it, not a limit — the limit is `timeout`.
|
||||
*/
|
||||
duration_s?: (number | null);
|
||||
/**
|
||||
* Extra environment for the worker this node runs in, applied over what the allocation derives. Where a library's own tuning goes — XLA_FLAGS, XLA_PYTHON_CLIENT_MEM_FRACTION — since those are composed strings the engine must not invent.
|
||||
*/
|
||||
@@ -898,6 +946,16 @@ export type Resources = {
|
||||
};
|
||||
};
|
||||
|
||||
export type ResourcesSnapshot = {
|
||||
cpus: ResourceLevel;
|
||||
gpus: ResourceLevel;
|
||||
waiting?: Array<WaitingNode>;
|
||||
targets?: Array<TargetResources>;
|
||||
provisioners?: Array<{
|
||||
[key: string]: unknown;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Which events go to which channels.
|
||||
*/
|
||||
@@ -1033,6 +1091,18 @@ export type SweepEntry = {
|
||||
idempotency_key?: (string | null);
|
||||
};
|
||||
|
||||
/**
|
||||
* One machine: this engine, or a worker attached to it.
|
||||
*/
|
||||
export type TargetResources = {
|
||||
target: string;
|
||||
cpus: ResourceLevel;
|
||||
gpus: ResourceLevel;
|
||||
ram_mb?: (ResourceLevel | null);
|
||||
labels?: Array<(string)>;
|
||||
in_flight?: number;
|
||||
};
|
||||
|
||||
export type Token = {
|
||||
access_token: string;
|
||||
token_type?: string;
|
||||
@@ -1134,6 +1204,12 @@ export type ValidationResult = {
|
||||
issues?: Array<ValidationIssue>;
|
||||
};
|
||||
|
||||
export type WaitingNode = {
|
||||
node: string;
|
||||
reason: string;
|
||||
seconds: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* One tile: what it shows or does, and where it sits.
|
||||
*
|
||||
@@ -1184,6 +1260,9 @@ export type WorkerInfo = {
|
||||
last_seen?: number;
|
||||
python?: string;
|
||||
venv_digest?: string;
|
||||
cpus?: number;
|
||||
gpus?: number;
|
||||
ram_mb?: (number | null);
|
||||
};
|
||||
|
||||
export type AlertsReadAlertsConfigResponse = (AlertsConfig);
|
||||
@@ -1286,6 +1365,27 @@ export type DashboardsRenameDashboardData = {
|
||||
|
||||
export type DashboardsRenameDashboardResponse = (DashboardDef_Output);
|
||||
|
||||
export type FlavorsReadFlavorsResponse = (FlavorsPublic);
|
||||
|
||||
export type FlavorsCreateFlavorData = {
|
||||
requestBody: FlavorCreate;
|
||||
};
|
||||
|
||||
export type FlavorsCreateFlavorResponse = (FlavorPublic);
|
||||
|
||||
export type FlavorsUpdateFlavorData = {
|
||||
name: string;
|
||||
requestBody: FlavorUpdate;
|
||||
};
|
||||
|
||||
export type FlavorsUpdateFlavorResponse = (FlavorPublic);
|
||||
|
||||
export type FlavorsDeleteFlavorData = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type FlavorsDeleteFlavorResponse = (Message);
|
||||
|
||||
export type FlowsReadFlowsResponse = (FlowsPublic);
|
||||
|
||||
export type FlowsReadNodeTypesResponse = (Array<NodeTypeInfo>);
|
||||
@@ -1760,7 +1860,7 @@ export type UtilsHealthResponse = ({
|
||||
|
||||
export type WorkersReadWorkersResponse = (Array<WorkerInfo>);
|
||||
|
||||
export type WorkersReadResourcesResponse = (unknown);
|
||||
export type WorkersReadResourcesResponse = (ResourcesSnapshot);
|
||||
|
||||
export type WorkersIssueTokenData = {
|
||||
requestBody: TokenRequest;
|
||||
|
||||
Reference in New Issue
Block a user