Detect unsupported quota filesystems (ZFS) and hide quota UI
- _quota_supported() checks df -T /srv; returns false + reason for ZFS, tmpfs, overlay — surfaces as quota_supported/quota_reason in dashboard API - Dashboard quota section shows the reason string instead of an empty table - Quota button hidden from share rows when quota_supported is false - Prevents setquota errors for ZFS-backed Proxmox LXC containers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f4943b6b6d
commit
af315b37b2
3 files changed
+29
-6
No files matched your search
@@ -613,6 +613,20 @@ def _shares_with_usage():
|
||||
return result
|
||||
|
||||
|
||||
def _quota_supported():
|
||||
"""Return (supported: bool, reason: str). Checks filesystem type under /srv."""
|
||||
r = subprocess.run(['df', '-T', '/srv'], capture_output=True, text=True)
|
||||
for line in r.stdout.splitlines()[1:]:
|
||||
parts = line.split()
|
||||
if len(parts) >= 2:
|
||||
fstype = parts[1].lower()
|
||||
if fstype == 'zfs':
|
||||
return False, 'ZFS storage detected — Linux disk quotas are not available inside a ZFS-backed container. Manage per-dataset quotas from the Proxmox host with: zfs set quota=10G <dataset>'
|
||||
if fstype in ('tmpfs', 'overlay', 'aufs'):
|
||||
return False, f'{fstype} filesystem does not support disk quotas'
|
||||
return True, ''
|
||||
|
||||
|
||||
def _quota_usage():
|
||||
try:
|
||||
r = subprocess.run(['repquota', '-u', '/srv'], capture_output=True, text=True, timeout=10)
|
||||
@@ -649,11 +663,14 @@ def _quota_usage():
|
||||
@app.route('/api/dashboard', methods=['GET'])
|
||||
@require_auth
|
||||
def get_dashboard():
|
||||
quota_ok, quota_reason = _quota_supported()
|
||||
return jsonify({
|
||||
'status': _daemon_status(),
|
||||
'disk': _disk_usage(),
|
||||
'shares': _shares_with_usage(),
|
||||
'quotas': _quota_usage(),
|
||||
'quotas': _quota_usage() if quota_ok else [],
|
||||
'quota_supported': quota_ok,
|
||||
'quota_reason': quota_reason,
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -36,8 +36,14 @@ async function loadDashboard() {
|
||||
</tr>`).join('');
|
||||
}
|
||||
|
||||
// Persist quota support flag for shares.js to read
|
||||
window._quotaSupported = d.quota_supported !== false;
|
||||
window._quotaReason = d.quota_reason || '';
|
||||
|
||||
const qb = document.getElementById('dashQuotasBody');
|
||||
if (!d.quotas?.length) {
|
||||
if (!d.quota_supported) {
|
||||
qb.innerHTML = `<tr><td colspan="4" style="color:var(--muted); font-size:.87rem">${esc(d.quota_reason)}</td></tr>`;
|
||||
} else if (!d.quotas?.length) {
|
||||
qb.innerHTML = `<tr><td colspan="4" style="color:var(--muted)">No quotas set.</td></tr>`;
|
||||
} else {
|
||||
qb.innerHTML = d.quotas.map(q => {
|
||||
|
||||
+4
-4
@@ -23,8 +23,8 @@ async function loadShares() {
|
||||
<td>
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openEditModal('smb',${shareJson})">Edit</button>
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openQuotaModal('smb',${nameJson},${pathJson},${usersJson})">Quota</button>
|
||||
${window._quotaSupported !== false ? `<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openQuotaModal('smb',${nameJson},${pathJson},${usersJson})">Quota</button>` : ''}
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="toggleShare('smb',${nameJson},${!enabled})">${enabled ? 'Disable' : 'Enable'}</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
@@ -50,8 +50,8 @@ async function loadShares() {
|
||||
<td>
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openEditModal('nfs',${shareJson})">Edit</button>
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openQuotaModal('nfs',null,${pathJson},null)">Quota</button>
|
||||
${window._quotaSupported !== false ? `<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openQuotaModal('nfs',null,${pathJson},null)">Quota</button>` : ''}
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="toggleShare('nfs',${pathJson},${!enabled})">${enabled ? 'Disable' : 'Enable'}</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
|
||||
Reference in new issue
Block a user