Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ra-rpc/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,7 @@ fn build_swagger_ui_html(spec_url: &str, cfg: &SwaggerUiConfig) -> String {
window.ui = SwaggerUIBundle({{
url: '{spec}',
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
Expand Down
202 changes: 179 additions & 23 deletions vmm/src/console_v1.html

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions vmm/ui/src/components/CreateVmDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ const CreateVmDialogComponent = {
<textarea id="preLaunchScript" v-model="form.preLaunchScript" placeholder="Optional script executed before launch" rows="6"></textarea>
</div>

<div class="form-group full-width">
<label for="userConfig">User Config</label>
<textarea id="userConfig" v-model="form.user_config" placeholder="Optional user config placed at /dstack/.user-config in the CVM"></textarea>
</div>

<div class="form-group full-width" v-if="availableGpus.length > 0">
<gpu-config-editor
:available-gpus="availableGpus"
Expand All @@ -118,11 +123,6 @@ const CreateVmDialogComponent = {
/>
</div>

<div class="form-group full-width">
<label for="userConfig">User Config</label>
<textarea id="userConfig" v-model="form.user_config" placeholder="Optional user config placed at /dstack/.user-config in the CVM"></textarea>
</div>

<div class="form-group full-width">
<label>Features</label>
<div class="feature-checkboxes">
Expand Down
28 changes: 14 additions & 14 deletions vmm/ui/src/components/UpdateVmDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,6 @@ const UpdateVmDialogComponent = {
</select>
</div>

<div class="form-group" v-if="availableGpus.length > 0">
<div class="checkbox-grid">
<label><input type="checkbox" v-model="dialog.updateGpuConfig"> Update GPU configuration</label>
</div>
<div v-if="dialog.updateGpuConfig">
<gpu-config-editor
:available-gpus="availableGpus"
:allow-attach-all="allowAttachAllGpus"
v-model:gpus="dialog.selectedGpus"
v-model:attach-all="dialog.attachAllGpus"
/>
</div>
</div>

<div class="checkbox-grid">
<label><input type="checkbox" v-model="dialog.updateCompose"> Update App Compose</label>
</div>
Expand Down Expand Up @@ -122,6 +108,20 @@ const UpdateVmDialogComponent = {
</div>
</div>

<div class="form-group" v-if="availableGpus.length > 0">
<div class="checkbox-grid">
<label><input type="checkbox" v-model="dialog.updateGpuConfig"> Update GPU configuration</label>
</div>
<div v-if="dialog.updateGpuConfig">
<gpu-config-editor
:available-gpus="availableGpus"
:allow-attach-all="allowAttachAllGpus"
v-model:gpus="dialog.selectedGpus"
v-model:attach-all="dialog.attachAllGpus"
/>
</div>
</div>

<div class="form-group full-width" v-if="portMappingEnabled">
<port-mapping-editor :ports="dialog.ports" />
</div>
Expand Down
13 changes: 11 additions & 2 deletions vmm/ui/src/composables/useVmManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,19 @@ fi
}
}

function configGpu(form: { attachAllGpus: boolean; selectedGpus: string[] }): VmmTypes.IGpuConfig | undefined {
function configGpu(form: { attachAllGpus: boolean; selectedGpus: string[] }, isUpdate: boolean = false): VmmTypes.IGpuConfig | undefined {
if (form.attachAllGpus) {
return { attach_mode: 'all' };
}
// For updates, always return a config when GPUs are being explicitly updated
// Empty array means no GPUs should be attached
if (isUpdate) {
return {
attach_mode: 'listed',
gpus: (form.selectedGpus || []).map((slot: string) => ({ slot })),
};
}
// For creation, return undefined if no GPUs are selected
if (form.selectedGpus && form.selectedGpus.length > 0) {
return {
attach_mode: 'listed',
Expand Down Expand Up @@ -1042,7 +1051,7 @@ type CreateVmPayloadSource = {
body.user_config = updated.user_config;
body.update_ports = true;
body.ports = normalizePorts(updated.ports);
body.gpus = updateDialog.value.updateGpuConfig ? configGpu(updated) : undefined;
body.gpus = updateDialog.value.updateGpuConfig ? configGpu(updated, true) : undefined;

await vmmRpc.updateVm(body);
updateDialog.value.encryptedEnvs = [];
Expand Down
28 changes: 26 additions & 2 deletions vmm/ui/src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,14 @@ h1, h2, h3, h4, h5, h6 {

.vm-table {
max-width: 900px;
width: 900px;
margin: 0 auto 24px;
padding: 0 24px;
}

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

.vm-row-main {
display: grid;
grid-template-columns: 24px 1fr 140px 120px 280px 60px;
grid-template-columns: 24px 2fr 100px 120px 180px 60px;
gap: 16px;
padding: 16px;
align-items: center;
Expand Down Expand Up @@ -554,6 +555,29 @@ h1, h2, h3, h4, h5, h6 {
cursor: help;
}

.gpu-chip-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}

.gpu-chip {
font-size: 12px;
line-height: 1.4;
padding: 4px 10px;
border-radius: 999px;
border: 1px solid var(--color-border);
background: var(--color-bg-primary);
color: var(--color-text-secondary);
white-space: nowrap;
}

.gpu-chip--all {
font-weight: 600;
color: var(--color-text-primary);
border-style: dashed;
}

.port-mappings {
display: flex;
flex-direction: column;
Expand Down
23 changes: 18 additions & 5 deletions vmm/ui/src/templates/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,25 @@ <h1 class="app-title">dstack-vmm</h1>
<span class="detail-label">TEE</span>
<span class="detail-value">{{ vm.configuration?.no_tee ? 'Disabled' : 'Enabled' }}</span>
</div>
<div class="detail-item" v-if="vm.configuration?.gpus && vm.configuration.gpus.length > 0">
<div class="detail-item detail-item--gpus" v-if="vm.configuration?.gpus">
<span class="detail-label">GPUs</span>
<div>
<div v-for="gpu in vm.configuration.gpus" :key="gpu.slot" class="detail-value">
{{ gpu.slot || gpu.product_id }}
</div>
<div v-if="vm.configuration.gpus.attach_mode === 'all'" class="gpu-chip-list">
<span class="gpu-chip gpu-chip--all" title="All available GPUs and NVSwitches are attached">
All GPUs
</span>
</div>
<div v-else-if="vm.configuration.gpus?.gpus?.length" class="gpu-chip-list">
<span
class="gpu-chip"
v-for="(gpu, index) in vm.configuration.gpus.gpus"
:key="gpu.slot || gpu.product_id || index"
:title="gpu.description || gpu.slot || gpu.product_id || ('GPU #' + (index + 1))"
>
{{ gpu.slot || gpu.product_id || ('GPU #' + (index + 1)) }}
</span>
</div>
<div v-else class="detail-value">
None
</div>
</div>
</div>
Expand Down
Loading