Skip to main content

foreman-client

A generic abstraction client library for the Red Hat Satellite / Foreman API, written in Python using requests.

Rather than hand-coding a Python method for every one of Foreman/Katello's hundreds of API endpoints, this library provides a single dynamic client that maps Python attribute access directly onto REST paths. Any endpoint under /api/v2 or /katello/api/v2 is reachable without needing per-resource code added to the library.

Features

  • Full API coverage via a dynamic resource wrapper — no need to wait for a method to be added for a given endpoint.
  • Sync HTTP client built on requests.Session.
  • Two auth modes: username/password (HTTP Basic) or API token (Bearer).
  • Automatic pagination.list() walks all pages and returns every result as a single list.
  • Katello namespaceclient.katello.* automatically targets /katello/api/v2 instead of /api/v2.
  • Custom/non-CRUD actions — call arbitrary action endpoints (e.g. power, errata/apply) via .action().
  • Typed exception hierarchy — distinct exceptions for auth, not-found, validation, and generic API errors.
  • Client-side field projection — trim returned records down to specific keys with fields=.
  • SSL verification toggle — defaults to verified, can be disabled per-client.

Installation

pip install .

or, for local development:

pip install -e .

Requires Python >= 3.8 and requests >= 2.25.0.

Quick start

from foreman_client import ForemanClient

client = ForemanClient(
    "https://satellite.example.com",
    username="admin",
    password="secret",
    verify_ssl=True,   # set False for self-signed certs (not recommended for prod)
)

hosts = client.hosts.list()

Authenticating with an API token instead

client = ForemanClient(
    "https://satellite.example.com",
    api_token="your-api-token-here",
)

You must supply either api_token, or both username and password. Supplying neither raises ValueError.

Core concepts

Dynamic resources

Any attribute access on the client (that doesn't start with _) returns a Resource bound to that path segment. Chaining attributes builds up a nested REST path:

client.hosts                     # -> /api/v2/hosts
client.hosts(5)                  # -> /api/v2/hosts/5
client.hosts(5).interfaces       # -> /api/v2/hosts/5/interfaces
client.smart_proxies.list()      # -> GET /api/v2/smart_proxies

Calling a Resource with an id (client.hosts(5)) scopes it to that specific record, letting you keep chaining sub-resources under it.

CRUD methods

Every Resource exposes:

Method HTTP Behavior
.list(paginate=True, fields=None, **params) GET Fetches the collection. Auto-paginates by default.
.get(item_id=None, fields=None, **params) GET Fetches a single record. Uses the bound id if the resource was called with one.
.create(**data) POST Creates a record from keyword args (sent as JSON body).
.update(item_id=None, **data) PUT Updates a record.
.delete(item_id=None, **params) DELETE Deletes a record.
.action(name, method="POST", item_id=None, **data) any Calls a non-CRUD action sub-path.

Examples:

# List all hosts (auto-paginated)
hosts = client.hosts.list()

# List with a Foreman search string
web_hosts = client.hosts.list(search="name ~ web")

# Get a single host by id
host = client.hosts.get(5)
# equivalent:
host = client.hosts(5).get()

# Create
client.hosts.create(name="myhost", organization_id=1, location_id=1)

# Update
client.hosts.update(5, name="renamed-host")

# Delete
client.hosts.delete(5)

# Non-CRUD action, e.g. power management
client.hosts.action("power", item_id=5, power_action="cycle")
# -> POST /api/v2/hosts/5/power

Katello / content endpoints

Satellite's content-related endpoints (content views, repositories, lifecycle environments, subscriptions, errata, etc.) live under /katello/api/v2 rather than /api/v2. Access them via client.katello:

content_views = client.katello.content_views.list()
repos = client.katello.repositories.list(search="name ~ EPEL")
errata = client.katello.hosts(5).errata.list()

Explicit path access

If you need a path that doesn't map cleanly onto chained attributes (e.g. numeric-looking segments, reserved Python keywords), use .api() directly:

client.api("hosts", 5, "smart_class_parameters").list()
client.api("content_views", 3, "publish", api_root="/katello/api/v2").action("publish")

Pagination

.list() paginates automatically by walking Foreman's page/per_page/total response metadata and returns a flat list of every result:

all_hosts = client.hosts.list()          # every host, across all pages

Set paginate=False to get the raw first-page response dict instead (includes total, page, per_page, results, etc.):

first_page = client.hosts.list(paginate=False, per_page=20)

Default page size is controlled by per_page on the client (default 100), and can be overridden per call:

client = ForemanClient(url, username=..., password=..., per_page=50)
client.hosts.list(per_page=200)

For manual control over iteration (e.g. to stop early), use the client's paginate() generator directly:

for host in client.paginate("/api/v2/hosts", params={"search": "name ~ web"}):
    print(host["name"])

Field projection

fields= filters the returned record(s) down to a set of top-level keys client-side, after the full response has already been received from Satellite:

client.hosts.get(5, fields=["id", "name", "operatingsystem_name"])
client.hosts.list(fields=["id", "name"])

Important: Foreman/Katello's API does not generally support arbitrary server-side field selection. This does not reduce what Satellite sends over the wire — only what's returned to your code afterward. Some endpoints support a genuine payload-reducing thin=true flag, which is a real server-side reduction and can be passed through like any other param:

client.hosts.list(thin=True)

Search syntax

Foreman/Katello search params are passed straight through as query params — use Foreman's native search syntax:

client.hosts.list(search="organization_id=1 and os_name=RedHat")
client.katello.errata.list(search="type=security and severity=critical")

Error handling

All non-2xx responses raise a typed exception from foreman_client.exceptions:

from foreman_client import (
    ForemanError,             # base class for all of the below
    ForemanConnectionError,   # network-level failure (connect/timeout)
    ForemanAuthError,         # 401 / 403
    ForemanNotFoundError,     # 404
    ForemanValidationError,   # 422 -- has an .errors attribute with Foreman's error payload
    ForemanAPIError,          # any other non-2xx
)

try:
    client.hosts.create(name="")  # missing required fields
except ForemanValidationError as exc:
    print(exc.status_code)   # 422
    print(exc.errors)        # Foreman's structured error detail
except ForemanAuthError as exc:
    print("auth problem:", exc.status_code)
except ForemanError as exc:
    print("something else went wrong:", exc)

All of these exceptions (except ForemanConnectionError) expose .status_code and .response_body.

Concurrency

The client is synchronous, but requests.Session is thread-safe for concurrent reads, so fan-out patterns work well for bulk operations across many hosts:

from concurrent.futures import ThreadPoolExecutor, as_completed

host_ids = [1, 2, 3, 4, 5]

def get_errata(host_id):
    return host_id, client.katello.hosts(host_id).errata.list()

results = {}
with ThreadPoolExecutor(max_workers=10) as pool:
    futures = {pool.submit(get_errata, hid): hid for hid in host_ids}
    for future in as_completed(futures):
        host_id, errata = future.result()
        results[host_id] = errata

SSL verification

Verification is enabled by default. Disable only if you understand the risk (e.g. internal lab Satellite with a self-signed cert):

client = ForemanClient(url, username=..., password=..., verify_ssl=False)

Project structure

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

powerdrill-0.1.2.tar.gz (11.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

powerdrill-0.1.2-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file powerdrill-0.1.2.tar.gz.

File metadata

  • Download URL: powerdrill-0.1.2.tar.gz
  • Upload date:
  • Size: 11.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for powerdrill-0.1.2.tar.gz
Algorithm Hash digest
SHA256 ed2594f4e874fd25d3da3d34522489d6097713c983fc6c84d8ae856c37799a15
MD5 15d71200e7c225a4ff626b32dd41c228
BLAKE2b-256 d199f1da40af163fb2a0dddbf245d58970aedb1d9801ebfd465e6abf72c5185a

See more details on using hashes here.

Provenance

The following attestation bundles were made for powerdrill-0.1.2.tar.gz:

Publisher: publish.yml on gebz97/powerdrill

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file powerdrill-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: powerdrill-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for powerdrill-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 35f7a6a48088c2840648f9fd9e5ad263459173cd2ccc4f39eba6961176c31e74
MD5 3b4f7af8054e7d63c896970c22e34ca9
BLAKE2b-256 eafbd5af2f6d91f9cb9060d8e166f4a1e9eb7ab75b2a22976e51e5fd305da74f

See more details on using hashes here.

Provenance

The following attestation bundles were made for powerdrill-0.1.2-py3-none-any.whl:

Publisher: publish.yml on gebz97/powerdrill

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.3

2 files

This release

0.1.2 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page