Skip to content

Commit d3275d1

Browse files
authored
Merge pull request #405 from Dstack-TEE/vmm-ui-v1
vmm: Improve web UI
2 parents 45c9bdb + a52fe84 commit d3275d1

File tree

7 files changed

+254
-51
lines changed

7 files changed

+254
-51
lines changed

ra-rpc/src/openapi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,7 @@ fn build_swagger_ui_html(spec_url: &str, cfg: &SwaggerUiConfig) -> String {
10351035
window.ui = SwaggerUIBundle({{
10361036
url: '{spec}',
10371037
dom_id: '#swagger-ui',
1038+
deepLinking: true,
10381039
presets: [
10391040
SwaggerUIBundle.presets.apis,
10401041
SwaggerUIStandalonePreset

vmm/src/console_v1.html

Lines changed: 179 additions & 23 deletions
Large diffs are not rendered by default.

vmm/ui/src/components/CreateVmDialog.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ const CreateVmDialogComponent = {
109109
<textarea id="preLaunchScript" v-model="form.preLaunchScript" placeholder="Optional script executed before launch" rows="6"></textarea>
110110
</div>
111111
112+
<div class="form-group full-width">
113+
<label for="userConfig">User Config</label>
114+
<textarea id="userConfig" v-model="form.user_config" placeholder="Optional user config placed at /dstack/.user-config in the CVM"></textarea>
115+
</div>
116+
112117
<div class="form-group full-width" v-if="availableGpus.length > 0">
113118
<gpu-config-editor
114119
:available-gpus="availableGpus"
@@ -118,11 +123,6 @@ const CreateVmDialogComponent = {
118123
/>
119124
</div>
120125
121-
<div class="form-group full-width">
122-
<label for="userConfig">User Config</label>
123-
<textarea id="userConfig" v-model="form.user_config" placeholder="Optional user config placed at /dstack/.user-config in the CVM"></textarea>
124-
</div>
125-
126126
<div class="form-group full-width">
127127
<label>Features</label>
128128
<div class="feature-checkboxes">

vmm/ui/src/components/UpdateVmDialog.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,6 @@ const UpdateVmDialogComponent = {
7373
</select>
7474
</div>
7575
76-
<div class="form-group" v-if="availableGpus.length > 0">
77-
<div class="checkbox-grid">
78-
<label><input type="checkbox" v-model="dialog.updateGpuConfig"> Update GPU configuration</label>
79-
</div>
80-
<div v-if="dialog.updateGpuConfig">
81-
<gpu-config-editor
82-
:available-gpus="availableGpus"
83-
:allow-attach-all="allowAttachAllGpus"
84-
v-model:gpus="dialog.selectedGpus"
85-
v-model:attach-all="dialog.attachAllGpus"
86-
/>
87-
</div>
88-
</div>
89-
9076
<div class="checkbox-grid">
9177
<label><input type="checkbox" v-model="dialog.updateCompose"> Update App Compose</label>
9278
</div>
@@ -122,6 +108,20 @@ const UpdateVmDialogComponent = {
122108
</div>
123109
</div>
124110
111+
<div class="form-group" v-if="availableGpus.length > 0">
112+
<div class="checkbox-grid">
113+
<label><input type="checkbox" v-model="dialog.updateGpuConfig"> Update GPU configuration</label>
114+
</div>
115+
<div v-if="dialog.updateGpuConfig">
116+
<gpu-config-editor
117+
:available-gpus="availableGpus"
118+
:allow-attach-all="allowAttachAllGpus"
119+
v-model:gpus="dialog.selectedGpus"
120+
v-model:attach-all="dialog.attachAllGpus"
121+
/>
122+
</div>
123+
</div>
124+
125125
<div class="form-group full-width" v-if="portMappingEnabled">
126126
<port-mapping-editor :ports="dialog.ports" />
127127
</div>

vmm/ui/src/composables/useVmManager.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,19 @@ fi
391391
}
392392
}
393393

394-
function configGpu(form: { attachAllGpus: boolean; selectedGpus: string[] }): VmmTypes.IGpuConfig | undefined {
394+
function configGpu(form: { attachAllGpus: boolean; selectedGpus: string[] }, isUpdate: boolean = false): VmmTypes.IGpuConfig | undefined {
395395
if (form.attachAllGpus) {
396396
return { attach_mode: 'all' };
397397
}
398+
// For updates, always return a config when GPUs are being explicitly updated
399+
// Empty array means no GPUs should be attached
400+
if (isUpdate) {
401+
return {
402+
attach_mode: 'listed',
403+
gpus: (form.selectedGpus || []).map((slot: string) => ({ slot })),
404+
};
405+
}
406+
// For creation, return undefined if no GPUs are selected
398407
if (form.selectedGpus && form.selectedGpus.length > 0) {
399408
return {
400409
attach_mode: 'listed',
@@ -1042,7 +1051,7 @@ type CreateVmPayloadSource = {
10421051
body.user_config = updated.user_config;
10431052
body.update_ports = true;
10441053
body.ports = normalizePorts(updated.ports);
1045-
body.gpus = updateDialog.value.updateGpuConfig ? configGpu(updated) : undefined;
1054+
body.gpus = updateDialog.value.updateGpuConfig ? configGpu(updated, true) : undefined;
10461055

10471056
await vmmRpc.updateVm(body);
10481057
updateDialog.value.encryptedEnvs = [];

vmm/ui/src/styles/main.css

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,14 @@ h1, h2, h3, h4, h5, h6 {
324324

325325
.vm-table {
326326
max-width: 900px;
327+
width: 900px;
327328
margin: 0 auto 24px;
328329
padding: 0 24px;
329330
}
330331

331332
.vm-table-header {
332333
display: grid;
333-
grid-template-columns: 24px 1fr 140px 120px 280px 60px;
334+
grid-template-columns: 24px 2fr 100px 120px 180px 60px;
334335
gap: 16px;
335336
padding: 12px 16px;
336337
background: var(--color-bg-primary);
@@ -354,7 +355,7 @@ h1, h2, h3, h4, h5, h6 {
354355

355356
.vm-row-main {
356357
display: grid;
357-
grid-template-columns: 24px 1fr 140px 120px 280px 60px;
358+
grid-template-columns: 24px 2fr 100px 120px 180px 60px;
358359
gap: 16px;
359360
padding: 16px;
360361
align-items: center;
@@ -554,6 +555,29 @@ h1, h2, h3, h4, h5, h6 {
554555
cursor: help;
555556
}
556557

558+
.gpu-chip-list {
559+
display: flex;
560+
flex-wrap: wrap;
561+
gap: 6px;
562+
}
563+
564+
.gpu-chip {
565+
font-size: 12px;
566+
line-height: 1.4;
567+
padding: 4px 10px;
568+
border-radius: 999px;
569+
border: 1px solid var(--color-border);
570+
background: var(--color-bg-primary);
571+
color: var(--color-text-secondary);
572+
white-space: nowrap;
573+
}
574+
575+
.gpu-chip--all {
576+
font-weight: 600;
577+
color: var(--color-text-primary);
578+
border-style: dashed;
579+
}
580+
557581
.port-mappings {
558582
display: flex;
559583
flex-direction: column;

vmm/ui/src/templates/app.html

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,25 @@ <h1 class="app-title">dstack-vmm</h1>
294294
<span class="detail-label">TEE</span>
295295
<span class="detail-value">{{ vm.configuration?.no_tee ? 'Disabled' : 'Enabled' }}</span>
296296
</div>
297-
<div class="detail-item" v-if="vm.configuration?.gpus && vm.configuration.gpus.length > 0">
297+
<div class="detail-item detail-item--gpus" v-if="vm.configuration?.gpus">
298298
<span class="detail-label">GPUs</span>
299-
<div>
300-
<div v-for="gpu in vm.configuration.gpus" :key="gpu.slot" class="detail-value">
301-
{{ gpu.slot || gpu.product_id }}
302-
</div>
299+
<div v-if="vm.configuration.gpus.attach_mode === 'all'" class="gpu-chip-list">
300+
<span class="gpu-chip gpu-chip--all" title="All available GPUs and NVSwitches are attached">
301+
All GPUs
302+
</span>
303+
</div>
304+
<div v-else-if="vm.configuration.gpus?.gpus?.length" class="gpu-chip-list">
305+
<span
306+
class="gpu-chip"
307+
v-for="(gpu, index) in vm.configuration.gpus.gpus"
308+
:key="gpu.slot || gpu.product_id || index"
309+
:title="gpu.description || gpu.slot || gpu.product_id || ('GPU #' + (index + 1))"
310+
>
311+
{{ gpu.slot || gpu.product_id || ('GPU #' + (index + 1)) }}
312+
</span>
313+
</div>
314+
<div v-else class="detail-value">
315+
None
303316
</div>
304317
</div>
305318
</div>

0 commit comments

Comments
 (0)