Task Handling - FreshPerf/PVE4J GitHub Wiki

Task Handling

Proxmox operations often involve long-running tasks. This guide explains how to work with tasks in PVE4J.

Understanding Proxmox Tasks

When you perform operations like starting a VM, cloning, or resizing disks, Proxmox creates a task that runs asynchronously. Each task has:

  • UPID - Unique Process ID (e.g., UPID:node:00000001:00000001:00000001:qmstart:100:root@pam:)
  • Node - The node where the task runs
  • Status - Current task status (running, stopped)
  • Exit Status - Result code (OK, or error message)

Task Response

Operations that create tasks return a PveTask object:

import fr.freshperf.pve4j.entities.PveTask;

PveTask task = proxmox.getNodes()
        .get("pve-node-01")
        .getQemu()
        .get(100)
        .start()
        .execute();

System.out.println("Task UPID: " + task.getUpid());
System.out.println("Node: " + task.getNode());

Checking Task Status

Get Current Task Status

import fr.freshperf.pve4j.entities.PveTaskStatus;

try {
    PveTask task = vm.start().execute();
    
    PveTaskStatus status = proxmox.getTaskStatus(task).execute();
    
    System.out.println("Status: " + status.getStatus());
    System.out.println("Exit Status: " + status.getExitstatus());
    System.out.println("Type: " + status.getType());
    System.out.println("User: " + status.getUser());
    System.out.println("Start Time: " + status.getStarttime());
} catch (ProxmoxAPIError | InterruptedException e) {
    e.printStackTrace();
}

Check if Task is Successful

PveTaskStatus status = proxmox.getTaskStatus(task).execute();

if (status.isSuccessful()) {
    System.out.println("Task completed successfully!");
} else if (status.isCompleted()) {
    System.err.println("Task failed: " + status.getExitstatus());
} else {
    System.out.println("Task is still running...");
}

Get Task Status by Node and UPID

PveTaskStatus status = proxmox.getTaskStatus("pve-node-01", upid).execute();

Waiting for Task Completion

Basic Wait

The simplest way to wait for a task to complete:

try {
    PveTask task = proxmox.getNodes()
            .get("pve-node-01")
            .getQemu()
            .get(100)
            .start()
            .waitForCompletion(proxmox)
            .execute();
    
    System.out.println("VM started successfully!");
} catch (ProxmoxAPIError | InterruptedException e) {
    System.err.println("Failed to start VM: " + e.getMessage());
}

Wait with Custom Polling Interval

By default, waitForCompletion() polls every second. You can customize this with taskCheckDelay():

import java.time.Duration;

// Using milliseconds
PveTask task = proxmox.getNodes()
        .get("pve-node-01")
        .getQemu()
        .get(100)
        .start()
        .waitForCompletion(proxmox)
        .taskCheckDelay(5000)  // Poll every 5 seconds
        .execute();

// Using Duration for better readability
PveTask task = proxmox.getNodes()
        .get("pve-node-01")
        .getQemu()
        .get(100)
        .cloneVm(200)
        .waitForCompletion(proxmox)
        .taskCheckDelay(Duration.ofSeconds(10))  // Long operations can use longer intervals
        .execute();

Task Timeout

Set a maximum time to wait for task completion:

PveTask task = proxmox.getNodes()
        .get("pve-node-01")
        .getQemu()
        .get(100)
        .cloneVm(200)
        .waitForCompletion(proxmox)
        .taskTimeout(Duration.ofMinutes(10))
        .taskCheckDelay(Duration.ofSeconds(2))
        .execute();

Static Wait Method

You can also wait for task completion after execution using the static helper:

import fr.freshperf.pve4j.request.ProxmoxRequest;

// Fire and forget, then wait later
PveTask task = vm.start().execute();

// Do other work...

// Wait for completion with default 1 second polling
ProxmoxRequest.waitForCompletion(proxmox, task);

// Or specify custom polling interval
ProxmoxRequest.waitForCompletion(proxmox, task, 5000);  // 5 second polling

// Or with Duration and timeout
ProxmoxRequest.waitForCompletion(proxmox, task, Duration.ofSeconds(2), Duration.ofMinutes(5));

Manual Polling (Advanced)

For complete control over the polling logic:

PveTask task = vm.start().execute();

boolean completed = false;
while (!completed) {
    Thread.sleep(5000);  // Poll every 5 seconds
    
    PveTaskStatus status = proxmox.getTaskStatus(task).execute();
    
    if (status.isCompleted()) {
        completed = true;
        
        if (status.isSuccessful()) {
            System.out.println("Task completed successfully!");
        } else {
            System.err.println("Task failed: " + status.getExitstatus());
        }
    }
}

Asynchronous Callbacks

For non-blocking operations, use onCompletion():

try {
    proxmox.getNodes()
            .get("pve-node-01")
            .getQemu()
            .get(100)
            .start()
            .onCompletion((task, status) -> {
                if (status.isSuccessful()) {
                    System.out.println("VM started successfully!");
                } else {
                    System.err.println("VM start failed: " + status.getExitstatus());
                }
            }, proxmox)
            .execute();
    
    System.out.println("Start task submitted, continuing with other work...");
    
    // Your code continues here while the task runs in the background
    
} catch (ProxmoxAPIError | InterruptedException e) {
    e.printStackTrace();
}

Task Patterns

Pattern 1: Fire and Forget

Submit a task without waiting:

PveTask task = vm.start().execute();
System.out.println("Task submitted: " + task.getUpid());
// Continue immediately

Use Case: When you don't need to know when the task completes.

Pattern 2: Blocking Wait

Wait for task to complete before continuing:

PveTask task = vm.start()
        .waitForCompletion(proxmox)
        .execute();
System.out.println("VM is now running");
// Task is guaranteed to be complete

Use Case: When you need the task to complete before proceeding.

Pattern 3: Asynchronous Callback

Submit task and handle completion asynchronously:

vm.start()
  .onCompletion((task, status) -> {
      System.out.println("Task completed!");
  }, proxmox)
  .execute();
  
System.out.println("Task running in background");
// Continue with other work

Use Case: When you want to perform other work while task runs.

Pattern 4: Multiple Tasks with Parallel Execution

import java.util.concurrent.*;

ExecutorService executor = Executors.newFixedThreadPool(5);

List<Integer> vmids = Arrays.asList(100, 101, 102, 103, 104);

List<CompletableFuture<Void>> futures = vmids.stream()
    .map(vmid -> CompletableFuture.runAsync(() -> {
        try {
            proxmox.getNodes()
                    .get("pve-node-01")
                    .getQemu()
                    .get(vmid)
                    .start()
                    .waitForCompletion(proxmox)
                    .execute();
            System.out.println("VM " + vmid + " started");
        } catch (Exception e) {
            System.err.println("Failed to start VM " + vmid + ": " + e.getMessage());
        }
    }, executor))
    .collect(Collectors.toList());

// Wait for all VMs to start
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();

Use Case: Starting multiple VMs in parallel.

Error Handling for Tasks

Task Failures

try {
    PveTask task = vm.start()
            .waitForCompletion(proxmox)
            .execute();
    
    // Check final status
    PveTaskStatus status = proxmox.getTaskStatus(task).execute();
    
    if (!status.isSuccessful()) {
        System.err.println("Task failed with exit status: " + status.getExitstatus());
    }
    
} catch (ProxmoxAPIError e) {
    System.err.println("API Error: " + e.getMessage());
    System.err.println("Status Code: " + e.getStatusCode());
    System.err.println("Response: " + e.getResponseBody());
} catch (InterruptedException e) {
    System.err.println("Operation interrupted");
    Thread.currentThread().interrupt();
}

Timeout Handling

import java.util.concurrent.*;

ExecutorService executor = Executors.newSingleThreadExecutor();

Future<PveTask> future = executor.submit(() -> {
    return vm.start()
            .waitForCompletion(proxmox)
            .execute();
});

try {
    PveTask task = future.get(30, TimeUnit.SECONDS);
    System.out.println("VM started within timeout");
} catch (TimeoutException e) {
    System.err.println("Operation timed out after 30 seconds");
    future.cancel(true);
} catch (Exception e) {
    System.err.println("Error: " + e.getMessage());
} finally {
    executor.shutdown();
}

Advanced Task Monitoring

Monitor Task Progress

PveTask task = vm.cloneVm(200).execute();

System.out.println("Monitoring clone task...");

boolean running = true;
while (running) {
    Thread.sleep(2000);
    
    PveTaskStatus status = proxmox.getTaskStatus(task).execute();
    
    System.out.println("Status: " + status.getStatus());
    
    if (status.isCompleted()) {
        running = false;
        
        if (status.isSuccessful()) {
            System.out.println("Clone completed successfully!");
        } else {
            System.err.println("Clone failed: " + status.getExitstatus());
        }
    }
}

Common Task Types

Task Type Description
qmstart Start QEMU VM
qmstop Stop QEMU VM
qmshutdown Shutdown QEMU VM
qmreboot Reboot QEMU VM
qmclone Clone QEMU VM
qmresize Resize QEMU disk
qmdestroy Delete QEMU VM
qmconfig Update QEMU configuration
vzcreate Create LXC container
vzstart Start LXC container
vzstop Stop LXC container
vzdump Backup operation

Next Steps

⚠️ **GitHub.com Fallback** ⚠️