Skip to content

Using the Proxmox SDK with Mock Data

The proxmox-sdk SDK provides built-in mock support for zero-setup testing and development — no real Proxmox server required!


Quick Start

Async Usage

from proxmox_sdk.sdk import ProxmoxSDK

# Create a mock SDK instance
async with ProxmoxSDK.mock() as proxmox:
    # Query mock data
    nodes = await proxmox.nodes.get()
    print(f"Nodes: {nodes}")

    # Create mock resources
    vm = await proxmox.nodes("pve").qemu.post(vmid=100, name="test-vm")
    print(f"Created VM: {vm}")

    # Retrieve created resource
    retrieved = await proxmox.nodes("pve").qemu(100).get()
    print(f"Retrieved VM: {retrieved}")

Sync Usage (No async/await)

from proxmox_sdk.sdk import ProxmoxSDK

# Create a mock SDK instance (blocking)
with ProxmoxSDK.sync_mock() as proxmox:
    # All API calls block until complete
    nodes = proxmox.nodes.get()
    print(f"Nodes: {nodes}")

    # Create mock resources
    vm = proxmox.nodes("pve").qemu.post(vmid=100, name="test-vm")
    print(f"Created VM: {vm}")

Features

✅ Zero Setup — No real Proxmox server needed ✅ Local Mock CRUD — Create, read, update, and delete mock resources through SQLite/WAL by default ✅ Deterministic Data — Same seed produces same data (testable) ✅ Schema-Driven — Mock data respects Proxmox OpenAPI schema ✅ Multi-Service Support — PVE, PMG, PBS ✅ State Persistence — Changes persist during runtime


Detailed Guide

from proxmox_sdk.sdk import ProxmoxSDK

async with ProxmoxSDK.mock() as proxmox:
    # proxmox is the root ProxmoxResource
    nodes = await proxmox.nodes.get()

    # The SDK context closes; shared mock state is not reset.

Async Manual Lifecycle

from proxmox_sdk.sdk import ProxmoxSDK

proxmox_sdk = ProxmoxSDK.mock()
proxmox = proxmox_sdk._root  # Access the root resource

try:
    nodes = await proxmox.nodes.get()
finally:
    await proxmox_sdk.close()
from proxmox_sdk.sdk import ProxmoxSDK

with ProxmoxSDK.sync_mock() as proxmox:
    nodes = proxmox.nodes.get()
    print(nodes)

    # The SDK context closes; shared mock state is not reset.

Sync Manual Lifecycle

from proxmox_sdk.sdk import ProxmoxSDK

proxmox = ProxmoxSDK.sync_mock()

try:
    nodes = proxmox.nodes.get()
    print(nodes)
finally:
    proxmox.close()

Common Workflows

Listing Resources

from proxmox_sdk.sdk import ProxmoxSDK

with ProxmoxSDK.sync_mock() as proxmox:
    # List nodes
    nodes = proxmox.nodes.get()
    print(f"Nodes: {nodes}")

    # List VMs on a node
    vms = proxmox.nodes("pve").qemu.get()
    print(f"VMs: {vms}")

    # List containers (LXC)
    containers = proxmox.nodes("pve").lxc.get()
    print(f"Containers: {containers}")

Creating Resources

with ProxmoxSDK.sync_mock() as proxmox:
    # Create a VM
    vm = proxmox.nodes("pve").qemu.post(
        vmid=100,
        name="my-vm",
        memory=2048,
        cores=2
    )
    print(f"Created: {vm}")

Retrieving Specific Resources

with ProxmoxSDK.sync_mock() as proxmox:
    # Get a specific VM
    vm = proxmox.nodes("pve").qemu(100).get()
    print(f"VM 100: {vm}")

    # Get node status
    node_status = proxmox.nodes("pve").status.get()
    print(f"Node status: {node_status}")

Updating Resources

with ProxmoxSDK.sync_mock() as proxmox:
    # Update a VM (PUT replaces all fields)
    updated = proxmox.nodes("pve").qemu(100).put(
        name="updated-vm",
        memory=4096
    )
    print(f"Updated: {updated}")

    # Patch a VM (PATCH updates specific fields)
    patched = proxmox.nodes("pve").qemu(100).patch(
        name="patched-vm"
    )
    print(f"Patched: {patched}")

Deleting Resources

with ProxmoxSDK.sync_mock() as proxmox:
    # Delete a VM
    result = proxmox.nodes("pve").qemu(100).delete()
    print(f"Deleted: {result}")

Specifying Schema Versions

Control which Proxmox OpenAPI schema to use:

from proxmox_sdk.sdk import ProxmoxSDK

# Use latest version (default)
async with ProxmoxSDK.mock() as proxmox:
    nodes = await proxmox.nodes.get()

# Use a specific version
async with ProxmoxSDK.mock(schema_version="9.2") as proxmox:  # or '9.1.11' for previous version
    nodes = await proxmox.nodes.get()

# Sync version
with ProxmoxSDK.sync_mock(schema_version="9.2") as proxmox:  # or '9.1.11' for previous version
    nodes = proxmox.nodes.get()

Available versions depend on your installation. See Mock API documentation for a list of supported versions.


Multi-Service Support

Create mock instances for different Proxmox services:

from proxmox_sdk.sdk import ProxmoxSDK

# Proxmox VE (default)
async with ProxmoxSDK.mock(service="PVE") as proxmox:
    nodes = await proxmox.nodes.get()

