A Python client for Enroot
Project description
enroot-py
A Python client library for Enroot, providing a docker-py inspired interface for managing Enroot containers and images.
Overview
enroot-py offers a Pythonic API to interact with Enroot, NVIDIA's container runtime for HPC environments. It provides a familiar interface similar to docker-py, making it easy to manage containers, pull images, and execute commands within Enroot containers.
Features
- Image Management: Pull, list, and retrieve Docker images converted to Enroot's
.sqshformat - Container Operations: Create, start, stop, remove, and execute commands in containers
- Port Mapping: Automatic port allocation and mapping for containerized services
- Environment Variables: Pass environment variables to containers
- Resource Limits: CPU and memory limits via
systemd-run(when available) - Docker Compatibility: Automatically imports Docker images if not found locally
Prerequisites
- Python 3.8 or higher
- Enroot installed and available in your
PATH - Linux operating system (Enroot is Linux-only)
Installation
This library is not yet packaged on PyPI. To use it, clone the repository and add it to your PYTHONPATH:
git clone https://github.com/your-username/enroot-py.git
cd enroot-py
export PYTHONPATH=$PYTHONPATH:$(pwd)
Alternatively, you can install it in development mode:
pip install -e .
Quick Start
Creating a Client
from enroot.client import from_env
client = from_env()
# Check if Enroot is available
if client.ping():
print("Enroot is running!")
Working with Images
Pulling an Image
Pull a Docker image and convert it to Enroot's .sqsh format:
# Pull with explicit tag
image = client.images.pull("ubuntu", "20.04")
print(f"Pulled image: {image.id}")
# Pull latest tag (default)
image = client.images.pull("python", "3.9")
# Pull from a custom registry
image = client.images.pull("my-image", "latest", registry_host="registry.example.com")
Listing Images
images = client.images.list()
for image in images:
print(f"Image: {image.id}, Tags: {image.tags}")
Getting a Specific Image
image = client.images.get("ubuntu+20.04") # Use '+' instead of '/' or ':'
print(f"Found image: {image.id}")
Working with Containers
Starting a Container
container = client.containers.run(
"ubuntu:20.04", # Image name (will be imported if needed)
command="sleep 3600", # Command to run
name="my-container", # Optional container name
detach=True, # Required: must be True
ports={"8080/tcp": "8080"}, # Port mapping (container_port: host_port)
environment={ # Environment variables
"MY_VAR": "my_value",
"DEBUG": "1"
},
cpu_count=2.0, # Optional: CPU limit (requires systemd-run)
mem_limit="2G" # Optional: Memory limit (requires systemd-run)
)
print(f"Started container: {container.name}")
print(f"Status: {container.status}")
Note: detach=True is required. Non-detached mode is not currently supported.
Using Local .sqsh Files
You can also use local .sqsh files directly:
container = client.containers.run(
"/path/to/image.sqsh",
command="bash",
detach=True
)
Listing Containers
containers = client.containers.list()
for container in containers:
print(f"- {container.name} ({container.status})")
Getting a Container
container = client.containers.get("my-container")
print(f"Container status: {container.status}")
print(f"Container attributes: {container.attrs}")
Executing Commands in a Container
result = container.exec_run("echo 'Hello from container!'")
print(f"Exit code: {result.exit_code}")
print(f"Output: {result.output}")
# Execute with a list of arguments
result = container.exec_run(["ls", "-la", "/tmp"])
Stopping and Removing Containers
# Stop (kill) a container
container.kill()
# Remove a container
container.remove()
# Force remove a container
container.remove(force=True)
Checking Container Status
# Reload status from Enroot
container.reload()
# Get current status
status = container.status # "running" or "exited"
# Get container attributes (including port mappings)
attrs = container.attrs
Configuration
Environment Variables
ENROOT_HOME: Override the default Enroot home directory (default:~/.cache/enroot)ENROOT_IMAGES_PATH: Override where pulled.sqshimages are stored (default:<ENROOT_HOME>/images)ENROOT_DEBUG: Set to"1"to enable debug logging (shows Enroot command execution)ENROOT_ASYNC: Set to"1"to make the synchronousrun_enroothelper transparently use the async backend when no event loop is already runningXDG_CACHE_HOME: Used for cache directory ifENROOT_HOMEis not set
Port Mapping
When you specify ports in containers.run(), the library automatically allocates a free port on the host and maps it to the container port. The allocated port is available in the container's attrs:
container = client.containers.run(
"nginx",
detach=True,
ports={"80/tcp": "80"}
)
# Access port mapping
port_mapping = container.attrs["NetworkSettings"]["Ports"]
# The actual host port is stored here
The allocated port is also set as the PORT environment variable inside the container.
Resource Limits
CPU and memory limits are applied using systemd-run when available:
container = client.containers.run(
"my-image",
detach=True,
cpu_count=1.5, # 1.5 CPUs
mem_limit="512M" # 512 MB memory limit
)
Note: Resource limits require systemd-run to be available. If it's not found, a warning is issued and the limits are ignored.
API Reference
EnrootClient
Main client class for interacting with Enroot.
ping() -> bool: Check if Enroot is availableimages: Access to image operations (Imagesinstance)containers: Access to container operations (Containersinstance)
Images
Image management operations.
pull(repository: str, tag: str | None = None, registry_host: str = "", directory: str | None = None) -> Image: Pull and import a Docker image. Whenregistry_hostis empty (the default), Enroot uses its built-in default registry; pass e.g."registry.example.com"to override.list() -> List[Image]: List all available imagesget(ref: str) -> Image: Get a specific image by reference
Containers
Container management operations.
run(image, command=None, name=None, timeout=120, detach=False, remove=False, ports=None, mount=None, environment=None, cpu_count=None, mem_limit=None, cpus=None, **_) -> Container: Create and start a container (detach=Trueis required).create(image, command=None, name=None, ports=None, mount=None, environment=None, cpu_count=None, mem_limit=None, cpus=None, timeout=120, **_) -> Container: Create a container without starting it (similar todocker create). The optionalcommandis stashed and used as the default forContainer.start(...).create_async(...) -> Container: Coroutine variant ofcreate.list() -> List[Container]: List all containersget(ident: str) -> Container: Get a container by name
Container
Represents a single container instance.
name: str/id: str/short_id: str: Container identifiers (Enroot uses the name as the id).status: str: Container status ("created","running", or"exited").attrs: Dict: Container attributes (similar to Docker API).pids: List[int]: Cached in-container PIDs fromenroot list --fancy.reload()/reload_async(): Refresh state fromenroot list.start(command=None, environment=None, mount=None, cpu_count=None, mem_limit=None, cpus=None, timeout=30, **_): Start a container previously created withcontainers.create(...).start_async(...): Async variant ofstart.stop(timeout=10, **_): Gracefully stop and remove the container.kill(**_)/kill_async(timeout=10, **_): Forcefully stop and remove.remove(force=False, **_): Remove the container viaenroot remove.exec_run(cmd, *, stdout=True, stderr=True, demux=False, workdir=None, user=None, environment=None, detach=False, mem_limit=None, mem_guard_limit=None, mem_guard_poll_interval=1.0, timeout=None, **_) -> ExecResult: Execute a command in the container. Supports host-sidetimeout(raisesenroot.errors.TimeoutErroron exit 124), prlimit-basedmem_limit, and psutil-based RSS capmem_guard_limit(raisesenroot.errors.MemGuardErroron exit 137).exec_run_async(...) -> ExecResult: Async variant ofexec_run.copy_to(local_path, container_path)/put_archive(path, data): Copy files into a running container.
Image
Represents a container image.
id: str: Image ID (path to .sqsh file)tags: List[str]: Image tags
Limitations
- Detached Mode Only: The
detachparameter must beTrue. Non-detached containers are not supported. - No Logs: The
logs()method returns an empty bytes object. Enroot doesn't provide a built-in logging mechanism. - Resource Limits: CPU and memory limits require
systemd-runto be available on the system. - Port Mapping: All ports are mapped to a single automatically-allocated free port. Individual port mappings are not fully supported.
Testing
The test suite uses pytest. Install the dev extras and run it:
pip install -e ".[dev]"
pytest
Slow integration checks that pull large images are gated behind the slow marker:
pytest -m slow
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License (see pyproject.toml for details)
Related Projects
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enroot_py-0.1.1.tar.gz.
File metadata
- Download URL: enroot_py-0.1.1.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2458b7a579bdc1e6a69107dc3d1040ad86cd33c8611b37d2b61a4507d060d4f4
|
|
| MD5 |
02cbf56215143b7d6fe450fad26da428
|
|
| BLAKE2b-256 |
9efc8b9ae7231e1166890cfb9017a37133f882391912eb1212827bc257873f5a
|
File details
Details for the file enroot_py-0.1.1-py3-none-any.whl.
File metadata
- Download URL: enroot_py-0.1.1-py3-none-any.whl
- Upload date:
- Size: 28.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
336d7451fd065ab78980a5f334fe7533cae4034f729ded0eff5c3fe8166d70ca
|
|
| MD5 |
d590388624982f8bcbd65dcab02bfad7
|
|
| BLAKE2b-256 |
e6f7c9640ef35c7de73fa425d1e0ee6fc02764e4b177edb3c3f1b2df2dccc843
|