mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
parent
117ec901da
commit
8046b5035d
12 changed files with 170 additions and 30 deletions
|
|
@ -110,11 +110,10 @@ pveum user permissions <user>@pam
|
|||
At minimum, ensure the user/token has read access for inventory and metrics:
|
||||
|
||||
- `Sys.Audit`
|
||||
- `VM.Monitor`
|
||||
- `VM.GuestAgent.Audit` and `VM.GuestAgent.FileRead` when available
|
||||
- `VM.Monitor` only as a legacy fallback on older Proxmox 8 systems
|
||||
- `Datastore.Audit`
|
||||
|
||||
For VM disk usage via QEMU guest agent, also ensure `VM.GuestAgent.Audit` (PVE 9+).
|
||||
|
||||
### Recovery Mode
|
||||
If you are completely locked out, you can trigger a recovery token from localhost:
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ Monitor actual disk usage inside your VMs using the QEMU Guest Agent.
|
|||
## ⚙️ Requirements
|
||||
|
||||
* **QEMU Guest Agent**: Must be installed and running inside the VM.
|
||||
* **Proxmox Permissions**: `VM.Monitor` (Proxmox 8) or `VM.GuestAgent.Audit` (Proxmox 9+).
|
||||
* **Proxmox Permissions**: `VM.GuestAgent.Audit` plus `VM.GuestAgent.FileRead` when available, with `VM.Monitor` only as a legacy fallback on older Proxmox 8 systems.
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
| :--- | :--- |
|
||||
| **Disk shows "-"** | Hover over the dash for details. Common causes: Agent not running, disabled in config, or permission denied. |
|
||||
| **Permission Denied** | Ensure your Proxmox token/user has `VM.GuestAgent.Audit` (PVE 9+) or `VM.Monitor` (PVE 8). |
|
||||
| **Permission Denied** | Ensure your Proxmox token/user has `VM.GuestAgent.Audit` plus `VM.GuestAgent.FileRead` where available, or `VM.Monitor` on older Proxmox 8 systems. |
|
||||
| **Agent Timeout** | Increase the guest-agent filesystem timeout if network or guest responsiveness is slow: `GUEST_AGENT_FSINFO_TIMEOUT=30s`. |
|
||||
| **Windows VMs** | Ensure the **QEMU Guest Agent** service is running in Windows Services. |
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function DiskList(props: DiskListProps) {
|
|||
case 'agent-timeout':
|
||||
return 'Guest agent timeout. Agent may need to be restarted.';
|
||||
case 'permission-denied':
|
||||
return 'Permission denied. Check that your Pulse user/token has VM.Monitor permission (PVE 8) or VM.GuestAgent.Audit permission (PVE 9).';
|
||||
return 'Permission denied. Check that your Pulse user/token has VM.GuestAgent.Audit and VM.GuestAgent.FileRead permissions, or legacy VM.Monitor on older Proxmox 8 systems.';
|
||||
case 'agent-disabled':
|
||||
return 'Guest agent is disabled in VM configuration. Enable it in VM Options.';
|
||||
case 'no-filesystems':
|
||||
|
|
|
|||
|
|
@ -673,7 +673,7 @@ export function GuestRow(props: GuestRowProps) {
|
|||
case 'agent-timeout':
|
||||
return 'Guest agent timeout. Agent may need to be restarted.';
|
||||
case 'permission-denied':
|
||||
return 'Permission denied. Check that your Pulse user/token has VM.Monitor permission (PVE 8) or VM.GuestAgent.Audit permission (PVE 9).';
|
||||
return 'Permission denied. Check that your Pulse user/token has VM.GuestAgent.Audit and VM.GuestAgent.FileRead permissions, or legacy VM.Monitor on older Proxmox 8 systems.';
|
||||
case 'agent-disabled':
|
||||
return 'Guest agent is disabled in VM configuration. Enable it in VM Options.';
|
||||
case 'no-filesystems':
|
||||
|
|
|
|||
|
|
@ -1160,7 +1160,7 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
|||
type="button"
|
||||
onClick={async () => {
|
||||
const cmd =
|
||||
'pveum aclmod / -user pulse-monitor@pam -role PVEAuditor && if pveum role list 2>/dev/null | grep -q "VM.Monitor" || pveum role add TestMonitor -privs VM.Monitor 2>/dev/null; then pveum role delete TestMonitor 2>/dev/null; pveum role delete PulseMonitor 2>/dev/null; pveum role add PulseMonitor -privs VM.Monitor; pveum aclmod / -user pulse-monitor@pam -role PulseMonitor; fi';
|
||||
'pveum aclmod / -user pulse-monitor@pam -role PVEAuditor && EXTRA_PRIVS=(); if pveum role list 2>/dev/null | grep -q "VM.GuestAgent.Audit" || pveum role add PulseTmpGuestAudit -privs VM.GuestAgent.Audit 2>/dev/null; then EXTRA_PRIVS+=("VM.GuestAgent.Audit"); pveum role delete PulseTmpGuestAudit 2>/dev/null || true; if pveum role list 2>/dev/null | grep -q "VM.GuestAgent.FileRead" || pveum role add PulseTmpGuestFileRead -privs VM.GuestAgent.FileRead 2>/dev/null; then EXTRA_PRIVS+=("VM.GuestAgent.FileRead"); pveum role delete PulseTmpGuestFileRead 2>/dev/null || true; fi; elif pveum role list 2>/dev/null | grep -q "VM.Monitor" || pveum role add PulseTmpVMMonitor -privs VM.Monitor 2>/dev/null; then EXTRA_PRIVS+=("VM.Monitor"); pveum role delete PulseTmpVMMonitor 2>/dev/null || true; fi; if [ ${#EXTRA_PRIVS[@]} -gt 0 ]; then PRIV_STRING="$(IFS=,; echo "${EXTRA_PRIVS[*]}")"; pveum role modify PulseMonitor -privs "$PRIV_STRING" 2>/dev/null || pveum role add PulseMonitor -privs "$PRIV_STRING"; pveum aclmod / -user pulse-monitor@pam -role PulseMonitor; fi';
|
||||
if (await copyToClipboard(cmd)) {
|
||||
notificationStore.success('Command copied!');
|
||||
}
|
||||
|
|
@ -1189,7 +1189,7 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
|||
</button>
|
||||
<code class="text-gray-800 dark:text-gray-200 whitespace-pre-line">
|
||||
{
|
||||
'# Apply monitoring permissions - use built-in PVEAuditor role\npveum aclmod / -user pulse-monitor@pam -role PVEAuditor\n\n# Gather additional privileges for VM metrics\nEXTRA_PRIVS=()\n\n# Sys.Audit (Ceph, cluster status)\nif pveum role list 2>/dev/null | grep -q "Sys.Audit"; then\n EXTRA_PRIVS+=(\"Sys.Audit\")\nelse\n if pveum role add PulseTmpSysAudit -privs Sys.Audit 2>/dev/null; then\n EXTRA_PRIVS+=(\"Sys.Audit\")\n pveum role delete PulseTmpSysAudit 2>/dev/null\n fi\nfi\n\n# VM guest agent / monitor privileges\nVM_PRIV=\"\"\nif pveum role list 2>/dev/null | grep -q "VM.Monitor"; then\n VM_PRIV=\"VM.Monitor\"\nelif pveum role list 2>/dev/null | grep -q "VM.GuestAgent.Audit"; then\n VM_PRIV=\"VM.GuestAgent.Audit\"\nelse\n if pveum role add PulseTmpVMMonitor -privs VM.Monitor 2>/dev/null; then\n VM_PRIV=\"VM.Monitor\"\n pveum role delete PulseTmpVMMonitor 2>/dev/null\n elif pveum role add PulseTmpGuestAudit -privs VM.GuestAgent.Audit 2>/dev/null; then\n VM_PRIV=\"VM.GuestAgent.Audit\"\n pveum role delete PulseTmpGuestAudit 2>/dev/null\n fi\nfi\n\nif [ -n \"$VM_PRIV\" ]; then\n EXTRA_PRIVS+=(\"$VM_PRIV\")\nfi\n\nif [ ${#EXTRA_PRIVS[@]} -gt 0 ]; then\n PRIV_STRING=\"${EXTRA_PRIVS[*]}\"\n pveum role delete PulseMonitor 2>/dev/null\n pveum role add PulseMonitor -privs \"$PRIV_STRING\"\n pveum aclmod / -user pulse-monitor@pam -role PulseMonitor\nfi'
|
||||
'# Apply monitoring permissions - use built-in PVEAuditor role\npveum aclmod / -user pulse-monitor@pam -role PVEAuditor\n\n# Gather additional privileges for VM metrics\nEXTRA_PRIVS=()\n\n# Sys.Audit (Ceph, cluster status)\nif pveum role list 2>/dev/null | grep -q "Sys.Audit"; then\n EXTRA_PRIVS+=(\"Sys.Audit\")\nelse\n if pveum role add PulseTmpSysAudit -privs Sys.Audit 2>/dev/null; then\n EXTRA_PRIVS+=(\"Sys.Audit\")\n pveum role delete PulseTmpSysAudit 2>/dev/null\n fi\nfi\n\n# Prefer Proxmox guest-agent privileges; VM.Monitor is a legacy fallback.\nif pveum role list 2>/dev/null | grep -q "VM.GuestAgent.Audit" || pveum role add PulseTmpGuestAudit -privs VM.GuestAgent.Audit 2>/dev/null; then\n EXTRA_PRIVS+=(\"VM.GuestAgent.Audit\")\n pveum role delete PulseTmpGuestAudit 2>/dev/null || true\n\n if pveum role list 2>/dev/null | grep -q "VM.GuestAgent.FileRead" || pveum role add PulseTmpGuestFileRead -privs VM.GuestAgent.FileRead 2>/dev/null; then\n EXTRA_PRIVS+=(\"VM.GuestAgent.FileRead\")\n pveum role delete PulseTmpGuestFileRead 2>/dev/null || true\n fi\nelif pveum role list 2>/dev/null | grep -q "VM.Monitor" || pveum role add PulseTmpVMMonitor -privs VM.Monitor 2>/dev/null; then\n EXTRA_PRIVS+=(\"VM.Monitor\")\n pveum role delete PulseTmpVMMonitor 2>/dev/null || true\nfi\n\nif [ ${#EXTRA_PRIVS[@]} -gt 0 ]; then\n PRIV_STRING=\"$(IFS=,; echo \"${EXTRA_PRIVS[*]}\")\"\n pveum role modify PulseMonitor -privs \"$PRIV_STRING\" 2>/dev/null || pveum role add PulseMonitor -privs \"$PRIV_STRING\"\n pveum aclmod / -user pulse-monitor@pam -role PulseMonitor\nfi'
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
|
|
@ -1232,9 +1232,9 @@ export const NodeModal: Component<NodeModalProps> = (props) => {
|
|||
</div>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-xs mt-1">
|
||||
Note: PVEAuditor gives read-only API access. PulseMonitor adds
|
||||
Sys.Audit plus either VM.Monitor (PVE 8) or VM.GuestAgent.Audit
|
||||
(PVE 9+) for disk and guest metrics. PVEDatastoreAdmin on
|
||||
/storage adds backup visibility.
|
||||
Sys.Audit plus VM.GuestAgent.Audit when available, with
|
||||
VM.Monitor only as an older PVE 8 fallback. PVEDatastoreAdmin
|
||||
on /storage adds backup visibility.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -4541,8 +4541,10 @@ fi
|
|||
echo "Setting up permissions..."
|
||||
pveum aclmod / -user pulse-monitor@pam -role PVEAuditor%s
|
||||
|
||||
# Detect Proxmox version and apply appropriate permissions
|
||||
# Method 1: Try to check if VM.Monitor exists (reliable for PVE 8 and below)
|
||||
# Detect Proxmox privileges and apply appropriate permissions
|
||||
# VM.Monitor is deprecated for the Proxmox 9 upgrade path, so prefer the
|
||||
# newer guest-agent privileges whenever they are available and fall back to
|
||||
# VM.Monitor only on older PVE 8 systems.
|
||||
HAS_VM_MONITOR=false
|
||||
if pveum role list 2>/dev/null | grep -q "VM.Monitor" ||
|
||||
pveum role add TestMonitor -privs VM.Monitor 2>/dev/null; then
|
||||
|
|
@ -4550,7 +4552,7 @@ if pveum role list 2>/dev/null | grep -q "VM.Monitor" ||
|
|||
pveum role delete TestMonitor 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Detect availability of newer guest agent privileges (PVE 9+)
|
||||
# Detect availability of newer guest agent privileges
|
||||
HAS_VM_GUEST_AGENT_AUDIT=false
|
||||
if pveum role list 2>/dev/null | grep -q "VM.GuestAgent.Audit"; then
|
||||
HAS_VM_GUEST_AGENT_AUDIT=true
|
||||
|
|
@ -4561,7 +4563,7 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
# VM.GuestAgent.FileRead (PVE 9+): needed for reading /proc/meminfo
|
||||
# VM.GuestAgent.FileRead: needed for reading /proc/meminfo
|
||||
# via the guest agent for accurate memory reporting
|
||||
HAS_VM_GUEST_AGENT_FILE_READ=false
|
||||
if pveum role list 2>/dev/null | grep -q "VM.GuestAgent.FileRead"; then
|
||||
|
|
@ -4597,15 +4599,13 @@ if [ "$HAS_SYS_AUDIT" = true ]; then
|
|||
EXTRA_PRIVS+=("Sys.Audit")
|
||||
fi
|
||||
|
||||
if [ "$HAS_VM_MONITOR" = true ]; then
|
||||
# PVE 8 or below - VM.Monitor exists
|
||||
EXTRA_PRIVS+=("VM.Monitor")
|
||||
elif [ "$HAS_VM_GUEST_AGENT_AUDIT" = true ]; then
|
||||
# PVE 9+ - VM.Monitor removed, prefer VM.GuestAgent.Audit for guest data
|
||||
if [ "$HAS_VM_GUEST_AGENT_AUDIT" = true ]; then
|
||||
EXTRA_PRIVS+=("VM.GuestAgent.Audit")
|
||||
if [ "$HAS_VM_GUEST_AGENT_FILE_READ" = true ]; then
|
||||
EXTRA_PRIVS+=("VM.GuestAgent.FileRead")
|
||||
fi
|
||||
elif [ "$HAS_VM_MONITOR" = true ]; then
|
||||
EXTRA_PRIVS+=("VM.Monitor")
|
||||
fi
|
||||
|
||||
if [ ${#EXTRA_PRIVS[@]} -gt 0 ]; then
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
||||
|
|
@ -145,6 +146,39 @@ func TestPVESetupScript_ConfiguresPulseMonitorRoleSafely(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPVESetupScript_PrefersGuestAgentPrivileges(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
cfg := &config.Config{
|
||||
DataPath: tempDir,
|
||||
ConfigPath: tempDir,
|
||||
}
|
||||
|
||||
handlers := newTestConfigHandlers(t, cfg)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet,
|
||||
"/api/setup-script?type=pve&host=http://SENTINEL_HOST:8006&pulse_url=http://SENTINEL_URL:7656", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
handlers.HandleSetupScript(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200 OK, got %d (%s)", rr.Code, rr.Body.String())
|
||||
}
|
||||
|
||||
script := rr.Body.String()
|
||||
guestIdx := strings.Index(script, `if [ "$HAS_VM_GUEST_AGENT_AUDIT" = true ]; then`)
|
||||
monitorIdx := strings.Index(script, `elif [ "$HAS_VM_MONITOR" = true ]; then`)
|
||||
if guestIdx < 0 || monitorIdx < 0 {
|
||||
t.Fatalf("expected generated script to contain guest-agent preference with VM.Monitor fallback")
|
||||
}
|
||||
if guestIdx > monitorIdx {
|
||||
t.Fatalf("expected generated script to prefer VM.GuestAgent.Audit before VM.Monitor fallback")
|
||||
}
|
||||
if !containsString(script, `EXTRA_PRIVS+=("VM.GuestAgent.FileRead")`) {
|
||||
t.Fatalf("expected generated script to include VM.GuestAgent.FileRead when available")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPVESetupScript_PreservesAuthorizedKeysSymlinkAndQuotesSensorsKey(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
cfg := &config.Config{
|
||||
|
|
|
|||
|
|
@ -1349,7 +1349,7 @@ func (r *Router) checkVMDiskMonitoring(ctx context.Context, client *proxmox.Clie
|
|||
} else if strings.Contains(errStr, "403") || strings.Contains(errStr, "401") {
|
||||
result.Recommendations = append(result.Recommendations,
|
||||
"Ensure API token has PVEAuditor role for baseline access",
|
||||
"Add VM.GuestAgent.Audit+VM.GuestAgent.FileRead (PVE 9) or VM.Monitor (PVE 8) privileges; Pulse setup adds these via the PulseMonitor role",
|
||||
"Add VM.GuestAgent.Audit+VM.GuestAgent.FileRead when available, or VM.Monitor only on older Proxmox 8 systems; Pulse setup adds these via the PulseMonitor role",
|
||||
"Include Sys.Audit when available for Ceph metrics",
|
||||
)
|
||||
} else {
|
||||
|
|
@ -1379,7 +1379,7 @@ func (r *Router) checkVMDiskMonitoring(ctx context.Context, client *proxmox.Clie
|
|||
result.TestResult = "Permission denied accessing guest agent"
|
||||
result.Recommendations = append(result.Recommendations,
|
||||
"Ensure API token has PVEAuditor role for baseline access",
|
||||
"Add VM.GuestAgent.Audit+VM.GuestAgent.FileRead (PVE 9) or VM.Monitor (PVE 8) privileges; Pulse setup adds these via the PulseMonitor role",
|
||||
"Add VM.GuestAgent.Audit+VM.GuestAgent.FileRead when available, or VM.Monitor only on older Proxmox 8 systems; Pulse setup adds these via the PulseMonitor role",
|
||||
"Include Sys.Audit when available for Ceph metrics")
|
||||
} else if errors.Is(err, context.DeadlineExceeded) || strings.Contains(errStr, "context deadline exceeded") {
|
||||
result.TestResult = "Guest agent request timed out"
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ func (p *ProxmoxSetup) configurePVEPermissions(ctx context.Context) {
|
|||
|
||||
// Extra privileges are optional, but enable additional features:
|
||||
// - Sys.Audit: required for pending apt updates + some cluster/ceph visibility
|
||||
// - VM.Monitor (PVE 8) or VM.GuestAgent.Audit+VM.GuestAgent.FileRead (PVE 9+): guest agent data
|
||||
// - VM.GuestAgent.Audit+VM.GuestAgent.FileRead (preferred) or legacy VM.Monitor: guest agent data
|
||||
// - Datastore.Audit: improved storage visibility
|
||||
var extraPrivs []string
|
||||
|
||||
|
|
@ -191,15 +191,15 @@ func (p *ProxmoxSetup) configurePVEPermissions(ctx context.Context) {
|
|||
extraPrivs = append(extraPrivs, "Sys.Audit")
|
||||
}
|
||||
|
||||
if p.probePVEPrivilege(ctx, "VM.Monitor") {
|
||||
extraPrivs = append(extraPrivs, "VM.Monitor")
|
||||
} else if p.probePVEPrivilege(ctx, "VM.GuestAgent.Audit") {
|
||||
if p.probePVEPrivilege(ctx, "VM.GuestAgent.Audit") {
|
||||
extraPrivs = append(extraPrivs, "VM.GuestAgent.Audit")
|
||||
// VM.GuestAgent.FileRead is needed for reading /proc/meminfo via the
|
||||
// guest agent file-read endpoint (accurate memory excluding buff/cache).
|
||||
if p.probePVEPrivilege(ctx, "VM.GuestAgent.FileRead") {
|
||||
extraPrivs = append(extraPrivs, "VM.GuestAgent.FileRead")
|
||||
}
|
||||
} else if p.probePVEPrivilege(ctx, "VM.Monitor") {
|
||||
extraPrivs = append(extraPrivs, "VM.Monitor")
|
||||
}
|
||||
|
||||
if p.probePVEPrivilege(ctx, "Datastore.Audit") {
|
||||
|
|
|
|||
|
|
@ -554,6 +554,113 @@ func TestProxmoxSetup_PVEPrivilegeProbe_FallsBackToGuestAgentAudit(t *testing.T)
|
|||
}
|
||||
}
|
||||
|
||||
func TestProxmoxSetup_PVEPrivilegeProbe_PrefersGuestAgentAudit(t *testing.T) {
|
||||
mc := &mockCollector{}
|
||||
var pulseMonitorPrivs string
|
||||
|
||||
mc.commandCombinedOutputFn = func(ctx context.Context, name string, arg ...string) (string, error) {
|
||||
if name != "pveum" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(arg) >= 5 && arg[0] == "role" && (arg[1] == "modify" || arg[1] == "add") && arg[2] == proxmoxMonitorRole {
|
||||
for i := 0; i < len(arg)-1; i++ {
|
||||
if arg[i] == "-privs" {
|
||||
pulseMonitorPrivs = arg[i+1]
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(arg) > 2 && arg[0] == "user" && arg[1] == "token" && arg[2] == "add" {
|
||||
return "│ value │ my-token │", nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
p := &ProxmoxSetup{
|
||||
logger: zerolog.Nop(),
|
||||
collector: mc,
|
||||
}
|
||||
|
||||
tokenID, tokenValue, err := p.setupPVEToken(context.Background(), "pulse-test")
|
||||
if err != nil {
|
||||
t.Fatalf("setupPVEToken returned error: %v", err)
|
||||
}
|
||||
if tokenID == "" || tokenValue == "" {
|
||||
t.Fatalf("expected tokenID and tokenValue to be set, got tokenID=%q tokenValue=%q", tokenID, tokenValue)
|
||||
}
|
||||
|
||||
if !strings.Contains(pulseMonitorPrivs, "VM.GuestAgent.Audit") {
|
||||
t.Fatalf("expected PulseMonitor privileges to include VM.GuestAgent.Audit, got %q", pulseMonitorPrivs)
|
||||
}
|
||||
if !strings.Contains(pulseMonitorPrivs, "VM.GuestAgent.FileRead") {
|
||||
t.Fatalf("expected PulseMonitor privileges to include VM.GuestAgent.FileRead, got %q", pulseMonitorPrivs)
|
||||
}
|
||||
if strings.Contains(pulseMonitorPrivs, "VM.Monitor") {
|
||||
t.Fatalf("did not expect PulseMonitor privileges to include deprecated VM.Monitor when guest-agent privileges are available, got %q", pulseMonitorPrivs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxmoxSetup_PVEPrivilegeProbe_UsesLegacyVMMonitorFallback(t *testing.T) {
|
||||
mc := &mockCollector{}
|
||||
var pulseMonitorPrivs string
|
||||
|
||||
mc.commandCombinedOutputFn = func(ctx context.Context, name string, arg ...string) (string, error) {
|
||||
if name != "pveum" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(arg) >= 5 && arg[0] == "role" && arg[1] == "add" && strings.HasPrefix(arg[2], "PulseTmpPrivCheck_") {
|
||||
for i := 0; i < len(arg)-1; i++ {
|
||||
if arg[i] == "-privs" {
|
||||
priv := arg[i+1]
|
||||
if priv == "VM.GuestAgent.Audit" || priv == "VM.GuestAgent.FileRead" {
|
||||
return "", fmt.Errorf("unknown privilege")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(arg) >= 5 && arg[0] == "role" && (arg[1] == "modify" || arg[1] == "add") && arg[2] == proxmoxMonitorRole {
|
||||
for i := 0; i < len(arg)-1; i++ {
|
||||
if arg[i] == "-privs" {
|
||||
pulseMonitorPrivs = arg[i+1]
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(arg) > 2 && arg[0] == "user" && arg[1] == "token" && arg[2] == "add" {
|
||||
return "│ value │ my-token │", nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
p := &ProxmoxSetup{
|
||||
logger: zerolog.Nop(),
|
||||
collector: mc,
|
||||
}
|
||||
|
||||
tokenID, tokenValue, err := p.setupPVEToken(context.Background(), "pulse-test")
|
||||
if err != nil {
|
||||
t.Fatalf("setupPVEToken returned error: %v", err)
|
||||
}
|
||||
if tokenID == "" || tokenValue == "" {
|
||||
t.Fatalf("expected tokenID and tokenValue to be set, got tokenID=%q tokenValue=%q", tokenID, tokenValue)
|
||||
}
|
||||
|
||||
if !strings.Contains(pulseMonitorPrivs, "VM.Monitor") {
|
||||
t.Fatalf("expected PulseMonitor privileges to include legacy VM.Monitor fallback, got %q", pulseMonitorPrivs)
|
||||
}
|
||||
if strings.Contains(pulseMonitorPrivs, "VM.GuestAgent.Audit") {
|
||||
t.Fatalf("did not expect VM.GuestAgent.Audit when privilege probe fails, got %q", pulseMonitorPrivs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxmoxSetup_RunAll(t *testing.T) {
|
||||
mc := &mockCollector{}
|
||||
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ func (m *Monitor) pollVMsWithNodes(ctx context.Context, instanceName string, clu
|
|||
Str("instance", instanceName).
|
||||
Str("vm", vm.Name).
|
||||
Int("vmid", vm.VMID).
|
||||
Msg("Permission denied accessing guest agent. Verify Pulse user has VM.Monitor (PVE 8) or VM.GuestAgent.Audit+VM.GuestAgent.FileRead (PVE 9) permissions")
|
||||
Msg("Permission denied accessing guest agent. Verify Pulse user has VM.GuestAgent.Audit+VM.GuestAgent.FileRead permissions, or legacy VM.Monitor on older Proxmox 8 systems")
|
||||
} else if strings.Contains(errStr, "500") {
|
||||
// Generic 500 error without clear indicators - likely agent unavailable
|
||||
// Refs #596: Proxmox returns 500 errors when guest agent isn't installed/running
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ Next steps:
|
|||
• If the guest agent is disabled, enable it in the VM Options tab (set "QEMU Guest Agent" to "Enabled").
|
||||
• Inside the guest OS, ensure the qemu-guest-agent service is installed, running, and has access to disk information.
|
||||
• Verify the pulse_monitor@pam user (or your service account) has proper permissions:
|
||||
- Proxmox 9: VM.GuestAgent.Audit privilege (Pulse setup adds via PulseMonitor role)
|
||||
- Proxmox 8: VM.Monitor privilege (Pulse setup adds via PulseMonitor role)
|
||||
- Preferred: VM.GuestAgent.Audit plus VM.GuestAgent.FileRead when available (Pulse setup adds via PulseMonitor role)
|
||||
- Legacy fallback: VM.Monitor on older Proxmox 8 systems
|
||||
- Sys.Audit is recommended for Ceph metrics and included when available
|
||||
- Both API tokens and passwords work fine for guest agent access
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue