fix: replace storage.cfg parser with pvesm status for reliable pool discovery; add Custom... dropdown option
This commit is contained in:
parent
e53fcacaa6
commit
2e0c6cdba7
4 changed files with 58 additions and 33 deletions
|
|
@ -97,42 +97,34 @@ def health() -> HealthResponse:
|
|||
@app.get(f"{API_PREFIX}/storage/pools")
|
||||
def storage_pools():
|
||||
"""Return available Proxmox storage pools that support VM disk images."""
|
||||
pools = []
|
||||
result = []
|
||||
try:
|
||||
cfg = Path("/etc/pve/storage.cfg")
|
||||
if cfg.exists():
|
||||
lines = cfg.read_text()
|
||||
current = {}
|
||||
for line in lines.split("\n"):
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
if ":" in stripped and not line.startswith(" "):
|
||||
if current and current.get("name"):
|
||||
pools.append(current)
|
||||
parts = stripped.split(":", 1)
|
||||
stype = parts[0].strip()
|
||||
sname = parts[1].strip() if len(parts) > 1 else ""
|
||||
current = {"name": sname, "type": stype}
|
||||
elif line.startswith(" ") and current:
|
||||
if " " in stripped:
|
||||
key, val = stripped.split(" ", 1)
|
||||
current[key] = val
|
||||
if current and current.get("name"):
|
||||
pools.append(current)
|
||||
import subprocess
|
||||
out = subprocess.run(["pvesm", "status"], capture_output=True, text=True, timeout=10)
|
||||
for line in out.stdout.split("\n")[1:]:
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
name, stype, active = parts[0], parts[1], parts[2]
|
||||
if active != "active":
|
||||
continue
|
||||
if stype not in ("lvmthin", "zfspool", "rbd", "dir", "nfs"):
|
||||
continue
|
||||
# Verify content includes images
|
||||
content = ""
|
||||
try:
|
||||
cp = subprocess.run(["pvesm", "config", name], capture_output=True, text=True, timeout=5)
|
||||
for cl in cp.stdout.split("\n"):
|
||||
if cl.strip().startswith("content"):
|
||||
content = cl.strip().split(None, 1)[1] if len(cl.strip().split(None, 1)) > 1 else ""
|
||||
except Exception:
|
||||
pass
|
||||
if "images" not in content:
|
||||
continue
|
||||
result.append({"name": name, "type": stype})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
result = []
|
||||
for p in pools:
|
||||
if p.get("type") in ("lvmthin", "zfspool", "rbd", "dir"):
|
||||
content = p.get("content", "")
|
||||
if "images" in content:
|
||||
result.append({
|
||||
"name": p["name"],
|
||||
"type": p["type"],
|
||||
})
|
||||
|
||||
if not result:
|
||||
result.append({"name": "local-lvm", "type": "lvmthin"})
|
||||
|
||||
|
|
|
|||
|
|
@ -65,11 +65,14 @@
|
|||
<div class="pve-form-group">
|
||||
<label>Storage Pool</label>
|
||||
{% if storage_pools %}
|
||||
<select name="target_storage" class="pve-select">
|
||||
<select name="target_storage" class="pve-select" id="storage-select">
|
||||
{% for pool in storage_pools %}
|
||||
<option value="{{ pool.name }}">{{ pool.name }} ({{ pool.type }})</option>
|
||||
{% endfor %}
|
||||
<option value="__custom__">Custom...</option>
|
||||
</select>
|
||||
<input type="text" class="pve-input pve-hidden" id="storage-custom"
|
||||
placeholder="Enter pool name" style="margin-top:0.3rem;">
|
||||
{% else %}
|
||||
<input type="text" name="target_storage" class="pve-input" value="local-lvm">
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -314,6 +314,21 @@ function initConfirmForm() {
|
|||
this.value === 'false' ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
const storageSel = document.getElementById('storage-select');
|
||||
const storageCustom = document.getElementById('storage-custom');
|
||||
if (storageSel && storageCustom) {
|
||||
storageSel.addEventListener('change', function() {
|
||||
if (this.value === '__custom__') {
|
||||
storageCustom.classList.remove('pve-hidden');
|
||||
storageCustom.name = 'target_storage';
|
||||
this.name = '';
|
||||
} else {
|
||||
storageCustom.classList.add('pve-hidden');
|
||||
storageCustom.name = 'target_storage_custom';
|
||||
this.name = 'target_storage';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function submitJob(e) {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,21 @@ function scpInitConfirmForm() {
|
|||
this.value === 'false' ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
const storageSel = document.getElementById('storage-select');
|
||||
const storageCustom = document.getElementById('storage-custom');
|
||||
if (storageSel && storageCustom) {
|
||||
storageSel.addEventListener('change', function() {
|
||||
if (this.value === '__custom__') {
|
||||
storageCustom.classList.remove('pve-hidden');
|
||||
storageCustom.name = 'target_storage';
|
||||
this.name = '';
|
||||
} else {
|
||||
storageCustom.classList.add('pve-hidden');
|
||||
storageCustom.name = 'target_storage_custom';
|
||||
this.name = 'target_storage';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function scpSubmitJob(e) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue