Files
pxe-server/webapp/templates/import.html
cproudlock f4c158a5ac Fix PXE interface detection, add br-pxe bridge to test VM, network upload import
- Playbook: detect interface already configured with 10.9.100.1 before
  falling back to non-default-gateway heuristic (fixes dnsmasq binding
  to wrong NIC when multiple interfaces exist)
- test-vm.sh: auto-attach br-pxe bridge NIC if available on host
- Webapp: add network upload import via SMB share with shared driver
  deduplication and symlinks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 15:15:14 -05:00

198 lines
7.6 KiB
HTML

{% extends "base.html" %}
{% block title %}Image Import - PXE Server Manager{% endblock %}
{% block content %}
<h2 class="mb-4">Image Import</h2>
<div class="row">
<div class="col-lg-8">
<!-- Network Upload Import -->
<div class="card mb-3">
<div class="card-header">
<i class="bi bi-cloud-upload me-2"></i> Import from Network Upload
</div>
<div class="card-body">
{% if upload_sources %}
<form method="POST" id="uploadImportForm">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label for="uploadSource" class="form-label fw-semibold">Source</label>
<select class="form-select" name="source" id="uploadSource" required>
<option value="">-- Select upload source --</option>
{% for src in upload_sources %}
<option value="{{ src }}">{{ src }}</option>
{% endfor %}
</select>
<div class="form-text">
Files uploaded via SMB to <code>\\10.9.100.1\image-upload</code>
</div>
</div>
<div class="mb-3">
<label for="uploadTarget" class="form-label fw-semibold">Target Image Type</label>
<select class="form-select" name="target" id="uploadTarget" required>
<option value="">-- Select target image --</option>
{% for it in image_types %}
<option value="{{ it }}">{{ friendly_names[it] }} ({{ it }})</option>
{% endfor %}
</select>
<div class="form-text">
Content will be copied into the Deploy directory. Shared resources
(Out-of-box Drivers) are stored once and linked across all image types.
</div>
</div>
<div class="alert alert-info d-flex align-items-start" role="alert">
<i class="bi bi-info-circle-fill me-2 mt-1"></i>
<div>
<strong>Shared Drivers:</strong> Out-of-box Drivers are automatically pooled
into a shared directory and symlinked for each image type to save disk space.
</div>
</div>
<div class="alert alert-warning d-flex align-items-start" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2 mt-1"></i>
<div>
<strong>Warning:</strong> Existing files in the target Deploy directory with the
same names will be overwritten. This operation may take several minutes for large
images.
</div>
</div>
<button type="submit" class="btn btn-primary" id="uploadImportBtn">
<i class="bi bi-download me-1"></i> Start Import
</button>
</form>
{% else %}
<div class="text-center py-4">
<i class="bi bi-cloud-slash display-4 text-muted"></i>
<h5 class="mt-3 text-muted">No Upload Content Found</h5>
<p class="text-muted mb-0">
Map <code>\\10.9.100.1\image-upload</code> on your Windows PC and copy
the Deploy directory contents there.
</p>
<button class="btn btn-outline-secondary btn-sm mt-3" onclick="location.reload()">
<i class="bi bi-arrow-clockwise"></i> Refresh
</button>
</div>
{% endif %}
</div>
</div>
<!-- USB Import -->
<div class="card">
<div class="card-header">
<i class="bi bi-usb-drive me-2"></i> Import from USB Drive
</div>
<div class="card-body">
{% if usb_mounts %}
<form method="POST" id="importForm">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label for="source" class="form-label fw-semibold">Source (USB Mount Point)</label>
<select class="form-select" name="source" id="source" required>
<option value="">-- Select a mounted USB drive --</option>
{% for mount in usb_mounts %}
<option value="{{ mount }}">{{ mount }}</option>
{% endfor %}
</select>
<div class="form-text">
Select the mounted USB drive containing the WinPE deployment content.
</div>
</div>
<div class="mb-3">
<label for="target" class="form-label fw-semibold">Target Image Type</label>
<select class="form-select" name="target" id="target" required>
<option value="">-- Select target image --</option>
{% for it in image_types %}
<option value="{{ it }}">{{ friendly_names[it] }} ({{ it }})</option>
{% endfor %}
</select>
<div class="form-text">
Content will be copied into the Deploy directory for this image type.
</div>
</div>
<div class="alert alert-warning d-flex align-items-start" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2 mt-1"></i>
<div>
<strong>Warning:</strong> Existing files in the target Deploy directory with the
same names will be overwritten. This operation may take several minutes for large
images.
</div>
</div>
<button type="submit" class="btn btn-primary" id="importBtn">
<i class="bi bi-download me-1"></i> Start Import
</button>
</form>
{% else %}
<div class="text-center py-4">
<i class="bi bi-usb-plug display-4 text-muted"></i>
<h5 class="mt-3 text-muted">No USB Drives Detected</h5>
<p class="text-muted mb-0">
No mounted USB drives were found under <code>/mnt/</code> or <code>/media/</code>.<br>
Mount a USB drive and refresh this page.
</p>
<button class="btn btn-outline-secondary btn-sm mt-3" onclick="location.reload()">
<i class="bi bi-arrow-clockwise"></i> Refresh
</button>
</div>
{% endif %}
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<i class="bi bi-info-circle me-2"></i> Current Image Status
</div>
<div class="card-body p-0">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr>
<th>Image</th>
<th>Content</th>
</tr>
</thead>
<tbody>
{% for img in images %}
<tr>
<td class="small">{{ img.friendly_name }}</td>
<td>
{% if img.has_content %}
<span class="badge bg-success">Present</span>
{% else %}
<span class="badge bg-secondary">Empty</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
['importForm', 'uploadImportForm'].forEach(function(formId) {
var form = document.getElementById(formId);
if (form) {
form.addEventListener('submit', function() {
var btn = form.querySelector('button[type="submit"]');
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Importing...';
});
}
});
});
</script>
{% endblock %}