# Proxmox Mail Gateway
async with ProxmoxSDK.mock(service="PMG") as proxmox:
    # PMG-specific endpoints
    pass

# Proxmox Backup Server
async with ProxmoxSDK.mock(service="PBS") as proxmox:
    # PBS-specific endpoints
    pass

# Proxmox Datacenter Manager
async with ProxmoxSDK.mock(service="PDM") as proxmox:
    # PDM-specific endpoints, backed by the real generated PDM schema
    pass

PDM mock mode is schema-backed

ProxmoxSDK.mock(service="PDM") (and PDMClient.mock(), see PDM HOW-TO) load the real generated PDM OpenAPI schema via load_pdm_generated_openapi() (246 paths / 318 operations) instead of falling back to the PVE schema or returning empty responses. See the PDM Codegen Path for how that schema is produced.


Testing

Unit Tests

import pytest
from proxmox_sdk.sdk import ProxmoxSDK

@pytest.mark.asyncio
async def test_node_listing():
    """Test listing nodes from mock API."""
    async with ProxmoxSDK.mock() as proxmox:
        nodes = await proxmox.nodes.get()
        assert isinstance(nodes, list)
        assert len(nodes) > 0
        assert nodes[0].get("node") is not None

@pytest.mark.asyncio
async def test_vm_creation():
    """Test creating a VM in mock API."""
    async with ProxmoxSDK.mock() as proxmox:
        vm = await proxmox.nodes("pve").qemu.post(
            vmid=100,
            name="test-vm"
        )
        assert vm.get("vmid") == 100
        assert vm.get("name") == "test-vm"

@pytest.mark.asyncio
async def test_vm_retrieval():
    """Test retrieving a created VM."""
    async with ProxmoxSDK.mock() as proxmox:
        # Create
        created = await proxmox.nodes("pve").qemu.post(
            vmid=101,
            name="test-vm-2"
        )

        # Retrieve
        retrieved = await proxmox.nodes("pve").qemu(101).get()
        assert retrieved.get("vmid") == created.get("vmid")
        assert retrieved.get("name") == created.get("name")

Integration Tests

def test_vm_lifecycle_sync():
    """Test full VM lifecycle using sync API."""
    with ProxmoxSDK.sync_mock() as proxmox:
        # Create
        vm = proxmox.nodes("pve").qemu.post(
            vmid=200,
            name="lifecycle-test"
        )
        assert vm["vmid"] == 200

        # Retrieve
        retrieved = proxmox.nodes("pve").qemu(200).get()
        assert retrieved["vmid"] == 200

        # Update
        updated = proxmox.nodes("pve").qemu(200).put(
            name="lifecycle-updated"
        )
        assert updated["name"] == "lifecycle-updated"

        # Delete
        proxmox.nodes("pve").qemu(200).delete()

        # Verify deletion (should raise or be empty, depending on mock behavior)

State Persistence

The default SQLiteMockStore keeps CRUD state in a WAL-enabled SQLite file. Without configuration, that file is scoped by the process owner and mock namespace under the system temporary directory. SDK instances using the same owner, namespace, and path reuse the cached store within the process; leaving one ProxmoxSDK.sync_mock() context does not reset it. Use reset_shared_mock_state() or a new namespace when a test needs a deterministic seed. Set PROXMOX_MOCK_STATE_PATH when you need an explicit debugging path.

from proxmox_sdk import ProxmoxSDK
from proxmox_sdk.mock.state import reset_shared_mock_state

with ProxmoxSDK.sync_mock() as proxmox:
    vm = proxmox.nodes("pve").qemu.post(vmid=300, name="test")
    retrieved = proxmox.nodes("pve").qemu(300).get()
    print(retrieved)  # ✅ Works

with ProxmoxSDK.sync_mock() as proxmox:
    # The same owner/namespace/path reuses the SQLite-backed state.
    retrieved = proxmox.nodes("pve").qemu(300).get()
    print(retrieved)

reset_shared_mock_state()

Set PROXMOX_MOCK_STORE=shared-memory or PROXMOX_MOCK_STORE=dict only when you explicitly need those legacy in-process alternatives. They do not provide the SQLite backend's file-backed debugging behavior.

For persistent mock data across sessions, see Custom Mock Data (FastAPI Mode).


Error Handling

The SDK raises ResourceException for API errors:

from proxmox_sdk.sdk import ProxmoxSDK
from proxmox_sdk.sdk.exceptions import ResourceException

with ProxmoxSDK.sync_mock() as proxmox:
    try:
        # Attempt invalid operation
        proxmox.nodes("nonexistent").qemu.get()
    except ResourceException as e:
        print(f"API Error: {e.status_code} - {e.status_message}")

Differences from Real Proxmox

Similarities

  • ✅ Same endpoint structure /api2/json/...
  • ✅ Same request/response format (JSON)
  • ✅ Same attribute-based navigation
  • ✅ Same method names (GET, POST, PUT, PATCH, DELETE)

Differences

  • ⚠️ No actual VM/container creation (mock only)
  • ⚠️ No permission validation (all operations allowed)
  • ⚠️ No task tracking (UPIDs not returned)
  • ⚠️ Limited data validation (basic schema compliance)
  • ⚠️ Default tempdir-scoped SQLite state is development data, not a durable production database; use PROXMOX_MOCK_STATE_PATH for an explicit debugging path

Next Steps