Fix cluster nodes modal to show per-worker driver selects

- Show individual select forms for every worker in the driver modal
- Update API to handle per-worker driver selection for local nodes
- Maintain compatibility with existing backend switching logic
parent a7d2d90e
......@@ -226,94 +226,62 @@ function openDriverModal(hostname, token, hostnameValue) {
const container = document.getElementById('workerDriverSelections');
container.innerHTML = '';
// Handle local vs remote nodes differently
if (node.is_local) {
// For local node, use single driver selection for all workers
// Get GPU-requiring workers (analysis_cuda, training_cuda, analysis_rocm, training_rocm)
const gpuWorkers = [];
if (node.workers && node.workers.length > 0) {
node.workers.forEach(worker => {
if (worker.backend !== 'cpu') {
gpuWorkers.push(worker);
}
});
}
if (gpuWorkers.length === 0) {
container.innerHTML = '<p>No GPU-requiring workers found on this node.</p>';
return;
}
// Create form fields for each GPU worker
gpuWorkers.forEach(worker => {
const workerDiv = document.createElement('div');
workerDiv.className = 'form-group';
// Get available GPU backends for this node (exclude CPU for GPU workers)
const availableGpuBackends = node.available_backends.filter(b => b !== 'cpu');
// If no GPU backends available, skip this worker or show warning
if (availableGpuBackends.length === 0) {
container.innerHTML = '<p>No GPU backends available for local workers.</p>';
workerDiv.innerHTML = `
<label>${worker.type} (${worker.backend.toUpperCase()}) Worker:</label>
<p style="color: #dc2626;">No GPU backends available for this worker</p>
`;
container.appendChild(workerDiv);
return;
}
// Create select with only available GPU backends
let optionsHtml = '';
availableGpuBackends.forEach(backend => {
optionsHtml += `<option value="${backend}">${backend.toUpperCase()}</option>`;
});
container.innerHTML = `
<div class="form-group">
<label for="driver">Driver for all local workers:</label>
<select id="driver" name="driver">
${optionsHtml}
</select>
</div>
workerDiv.innerHTML = `
<label for="worker_${worker.type}_${worker.backend}_driver">${worker.type} (${worker.backend.toUpperCase()}) Worker:</label>
<select id="worker_${worker.type}_${worker.backend}_driver" name="worker_${worker.type}_${worker.backend}_driver">
${optionsHtml}
</select>
`;
container.appendChild(workerDiv);
// Set current preference or default to first available
const select = document.getElementById('driver');
// For local, default to the first GPU backend available
select.value = availableGpuBackends[0];
} else {
// For remote nodes, show worker-specific selections
// Get GPU-requiring workers (analysis_cuda, training_cuda, analysis_rocm, training_rocm)
const gpuWorkers = [];
if (node.workers && node.workers.length > 0) {
node.workers.forEach(worker => {
if (worker.backend !== 'cpu') {
gpuWorkers.push(worker);
}
});
}
if (gpuWorkers.length === 0) {
container.innerHTML = '<p>No GPU-requiring workers found on this node.</p>';
return;
const select = workerDiv.querySelector('select');
if (availableGpuBackends.includes(worker.backend)) {
select.value = worker.backend;
} else {
// Default to first available GPU backend
select.value = availableGpuBackends[0];
}
// Create form fields for each GPU worker
gpuWorkers.forEach(worker => {
const workerDiv = document.createElement('div');
workerDiv.className = 'form-group';
// Get available GPU backends for this node (exclude CPU for GPU workers)
const availableGpuBackends = node.available_backends.filter(b => b !== 'cpu');
// If no GPU backends available, skip this worker or show warning
if (availableGpuBackends.length === 0) {
workerDiv.innerHTML = `
<label>${worker.type} (${worker.backend.toUpperCase()}) Worker:</label>
<p style="color: #dc2626;">No GPU backends available for this worker</p>
`;
container.appendChild(workerDiv);
return;
}
// Create select with only available GPU backends
let optionsHtml = '';
availableGpuBackends.forEach(backend => {
optionsHtml += `<option value="${backend}">${backend.toUpperCase()}</option>`;
});
workerDiv.innerHTML = `
<label for="worker_${worker.type}_${worker.backend}_driver">${worker.type} (${worker.backend.toUpperCase()}) Worker:</label>
<select id="worker_${worker.type}_${worker.backend}_driver" name="worker_${worker.type}_${worker.backend}_driver">
${optionsHtml}
</select>
`;
container.appendChild(workerDiv);
// Set current preference or default to first available
const select = workerDiv.querySelector('select');
if (availableGpuBackends.includes(worker.backend)) {
select.value = worker.backend;
} else {
// Default to first available GPU backend
select.value = availableGpuBackends[0];
}
});
}
});
document.getElementById('driverModal').style.display = 'block';
}
......
......@@ -730,10 +730,15 @@ def api_set_client_driver():
# Handle local workers
if token == 'local':
# For local workers, expect a single driver for all workers
driver = request.form.get('driver')
if not driver or driver not in ['cuda', 'rocm', 'cpu']:
return {'success': False, 'error': 'Invalid driver - only CUDA, ROCm, and CPU are supported'}, 400
# For local workers, parse worker-specific drivers but use the same backend for all
driver = None
for key, value in request.form.items():
if key.startswith('worker_') and key.endswith('_driver'):
if value in ['cuda', 'rocm', 'cpu']:
driver = value
break # Use the first valid driver found
if not driver:
return {'success': False, 'error': 'No valid driver found for local workers'}, 400
success = switch_local_worker_backends(driver)
return {'success': success}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment