Skip to main content

rsspot

rsspot is a Python SDK and CLI for Rackspace Spot with unified sync/async ergonomics, sqlite-backed runtime state, and profile-based configuration.

Highlights

  • Unified client API:
    • SpotClient for sync + async access on one object
    • AsyncSpotClient for explicit async-only usage
  • Persistent sqlite state (state.db) for:
    • defaults/preferences
    • HTTP cache
    • redacted CLI command history
    • VM registration ledger entries
  • Config precedence + migration:
    • new default path: ~/.config/rsspot/config.yml
    • legacy ~/.spot_config auto-imported/migrated
  • Strong typing via pydantic models
  • Multi-profile workflows + global singleton helpers
  • OpenAPI sync/index scripts for upstream tracking

Install

uv sync

Quickstart (CLI)

uv run rsspot configure \
  --profile default \
  --org <org-name> \
  --region us-central-dfw-1 \
  --refresh-token "$SPOT_REFRESH_TOKEN"

uv run rsspot organizations list --output table

Pricing Explorer + Builder

# default output is a rich table when --output is not explicitly set
uv run rsspot pricing list --region us-central-dfw-1 --class gp,ch --min-cpu 4 --gen 2 --nodes 5

# explicit machine-readable output stays json/yaml
uv run rsspot -o json pricing list --class gp --max-cpu 8 --nodes 10

# generate 3 recommendation strategies (max_performance, max_value, balanced)
uv run rsspot pricing build --nodes 5 --risk med --classes gp,ch,mh

# spread balanced strategy across multiple pools and constrain cluster hourly spend
uv run rsspot pricing build --nodes 5 --balanced --risk low --max-hour 0.25

# constrain by total cluster monthly spend
uv run rsspot pricing build --nodes 5 --min-month 50 --max-month 180

Quickstart (SDK)

Unified client

from rsspot import SpotClient

client = SpotClient(profile="default")
orgs = client.organizations.list()
print([org.name for org in orgs.organizations])
client.close()
import asyncio
from rsspot import SpotClient

async def main() -> None:
    client = SpotClient(profile="default")
    orgs = await client.aorganizations.list()
    print([org.name for org in orgs.organizations])
    await client.aclose()

asyncio.run(main())

Async-only client

import asyncio
from rsspot import AsyncSpotClient

async def main() -> None:
    async with AsyncSpotClient(profile="default") as client:
        regions = await client.regions.list()
        print([r.name for r in regions])

asyncio.run(main())

Configuration

Resolution precedence:

  1. Runtime config dict/model passed to client constructor
  2. Explicit config_path=...
  3. Env path (RSSPOT_CONFIG, RSSPOT_CONFIG_FILE, SPOT_CONFIG_FILE)
  4. Default search in ~/.config/rsspot/config.{yml,yaml,toml,json}
  5. Legacy fallback ~/.spot_config (auto-migrated)

Expected schema is identical across config.yaml, config.toml, and config.json.

config.yaml

version: "2"
default_profile: prod
active_profile: prod
state_path: /Users/you/.config/rsspot/state.db

profiles:
  prod:
    org: sparkai
    region: us-central-dfw-1
    refresh_token: replace-me
    base_url: https://spot.rackspace.com
    oauth_url: https://spot.rackspace.com
    request_timeout_seconds: 30
    verify_ssl: true

preferences:
  default_profile: prod
  default_org: sparkai
  default_region: us-central-dfw-1

retry:
  max_attempts: 4
  base_delay: 0.2
  max_delay: 2.5
  jitter: 0.2

cache:
  enabled: true
  default_ttl: 5
  max_entries: 1000
  backend: sqlite

config.toml

version = "2"
default_profile = "prod"
active_profile = "prod"
state_path = "/Users/you/.config/rsspot/state.db"

[profiles.prod]
org = "sparkai"
region = "us-central-dfw-1"
refresh_token = "replace-me"
base_url = "https://spot.rackspace.com"
oauth_url = "https://spot.rackspace.com"
request_timeout_seconds = 30
verify_ssl = true

[preferences]
default_profile = "prod"
default_org = "sparkai"
default_region = "us-central-dfw-1"

[retry]
max_attempts = 4
base_delay = 0.2
max_delay = 2.5
jitter = 0.2

[cache]
enabled = true
default_ttl = 5
max_entries = 1000
backend = "sqlite"

config.json

{
  "version": "2",
  "default_profile": "prod",
  "active_profile": "prod",
  "state_path": "/Users/you/.config/rsspot/state.db",
  "profiles": {
    "prod": {
      "org": "sparkai",
      "region": "us-central-dfw-1",
      "refresh_token": "replace-me",
      "base_url": "https://spot.rackspace.com",
      "oauth_url": "https://spot.rackspace.com",
      "request_timeout_seconds": 30,
      "verify_ssl": true
    }
  },
  "preferences": {
    "default_profile": "prod",
    "default_org": "sparkai",
    "default_region": "us-central-dfw-1"
  },
  "retry": {
    "max_attempts": 4,
    "base_delay": 0.2,
    "max_delay": 2.5,
    "jitter": 0.2
  },
  "cache": {
    "enabled": true,
    "default_ttl": 5,
    "max_entries": 1000,
    "backend": "sqlite"
  }
}

Notes:

  • profiles.<name> is where profile-specific credentials/selectors live.
  • Top-level retry/cache are defaults; profile-level retry/cache can override them.
  • Legacy aliases like refreshToken, clientId, and baseUrl are still accepted.

VM Registration Workflow Primitives

rsspot includes registration state helpers for composing with external orchestrators (including separate omni-sdk scripts) without taking a direct dependency.

Core entrypoint:

  • rsspot.workflows.RegistrationWorkflow

OpenAPI Tracking

uv run python scripts/sync_openapi.py
uv run python scripts/generate_openapi_index.py

Development

uv run ruff check src tests
uv run mypy src
uv run pytest -q

Changelog

See CHANGELOG.md.

Metadata

Release files for rsspot 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rsspot 0.3.0
File Size Uploaded
rsspot-0.3.0.tar.gz 92.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rsspot 0.3.0
File Interpreter ABI Platform
rsspot-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 144.4 kB

Release files / rsspot-0.3.0.tar.gz

Download URL rsspot-0.3.0.tar.gz
Size 92.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e7048a2f867461eb3ee4ff3f898e684837bded8333e0457822acb7edeb758fe3
BLAKE2b-256 checksum
How to use checksums
24a9c85d029e13c393f7b63e9eeb4d110b09457f3e22945a340923ceb8e2417f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / rsspot-0.3.0-py3-none-any.whl

Download URL rsspot-0.3.0-py3-none-any.whl
Size 51.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
895479fc68c9b6222a297b9c791a609f6749c4cf2b8aa9b98201d1c4173189a9
BLAKE2b-256 checksum
How to use checksums
6c322577a1bc25b27fadb611549eea3e26b7618eb77931666144544dfe92c3bf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release 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